diff --git a/.eslintrc.json b/.eslintrc.json index 055bc22f..fd72f0e5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -983,6 +983,7 @@ "collapse", "create", "delete", + "discover", "dispose", "edit", "end", diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4a04784b..51e7f366 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Question - url: https://stackoverflow.com/questions/tagged/visual-studio-code + url: https://stackoverflow.com/questions/tagged/visual-studio-code about: Please ask and answer questions here. diff --git a/.github/calendar.yml b/.github/calendar.yml index f1495796..c1b5a256 100644 --- a/.github/calendar.yml +++ b/.github/calendar.yml @@ -1,37 +1,37 @@ { - '2018-01-29 18:00, US/Pacific': 'endgame', - '2018-02-07 12:00, US/Pacific': 'release', # 1.20.0 - '2018-02-12 12:00, US/Pacific': 'development', - '2018-02-14 16:00, Europe/Zurich': 'release', # 1.20.1 - '2018-02-19 16:00, Europe/Zurich': 'development', - '2018-02-26 18:00, US/Pacific': 'endgame', - '2018-03-07 12:00, US/Pacific': 'release', # 1.21.0 - '2018-03-12 12:00, US/Pacific': 'development', - '2018-03-15 12:00, US/Pacific': 'release', # 1.21.1 - '2018-03-20 12:00, US/Pacific': 'development', - '2018-03-26 18:00, US/Pacific': 'endgame', - '2018-04-06 18:00, US/Pacific': 'release', # 1.22.1 - '2018-04-11 18:00, US/Pacific': 'development', - '2018-04-12 12:00, US/Pacific': 'release', # 1.22.2 - '2018-04-17 12:00, US/Pacific': 'development', - '2018-04-23 18:00, US/Pacific': 'endgame', - '2018-05-03 12:00, US/Pacific': 'release', # 1.23.0 - '2018-05-08 12:00, US/Pacific': 'development', - '2018-05-10 12:00, US/Pacific': 'release', # 1.23.1 - '2018-05-15 12:00, US/Pacific': 'development', - '2018-05-28 18:00, US/Pacific': 'endgame', + "2018-01-29 18:00, US/Pacific": "endgame", + "2018-02-07 12:00, US/Pacific": "release", # 1.20.0 + "2018-02-12 12:00, US/Pacific": "development", + "2018-02-14 16:00, Europe/Zurich": "release", # 1.20.1 + "2018-02-19 16:00, Europe/Zurich": "development", + "2018-02-26 18:00, US/Pacific": "endgame", + "2018-03-07 12:00, US/Pacific": "release", # 1.21.0 + "2018-03-12 12:00, US/Pacific": "development", + "2018-03-15 12:00, US/Pacific": "release", # 1.21.1 + "2018-03-20 12:00, US/Pacific": "development", + "2018-03-26 18:00, US/Pacific": "endgame", + "2018-04-06 18:00, US/Pacific": "release", # 1.22.1 + "2018-04-11 18:00, US/Pacific": "development", + "2018-04-12 12:00, US/Pacific": "release", # 1.22.2 + "2018-04-17 12:00, US/Pacific": "development", + "2018-04-23 18:00, US/Pacific": "endgame", + "2018-05-03 12:00, US/Pacific": "release", # 1.23.0 + "2018-05-08 12:00, US/Pacific": "development", + "2018-05-10 12:00, US/Pacific": "release", # 1.23.1 + "2018-05-15 12:00, US/Pacific": "development", + "2018-05-28 18:00, US/Pacific": "endgame", # 'release' not needed anymore, return to 'development' after releasing. - '2018-06-06 12:00, US/Pacific': 'development', # 1.24.0 released - '2018-06-25 18:00, US/Pacific': 'endgame', - '2018-07-05 12:00, US/Pacific': 'development', # 1.25.0 released - '2018-07-30 18:00, US/Pacific': 'endgame', - '2018-08-13 12:00, US/Pacific': 'development', # 1.26.0 released - '2018-08-27 18:00, US/Pacific': 'endgame', - '2018-09-05 12:00, US/Pacific': 'development', # 1.27.0 released - '2018-09-24 18:00, US/Pacific': 'endgame', - '2018-10-08 09:00, US/Pacific': 'development', # 1.28.0 released - '2018-10-29 18:00, US/Pacific': 'endgame', - '2018-11-12 11:00, US/Pacific': 'development', # 1.29.0 released - '2018-12-03 18:00, US/Pacific': 'endgame', - '2018-12-12 13:00, US/Pacific': 'development', # 1.30.0 released + "2018-06-06 12:00, US/Pacific": "development", # 1.24.0 released + "2018-06-25 18:00, US/Pacific": "endgame", + "2018-07-05 12:00, US/Pacific": "development", # 1.25.0 released + "2018-07-30 18:00, US/Pacific": "endgame", + "2018-08-13 12:00, US/Pacific": "development", # 1.26.0 released + "2018-08-27 18:00, US/Pacific": "endgame", + "2018-09-05 12:00, US/Pacific": "development", # 1.27.0 released + "2018-09-24 18:00, US/Pacific": "endgame", + "2018-10-08 09:00, US/Pacific": "development", # 1.28.0 released + "2018-10-29 18:00, US/Pacific": "endgame", + "2018-11-12 11:00, US/Pacific": "development", # 1.29.0 released + "2018-12-03 18:00, US/Pacific": "endgame", + "2018-12-12 13:00, US/Pacific": "development", # 1.30.0 released } diff --git a/.github/classifier.json b/.github/classifier.json index e4acc63c..0ff0712c 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -72,7 +72,7 @@ "file-watcher": {"assign": ["bpasero"]}, "font-rendering": {"assign": []}, "formatting": {"assign": []}, - "git": {"assign": ["joaomoreno"]}, + "git": {"assign": ["eamodio"]}, "gpu": {"assign": ["deepak1556"]}, "grammar": {"assign": ["mjbvz"]}, "grid-view": {"assign": ["joaomoreno"]}, @@ -81,9 +81,9 @@ "icon-brand": {"assign": []}, "icons-product": {"assign": ["misolori"]}, "install-update": {"assign": []}, - "integrated-terminal": {"assign": ["Tyriar"]}, - "integrated-terminal-conpty": {"assign": ["Tyriar"]}, - "integrated-terminal-links": {"assign": ["Tyriar"]}, + "integrated-terminal": {"assign": ["meganrogge"]}, + "integrated-terminal-conpty": {"assign": ["meganrogge"]}, + "integrated-terminal-links": {"assign": ["meganrogge"]}, "integration-test": {"assign": []}, "intellisense-config": {"assign": []}, "ipc": {"assign": ["joaomoreno"]}, @@ -119,7 +119,7 @@ "remote": {"assign": []}, "remote-explorer": {"assign": ["alexr00"]}, "rename": {"assign": ["jrieken"]}, - "scm": {"assign": ["joaomoreno"]}, + "scm": {"assign": ["eamodio"]}, "screencast-mode": {"assign": ["lszomoru"]}, "search": {"assign": ["roblourens"]}, "search-editor": {"assign": ["JacksonKearl"]}, @@ -159,7 +159,7 @@ "workbench-electron": {"assign": ["deepak1556"]}, "workbench-feedback": {"assign": ["bpasero"]}, "workbench-history": {"assign": ["bpasero"]}, - "workbench-hot-exit": {"assign": ["Tyriar"]}, + "workbench-hot-exit": {"assign": []}, "workbench-launch": {"assign": []}, "workbench-link": {"assign": []}, "workbench-multiroot": {"assign": ["bpasero"]}, diff --git a/.github/commands.json b/.github/commands.json index 7a1220f7..980b2906 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -202,6 +202,19 @@ "addLabel": "*caused-by-extension", "comment": "It looks like this is caused by the Python extension. Please file it with the repository [here](https://github.com/microsoft/vscode-python). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" }, + { + "type": "comment", + "name": "extJupyter", + "allowUsers": [ + "cleidigh", + "usernamehw", + "gjsjohnmurray", + "IllusionMH" + ], + "action": "close", + "addLabel": "*caused-by-extension", + "comment": "It looks like this is caused by the Jupyter extension. Please file it with the repository [here](https://github.com/microsoft/vscode-jupyter). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + }, { "type": "comment", "name": "extC", diff --git a/.github/commands.yml b/.github/commands.yml index 64fdf683..5073658e 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -1,12 +1,13 @@ { - perform: true, - commands: [ - { - type: 'comment', - name: 'findDuplicates', - allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], - action: 'comment', - comment: "Potential duplicates:\n${potentialDuplicates}" - } - ] + perform: true, + commands: + [ + { + type: "comment", + name: "findDuplicates", + allowUsers: ["cleidigh", "usernamehw", "gjsjohnmurray", "IllusionMH"], + action: "comment", + comment: "Potential duplicates:\n${potentialDuplicates}", + }, + ], } diff --git a/.github/endgame/insiders.yml b/.github/endgame/insiders.yml index f11bd611..4996474f 100644 --- a/.github/endgame/insiders.yml +++ b/.github/endgame/insiders.yml @@ -1,6 +1,6 @@ { - insidersLabel: 'insiders', - insidersColor: '006b75', - action: 'add', - perform: true -} \ No newline at end of file + insidersLabel: "insiders", + insidersColor: "006b75", + action: "add", + perform: true, +} diff --git a/.github/insiders.yml b/.github/insiders.yml index e9a49ce7..ab59e873 100644 --- a/.github/insiders.yml +++ b/.github/insiders.yml @@ -1,6 +1,6 @@ { - insidersLabel: 'insiders', - insidersColor: '006b75', - action: 'remove', - perform: true -} \ No newline at end of file + insidersLabel: "insiders", + insidersColor: "006b75", + action: "remove", + perform: true, +} diff --git a/.github/similarity.yml b/.github/similarity.yml index cd51cd2d..cb00a4c9 100644 --- a/.github/similarity.yml +++ b/.github/similarity.yml @@ -1,5 +1,5 @@ { - perform: true, - whenCreatedByTeam: false, - comment: "(Experimental duplicate detection)\nThanks for submitting this issue. Please also check if it is already covered by an existing one, like:\n${potentialDuplicates}" + perform: true, + whenCreatedByTeam: false, + comment: "(Experimental duplicate detection)\nThanks for submitting this issue. Please also check if it is already covered by an existing one, like:\n${potentialDuplicates}", } diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index de8311d7..6924d965 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -16,8 +16,8 @@ jobs: if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') @@ -34,7 +34,7 @@ jobs: with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by commenting `/verified` if things are now working as expected.\n\nIf things still don't seem right, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" + requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by commenting `/verified` if things are now working as expected.\n\nIf things still don't seem right, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command palette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" pendingReleaseLabel: awaiting-insiders-release verifiedLabel: verified authorVerificationRequestedLabel: author-verification-requested diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d509cce2..08775eab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,22 +123,22 @@ jobs: CHILD_CONCURRENCY: "1" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v1 - # TODO: rename azure-pipelines/linux/xvfb.init to github-actions - - run: | - sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start - name: Setup Build Environment - - uses: actions/setup-node@v1 - with: - node-version: 10 - - run: yarn --frozen-lockfile - name: Install Dependencies - - run: yarn monaco-compile-check - name: Run Monaco Editor Checks - - run: yarn gulp editor-esm-bundle - name: Editor Distro & ESM Bundle + - uses: actions/checkout@v1 + # TODO: rename azure-pipelines/linux/xvfb.init to github-actions + - run: | + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start + name: Setup Build Environment + - uses: actions/setup-node@v1 + with: + node-version: 10 + - run: yarn --frozen-lockfile + name: Install Dependencies + - run: yarn monaco-compile-check + name: Run Monaco Editor Checks + - run: yarn gulp editor-esm-bundle + name: Editor Distro & ESM Bundle diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 01770884..88761f8a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,48 +2,47 @@ name: "Code Scanning" on: schedule: - - cron: '0 0 * * 2' + - cron: "0 0 * * 2" jobs: CodeQL-Build: - # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: javascript + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: javascript - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index cac27757..c2b8f95e 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -11,9 +11,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml index ef7fedae..2734f6b7 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-monitor.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index de49bf3b..1aaf05a8 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -12,8 +12,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index acf3c50d..42b5f1b6 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/devcontainer-cache.yml b/.github/workflows/devcontainer-cache.yml index 82290a47..a250b56c 100644 --- a/.github/workflows/devcontainer-cache.yml +++ b/.github/workflows/devcontainer-cache.yml @@ -4,12 +4,12 @@ on: push: # Currently doing this for master, but could be done for PRs as well branches: - - 'master' + - "master" # Only updates to these files result in changes to installed packages, so skip otherwise paths: - - '**/package-lock.json' - - '**/yarn.lock' + - "**/package-lock.json" + - "**/yarn.lock" jobs: devcontainer: @@ -38,4 +38,3 @@ jobs: if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=master; fi .devcontainer/cache/build-cache-image.sh "${{ secrets.CONTAINER_IMAGE_REGISTRY }}/public/vscode/devcontainers/repos/microsoft/vscode" "${GIT_BRANCH}" - diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml index 0fdae28b..5253fb73 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -12,8 +12,8 @@ jobs: if: contains(github.event.issue.labels.*.name, '*english-please') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index 005e8818..ec3bbb5e 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -16,9 +16,9 @@ jobs: if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index f4fe0079..b27d79fd 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -12,9 +12,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Install Storage Module diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml index 4d5e9ea1..dca0f5be 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -12,9 +12,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Locker @@ -24,3 +24,5 @@ jobs: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} daysSinceUpdate: 3 ignoredLabel: "*out-of-scope" + ignoreLabelUntil: "author-verification-requested" + labelUntil: "verified" diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index 8ef8347f..75b7af42 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -12,9 +12,9 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Needs More Info Closer @@ -24,7 +24,7 @@ jobs: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} label: needs more info closeDays: 7 - additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" + additionalTeam: "cleidigh|usernamehw|gjsjohnmurray|IllusionMH" closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" pingDays: 80 pingComment: "Hey @${assignee}, this issue might need further attention.\n\n@${author}, you can help us out by closing this issue if the problem no longer exists, or adding more information." diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml index 60d2cfe2..67b8ec94 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions @@ -28,7 +28,7 @@ jobs: uses: ./actions/author-verified with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} - requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by confirming things are working as expected in the latest Insiders release. If things look good, please leave a comment with the text `/verified` to let us know. If not, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallete to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" + requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by confirming things are working as expected in the latest Insiders release. If things look good, please leave a comment with the text `/verified` to let us know. If not, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command palette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!" pendingReleaseLabel: awaiting-insiders-release verifiedLabel: verified authorVerificationRequestedLabel: author-verification-requested diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index ba392bc4..ebfa1cb3 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -10,8 +10,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml index 0b38142f..e5ea1a26 100644 --- a/.github/workflows/release-pipeline-labeler.yml +++ b/.github/workflows/release-pipeline-labeler.yml @@ -12,8 +12,8 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' - ref: v37 + repository: "microsoft/vscode-github-triage-actions" + ref: v41 path: ./actions - name: Checkout Repo if: github.event_name != 'issues' diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index bd2444b6..aee0796f 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -9,24 +9,28 @@ jobs: richnav: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - uses: actions/cache@v2 - id: caching-stage - name: Cache VS Code dependencies - with: - path: node_modules - key: ${{ runner.os }}-dependencies-${{ hashfiles('yarn.lock') }} - restore-keys: ${{ runner.os }}-dependencies- + - uses: actions/cache@v2 + id: caching-stage + name: Cache VS Code dependencies + with: + path: node_modules + key: ${{ runner.os }}-dependencies-${{ hashfiles('yarn.lock') }} + restore-keys: ${{ runner.os }}-dependencies- - - name: Install dependencies - if: steps.caching-stage.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile - env: - CHILD_CONCURRENCY: 1 + - uses: actions/setup-node@v1 + with: + node-version: 10 - - uses: microsoft/RichCodeNavIndexer@v0.1 - with: - languages: typescript - repo-token: ${{ secrets.GITHUB_TOKEN }} - continue-on-error: true + - name: Install dependencies + if: steps.caching-stage.outputs.cache-hit != 'true' + run: yarn --frozen-lockfile + env: + CHILD_CONCURRENCY: 1 + + - uses: microsoft/RichCodeNavIndexer@v0.1 + with: + languages: typescript + repo-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 3174a463..6c275210 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -12,9 +12,9 @@ jobs: if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') uses: actions/checkout@v2 with: - repository: 'microsoft/vscode-github-triage-actions' + repository: "microsoft/vscode-github-triage-actions" path: ./actions - ref: v37 + ref: v41 - name: Install Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') run: npm install --production --prefix ./actions diff --git a/.vscode/launch.json b/.vscode/launch.json index 6153d7a9..a83c164f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,10 +41,7 @@ "port": 5876, "outFiles": [ "${workspaceFolder}/out/**/*.js" - ], - "presentation": { - "hidden": true, - } + ] }, { "type": "node", @@ -200,7 +197,8 @@ "request": "attach", "name": "Attach to VS Code", "browserAttachLocation": "workspace", - "port": 9222 + "port": 9222, + "perScriptSourcemaps": "yes" }, { "type": "pwa-chrome", @@ -377,7 +375,7 @@ } }, { - "type": "node", + "type": "pwa-node", "request": "launch", "name": "Run Unit Tests", "program": "${workspaceFolder}/test/unit/electron/index.js", @@ -396,6 +394,41 @@ "outFiles": [ "${workspaceFolder}/out/**/*.js" ], + "cascadeTerminateToConfigurations": [ + "Attach to VS Code" + ], + "env": { + "MOCHA_COLORS": "true" + }, + "presentation": { + "hidden": true + } + }, + { + "type": "pwa-node", + "request": "launch", + "name": "Run Unit Tests For Current File", + "program": "${workspaceFolder}/test/unit/electron/index.js", + "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron", + "windows": { + "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe" + }, + "linux": { + "runtimeExecutable": "${workspaceFolder}/.build/electron/code-oss" + }, + "cascadeTerminateToConfigurations": [ + "Attach to VS Code" + ], + "outputCapture": "std", + "args": [ + "--remote-debugging-port=9222", + "--run", + "${relativeFile}" + ], + "cwd": "${workspaceFolder}", + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], "env": { "MOCHA_COLORS": "true" }, @@ -482,6 +515,17 @@ "group": "1_vscode", "order": 2 } + }, + { + "name": "Debug Unit Tests (Current File)", + "configurations": [ + "Attach to VS Code", + "Run Unit Tests For Current File" + ], + "presentation": { + "group": "1_vscode", + "order": 2 + } } ] } diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 2eb4b643..8ff55e2c 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"September 2020\"", + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"November 2020\"", "editable": true }, { diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 0d6e4533..e749a11c 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -2,43 +2,49 @@ { "kind": 1, "language": "markdown", - "value": "# Endgame", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "## Macros", + "value": "#### Macros", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github\n\n$MILESTONE=milestone:\"October 2020\"\n\n$MINE=assignee:@me", + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server\n\n$MILESTONE=milestone:\"November 2020\"", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "# Preparation", "editable": true }, { "kind": 1, "language": "markdown", - "value": "## Endgame Champion", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Test Plan Items", + "value": "## Open Pull Requests on the Milestone", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE label:testplan-item is:open", + "value": "$REPOS $MILESTONE is:pr is:open", "editable": true }, { "kind": 1, "language": "markdown", - "value": "### Feature Requests Missing Labels", + "value": "## Open Issues on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Feature Requests Missing Labels", "editable": true }, { @@ -50,133 +56,55 @@ { "kind": 1, "language": "markdown", - "value": "### Open Issues on the Milestone", + "value": "# Testing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Test Plan Items", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -label:testplan-item -label:iteration-plan -label:endgame-plan is:open", + "value": "$REPOS $MILESTONE is:issue is:open label:testplan-item", "editable": true }, { "kind": 1, "language": "markdown", - "value": "## Testing", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### My Assigned Test Plan Items", + "value": "## Verification Needed", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE label:testplan-item is:open", + "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request label:verification-needed -label:verified", "editable": true }, { "kind": 1, "language": "markdown", - "value": "### My Created Test Plan Items", + "value": "# Verification", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE author:@me label:testplan-item", + "value": "$REPOS $MILESTONE is:issue is:closed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", "editable": true }, { "kind": 1, "language": "markdown", - "value": "## Verification", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Issues to Verify", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "#### Issues filed by me", + "value": "# Candidates", "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "#### Issues filed by others", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE -author:@me sort:updated-asc is:closed is:issue label:bug -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Verification Needed", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate ", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### My Issues Needing Verification", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE sort:updated-desc is:closed is:issue -label:verified -label:on-testplan -label:duplicate -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found\n", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### My Open Issues", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:open is:issue", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "## Documentation", - "editable": true - }, - { - "kind": 1, - "language": "markdown", - "value": "### Needing Release Notes", - "editable": true - }, - { - "kind": 2, - "language": "github-issues", - "value": "repo:microsoft/vscode $MILESTONE $MINE is:closed label:feature-request -label:on-release-notes", + "value": "$REPOS $MILESTONE label:candidate", "editable": true } ] \ No newline at end of file diff --git a/.vscode/notebooks/grooming-delta.github-issues b/.vscode/notebooks/grooming-delta.github-issues new file mode 100644 index 00000000..8bc9d38b --- /dev/null +++ b/.vscode/notebooks/grooming-delta.github-issues @@ -0,0 +1,767 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "## Config", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$since=2020-10-01", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode\n\nQuery exceeds the maximum result. Run the query manually: `is:issue is:open closed:>2020-10-01`", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "//repo:microsoft/vscode is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "//repo:microsoft/vscode is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-remote-release", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-remote-release is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-remote-release is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-editor", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-docs", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-docs is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-docs is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-js-debug", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-js-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-js-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# language-server-protocol", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-eslint", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-eslint is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-eslint is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-css-languageservice", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-css-languageservice is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-css-languageservice is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-test", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-pull-request-github" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-pull-request-github is:issue closed:>$since" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-test is:issue created:>$since" + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-chrome-debug (deprecated)", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-chrome-debug-core", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug-core is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-chrome-debug-core is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-debugadapter-node", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-debugadapter-node is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-debugadapter-node is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-emmet-helper", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-emmet-helper is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-emmet-helper is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-extension-vscode\n\nDeprecated", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-vscode is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-vscode is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-extension-samples", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-samples is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-extension-samples is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-filewatcher-windows", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-filewatcher-windows is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-filewatcher-windows is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-generator-code", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-generator-code is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-generator-code is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-html-languageservice", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-html-languageservice is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-html-languageservice is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-jshint", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-jshint is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-jshint is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-json-languageservice", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-json-languageservice is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-json-languageservice is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-languageserver-node", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-languageserver-node is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-languageserver-node is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-loader", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-loader is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-loader is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-mono-debug", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-mono-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-mono-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-node-debug", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-node-debug2", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug2 is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-node-debug2 is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-recipes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-recipes is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-recipes is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-textmate", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-textmate is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-textmate is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-themes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-themes is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-themes is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-vsce", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-vsce is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-vsce is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-website", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-website is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-website is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# vscode-windows-process-tree", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-windows-process-tree is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode-windows-process-tree is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# debug-adapter-protocol", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/debug-adapter-protocol is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/debug-adapter-protocol is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# inno-updater", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/inno-updater is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/inno-updater is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# language-server-protocol-inspector", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol-inspector is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/language-server-protocol-inspector is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-languages", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-languages is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-languages is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-typescript", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-typescript is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-typescript is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-css", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-css is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-css is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-json", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-json is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-json is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-html", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-html is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-html is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# monaco-editor-webpack-plugin", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor-webpack-plugin is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/monaco-editor-webpack-plugin is:issue created:>$since", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# node-jsonc-parser", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/node-jsonc-parser is:issue closed:>$since", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/node-jsonc-parser is:issue created:>$since", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 687ea5b5..f44b2c71 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -20,7 +20,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode is:open is:issue assignee:@me -label:L10N -label:VIM -label:api -label:api-finalization -label:api-proposal -label:authentication -label:breadcrumbs -label:callhierarchy -label:code-lens -label:color-palette -label:comments -label:config -label:context-keys -label:css-less-scss -label:custom-editors -label:debug -label:debug-console -label:dialogs -label:diff-editor -label:dropdown -label:editor -label:editor-RTL -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-scrollbar -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:error-list -label:explorer-custom -label:extension-host -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-guess-encoding -label:file-io -label:file-watcher -label:font-rendering -label:formatting -label:git -label:github -label:gpu -label:grammar -label:grid-view -label:html -label:i18n -label:icon-brand -label:icons-product -label:install-update -label:integrated-terminal -label:integrated-terminal-conpty -label:integrated-terminal-links -label:integrated-terminal-rendering -label:integrated-terminal-winpty -label:intellisense-config -label:ipc -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:notebook -label:outline -label:output -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-explorer -label:rename -label:samples -label:sandbox -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview -label:suggest -label:sync-error-handling -label:tasks -label:telemetry -label:themes -label:timeline -label:timeline-git -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree -label:typescript -label:undo-redo -label:uri -label:ux -label:variable-resolving -label:vscode-build -label:vscode-website -label:web -label:webview -label:workbench-actions -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:zoom", + "value": "repo:microsoft/vscode is:open is:issue assignee:@me -label:L10N -label:VIM -label:api -label:api-finalization -label:api-proposal -label:authentication -label:breadcrumbs -label:callhierarchy -label:code-lens -label:color-palette -label:comments -label:config -label:context-keys -label:css-less-scss -label:custom-editors -label:debug -label:debug-console -label:dialogs -label:diff-editor -label:dropdown -label:editor -label:editor-RTL -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-scrollbar -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:error-list -label:explorer-custom -label:extension-host -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-guess-encoding -label:file-io -label:file-watcher -label:font-rendering -label:formatting -label:git -label:github -label:gpu -label:grammar -label:grid-view -label:html -label:i18n -label:icon-brand -label:icons-product -label:install-update -label:integrated-terminal -label:integrated-terminal-conpty -label:integrated-terminal-links -label:integrated-terminal-rendering -label:integrated-terminal-winpty -label:intellisense-config -label:ipc -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:notebook -label:outline -label:output -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-explorer -label:rename -label:sandbox -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview -label:suggest -label:sync-error-handling -label:tasks -label:telemetry -label:themes -label:timeline -label:timeline-git -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree -label:typescript -label:undo-redo -label:uri -label:ux -label:variable-resolving -label:vscode-build -label:vscode-website -label:web -label:webview -label:workbench-actions -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:zoom", "editable": true } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues new file mode 100644 index 00000000..54ec5328 --- /dev/null +++ b/.vscode/notebooks/my-endgame.github-issues @@ -0,0 +1,218 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "#### Macros", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server\n\n$MILESTONE=milestone:\"November 2020\"\n\n$MINE=assignee:@me", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "# Preparation", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Pull Requests on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:pr is:open", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Issues on the Milestone", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Feature Requests Missing Labels", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:closed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:open author:@me label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Verification Needed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:closed label:feature-request label:verification-needed", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Testing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Test Plan Items", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:testplan-item", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Verification Needed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed -assignee:@me -label:verified label:feature-request label:verification-needed", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Fixing", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Issues", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:endgame-plan", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Open Bugs", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Verification", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## My Issues (verification-steps-needed)", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug label:verification-steps-needed", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## My Issues (verification-found)", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Issues filed by me", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed author:@me sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Issues filed from outside team", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:bamurtaugh -author:bpasero -author:btholt -author:chrisdias -author:chrmarti -author:Chuxel -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:eamodio -author:egamma -author:fiveisprime -author:gregvanl -author:isidorn -author:ItalyPaleAle -author:JacksonKearl -author:joaomoreno -author:jrieken -author:kieferrm -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:ornellaalt -author:orta -author:rebornix -author:RMacfarlane -author:roblourens -author:rzhao271 -author:sana-ajani -author:sandy081 -author:sbatten -author:stevencl -author:Tyriar -author:weinand", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Issues filed by others", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed -author:@me sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "# Release Notes", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode $MILESTONE is:issue is:closed label:feature-request -label:on-release-notes", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index a5de65e0..55fc585e 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -8,7 +8,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks\n\n// current milestone name\n$milestone=milestone:\"September 2020\"", + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"November 2020\"", "editable": true }, { diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index 67db0cb9..58285939 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -14,7 +14,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"October 2020\"", + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"November 2020\"", "editable": true }, { diff --git a/.yarnrc b/.yarnrc index d97527da..264a5e3a 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://electronjs.org/headers" -target "9.3.3" +target "9.3.5" runtime "electron" diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 074844d8..0d0db3ac 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -28,44 +28,45 @@ This project incorporates components from the projects listed below. The origina 21. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) 22. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) 23. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) -24. jeff-hykin/cpp-textmate-grammar version 1.14.15 (https://github.com/jeff-hykin/cpp-textmate-grammar) +24. jeff-hykin/cpp-textmate-grammar version 1.15.3 (https://github.com/jeff-hykin/cpp-textmate-grammar) 25. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) 26. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) 27. language-docker (https://github.com/moby/moby) 28. language-less version 0.34.2 (https://github.com/atom/language-less) 29. language-php version 0.44.5 (https://github.com/atom/language-php) -30. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) -31. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) -32. marked version 0.6.2 (https://github.com/markedjs/marked) -33. mdn-data version 1.1.12 (https://github.com/mdn/data) -34. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) -35. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) +30. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) +31. marked version 1.1.0 (https://github.com/markedjs/marked) +32. mdn-data version 1.1.12 (https://github.com/mdn/data) +33. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) +34. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) +35. microsoft/vscode-markdown-tm-grammar (https://github.com/microsoft/vscode-markdown-tm-grammar) 36. microsoft/vscode-mssql version 1.9.0 (https://github.com/microsoft/vscode-mssql) 37. mmims/language-batchfile version 0.7.5 (https://github.com/mmims/language-batchfile) 38. octref/language-css version 0.42.11 (https://github.com/octref/language-css) 39. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) -40. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) -41. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) -42. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) -43. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) -44. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) -45. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) -46. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) -47. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) -48. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) -49. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) -50. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) -51. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) -52. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) -53. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -54. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -55. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) -56. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) -57. Unicode version 12.0.0 (https://home.unicode.org/) -58. vscode-codicons version 0.0.1 (https://github.com/microsoft/vscode-codicons) -59. vscode-logfile-highlighter version 2.8.0 (https://github.com/emilast/vscode-logfile-highlighter) -60. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -61. Web Background Synchronization (https://github.com/WICG/background-sync) +40. rust-syntax version 0.2.13 (https://github.com/dustypomerleau/rust-syntax) +41. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) +42. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) +43. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) +44. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) +45. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) +46. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) +47. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) +48. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) +49. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) +50. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) +51. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) +52. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) +53. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) +54. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) +55. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +56. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) +57. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) +58. Unicode version 12.0.0 (https://home.unicode.org/) +59. vscode-codicons version 0.0.1 (https://github.com/microsoft/vscode-codicons) +60. vscode-logfile-highlighter version 2.8.0 (https://github.com/emilast/vscode-logfile-highlighter) +61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +62. Web Background Synchronization (https://github.com/WICG/background-sync) %% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE @@ -1101,31 +1102,6 @@ suitability for any purpose. ========================================= END OF language-php NOTICES AND INFORMATION -%% language-rust NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright Š `2013` `Andreas Neuhaus` `http://zargony.com/` - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -========================================= -END OF language-rust NOTICES AND INFORMATION - %% MagicStack/MagicPython NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License @@ -1164,6 +1140,7 @@ all code is your original work. `` ## Marked +Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/) Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) Permission is hereby granted, free of charge, to any person obtaining a copy @@ -1632,6 +1609,32 @@ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFT ========================================= END OF microsoft/vscode-JSON.tmLanguage NOTICES AND INFORMATION +%% microsoft/vscode-markdown-tm-grammar NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Microsoft 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF microsoft/vscode-markdown-tm-grammar NOTICES AND INFORMATION + %% microsoft/vscode-mssql NOTICES AND INFORMATION BEGIN HERE ========================================= ------------------------------------------ START OF LICENSE ----------------------------------------- @@ -1738,6 +1741,32 @@ SOFTWARE. ========================================= END OF PowerShell/EditorSyntax NOTICES AND INFORMATION +%% rust-syntax NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2020 Dustin Pomerleau + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF rust-syntax NOTICES AND INFORMATION + %% seti-ui NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2014 Jesse Weed diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b2f9a13c..cce8119d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,3 +16,8 @@ jobs: vmImage: macOS-latest steps: - template: build/azure-pipelines/darwin/continuous-build-darwin.yml + +trigger: + branches: + exclude: + - electron-11.x.y diff --git a/build/.cachesalt b/build/.cachesalt index 3f2ee542..a7a4d508 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2020-10-05T20:24:23.714Z +2020-11-30T16:21:34.566Z diff --git a/build/.moduleignore b/build/.moduleignore index 489d4709..d1f9194b 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -1,5 +1,117 @@ +# cleanup rules for node modules, .gitignore style -# additional cleanup rules for node modules, .gitignore style +# native node modules + +nan/** +*/node_modules/nan/** + +fsevents/binding.gyp +fsevents/fsevents.cc +fsevents/build/** +fsevents/src/** +fsevents/test/** +!fsevents/**/*.node + +vscode-sqlite3/binding.gyp +vscode-sqlite3/benchmark/** +vscode-sqlite3/cloudformation/** +vscode-sqlite3/deps/** +vscode-sqlite3/test/** +vscode-sqlite3/build/** +vscode-sqlite3/src/** +!vscode-sqlite3/build/Release/*.node + +windows-mutex/binding.gyp +windows-mutex/build/** +windows-mutex/src/** +!windows-mutex/**/*.node + +native-keymap/binding.gyp +native-keymap/build/** +native-keymap/src/** +native-keymap/deps/** +!native-keymap/build/Release/*.node + +native-is-elevated/binding.gyp +native-is-elevated/build/** +native-is-elevated/src/** +native-is-elevated/deps/** +!native-is-elevated/build/Release/*.node + +native-watchdog/binding.gyp +native-watchdog/build/** +native-watchdog/src/** +!native-watchdog/build/Release/*.node + +spdlog/binding.gyp +spdlog/build/** +spdlog/deps/** +spdlog/src/** +spdlog/test/** +spdlog/*.yml +!spdlog/build/Release/*.node + +jschardet/dist/** + +windows-foreground-love/binding.gyp +windows-foreground-love/build/** +windows-foreground-love/src/** +!windows-foreground-love/**/*.node + +windows-process-tree/binding.gyp +windows-process-tree/build/** +windows-process-tree/src/** +!windows-process-tree/**/*.node + +keytar/binding.gyp +keytar/build/** +keytar/src/** +keytar/script/** +keytar/node_modules/** +!keytar/**/*.node + +node-pty/binding.gyp +node-pty/build/** +node-pty/src/** +node-pty/tools/** +node-pty/deps/** +node-pty/scripts/** +!node-pty/build/Release/*.exe +!node-pty/build/Release/*.dll +!node-pty/build/Release/*.node + +vscode-nsfw/binding.gyp +vscode-nsfw/build/** +vscode-nsfw/src/** +vscode-nsfw/openpa/** +vscode-nsfw/includes/** +!vscode-nsfw/build/Release/*.node +!vscode-nsfw/**/*.a + +vsda/build/** +vsda/ci/** +vsda/src/** +vsda/.gitignore +vsda/binding.gyp +vsda/README.md +vsda/targets +!vsda/build/Release/vsda.node + +vscode-encrypt/build/** +vscode-encrypt/src/** +vscode-encrypt/vendor/** +vscode-encrypt/.gitignore +vscode-encrypt/binding.gyp +vscode-encrypt/README.md +!vscode-encrypt/build/Release/vscode-encrypt-native.node + +vscode-windows-ca-certs/**/* +!vscode-windows-ca-certs/package.json +!vscode-windows-ca-certs/**/*.node + +node-addon-api/**/* + +# other node modules **/docs/** **/example/** diff --git a/build/.nativeignore b/build/.nativeignore deleted file mode 100644 index a2753074..00000000 --- a/build/.nativeignore +++ /dev/null @@ -1,102 +0,0 @@ -# cleanup rules for native node modules, .gitignore style - -nan/** -*/node_modules/nan/** - -fsevents/binding.gyp -fsevents/fsevents.cc -fsevents/build/** -fsevents/src/** -fsevents/test/** -!fsevents/**/*.node - -vscode-sqlite3/binding.gyp -vscode-sqlite3/benchmark/** -vscode-sqlite3/cloudformation/** -vscode-sqlite3/deps/** -vscode-sqlite3/test/** -vscode-sqlite3/build/** -vscode-sqlite3/src/** -!vscode-sqlite3/build/Release/*.node - -windows-mutex/binding.gyp -windows-mutex/build/** -windows-mutex/src/** -!windows-mutex/**/*.node - -native-keymap/binding.gyp -native-keymap/build/** -native-keymap/src/** -native-keymap/deps/** -!native-keymap/build/Release/*.node - -native-is-elevated/binding.gyp -native-is-elevated/build/** -native-is-elevated/src/** -native-is-elevated/deps/** -!native-is-elevated/build/Release/*.node - -native-watchdog/binding.gyp -native-watchdog/build/** -native-watchdog/src/** -!native-watchdog/build/Release/*.node - -spdlog/binding.gyp -spdlog/build/** -spdlog/deps/** -spdlog/src/** -spdlog/test/** -spdlog/*.yml -!spdlog/build/Release/*.node - -jschardet/dist/** - -windows-foreground-love/binding.gyp -windows-foreground-love/build/** -windows-foreground-love/src/** -!windows-foreground-love/**/*.node - -windows-process-tree/binding.gyp -windows-process-tree/build/** -windows-process-tree/src/** -!windows-process-tree/**/*.node - -keytar/binding.gyp -keytar/build/** -keytar/src/** -keytar/script/** -keytar/node_modules/** -!keytar/**/*.node - -node-pty/binding.gyp -node-pty/build/** -node-pty/src/** -node-pty/tools/** -node-pty/deps/** -node-pty/scripts/** -!node-pty/build/Release/*.exe -!node-pty/build/Release/*.dll -!node-pty/build/Release/*.node - -vscode-nsfw/binding.gyp -vscode-nsfw/build/** -vscode-nsfw/src/** -vscode-nsfw/openpa/** -vscode-nsfw/includes/** -!vscode-nsfw/build/Release/*.node -!vscode-nsfw/**/*.a - -vsda/build/** -vsda/ci/** -vsda/src/** -vsda/.gitignore -vsda/binding.gyp -vsda/README.md -vsda/targets -!vsda/build/Release/vsda.node - -vscode-windows-ca-certs/**/* -!vscode-windows-ca-certs/package.json -!vscode-windows-ca-certs/**/*.node - -node-addon-api/**/* diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts index d7e62629..daf60d71 100644 --- a/build/azure-pipelines/common/createAsset.ts +++ b/build/azure-pipelines/common/createAsset.ts @@ -11,6 +11,7 @@ import * as crypto from 'crypto'; import * as azure from 'azure-storage'; import * as mime from 'mime'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; interface Asset { platform: string; @@ -121,7 +122,7 @@ async function main(): Promise { const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, key: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); + await retry(() => scripts.storedProcedure('createAsset').execute('', [commit, asset, true])); } main().then(() => { diff --git a/build/azure-pipelines/common/createBuild.ts b/build/azure-pipelines/common/createBuild.ts index c8fd66b7..e314d7c0 100644 --- a/build/azure-pipelines/common/createBuild.ts +++ b/build/azure-pipelines/common/createBuild.ts @@ -6,6 +6,7 @@ 'use strict'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; if (process.argv.length !== 3) { console.error('Usage: node createBuild.js VERSION'); @@ -48,7 +49,7 @@ async function main(): Promise { const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, key: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createBuild').execute('', [{ ...build, _partitionKey: '' }]); + await retry(() => scripts.storedProcedure('createBuild').execute('', [{ ...build, _partitionKey: '' }])); } main().then(() => { diff --git a/build/azure-pipelines/common/releaseBuild.ts b/build/azure-pipelines/common/releaseBuild.ts index ac49e7b0..d42b3f1a 100644 --- a/build/azure-pipelines/common/releaseBuild.ts +++ b/build/azure-pipelines/common/releaseBuild.ts @@ -6,6 +6,7 @@ 'use strict'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; function getEnv(name: string): string { const result = process.env[name]; @@ -58,7 +59,7 @@ async function main(): Promise { console.log(`Releasing build ${commit}...`); const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('releaseBuild').execute('', [commit]); + await retry(() => scripts.storedProcedure('releaseBuild').execute('', [commit])); } main().then(() => { diff --git a/build/azure-pipelines/common/retry.ts b/build/azure-pipelines/common/retry.ts new file mode 100644 index 00000000..17376765 --- /dev/null +++ b/build/azure-pipelines/common/retry.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export async function retry(fn: () => Promise): Promise { + for (let run = 1; run <= 10; run++) { + try { + return await fn(); + } catch (err) { + if (!/ECONNRESET/.test(err.message)) { + throw err; + } + + const millis = (Math.random() * 200) + (50 * Math.pow(1.5, run)); + console.log(`Failed with ECONNRESET, retrying in ${millis}ms...`); + + // maximum delay is 10th retry: ~3 seconds + await new Promise(c => setTimeout(c, millis)); + } + } + + throw new Error('Retried too many times'); +} diff --git a/build/azure-pipelines/common/sync-mooncake.ts b/build/azure-pipelines/common/sync-mooncake.ts index 76f12185..4ffe7a8f 100644 --- a/build/azure-pipelines/common/sync-mooncake.ts +++ b/build/azure-pipelines/common/sync-mooncake.ts @@ -9,6 +9,7 @@ import * as url from 'url'; import * as azure from 'azure-storage'; import * as mime from 'mime'; import { CosmosClient } from '@azure/cosmos'; +import { retry } from './retry'; function log(...args: any[]) { console.log(...[`[${new Date().toISOString()}]`, ...args]); @@ -99,8 +100,8 @@ async function sync(commit: string, quality: string): Promise { log(` Updating build in DB...`); const mooncakeUrl = `${process.env['MOONCAKE_CDN_URL']}${blobPath}`; - await container.scripts.storedProcedure('setAssetMooncakeUrl') - .execute('', [commit, asset.platform, asset.type, mooncakeUrl]); + await retry(() => container.scripts.storedProcedure('setAssetMooncakeUrl') + .execute('', [commit, asset.platform, asset.type, mooncakeUrl])); log(` Done ✔ī¸`); } catch (err) { diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 954e5bca..55efe5f3 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -1,79 +1,79 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" -- script: | - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install Dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install Dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - yarn electron x64 - displayName: Download Electron + - script: | + yarn electron x64 + displayName: Download Electron -- script: | - yarn monaco-compile-check - displayName: Run Monaco Editor Checks + - script: | + yarn monaco-compile-check + displayName: Run Monaco Editor Checks -- script: | - yarn valid-layers-check - displayName: Run Valid Layers Checks + - script: | + yarn valid-layers-check + displayName: Run Valid Layers Checks -- script: | - yarn compile - displayName: Compile Sources + - script: | + yarn compile + displayName: Compile Sources -- script: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions + - script: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions -- script: | - ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests (Electron) + - script: | + ./scripts/test.sh --tfs "Unit Tests" + displayName: Run Unit Tests (Electron) -- script: | - yarn test-browser --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" - displayName: Run Unit Tests (Browser) + - script: | + yarn test-browser --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) -- script: | - ./scripts/test-integration.sh --tfs "Integration Tests" - displayName: Run Integration Tests (Electron) + - script: | + ./scripts/test-integration.sh --tfs "Integration Tests" + displayName: Run Integration Tests (Electron) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-macos - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-macos + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 0500f84a..6f7dec16 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -1,270 +1,337 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flags -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + sudo xcode-select -s /Applications/Xcode_12.2.app + displayName: Switch to Xcode 12 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + echo -n $(VSCODE_ARCH) > .build/arch + displayName: Prepare yarn cache flags -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - script: | + set -e + npm install -g node-gyp@7.1.0 + node-gyp --version + displayName: Update node-gyp + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-darwin-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-darwin-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-darwin-min-ci - displayName: Build + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + export SDKROOT=/Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk + export CHILD_CONCURRENCY="1" -- script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + export SDKROOT=/Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk + ls /Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ - ./resources/server/test/test-web-integration.sh --browser webkit - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + export npm_config_build_from_source=true + export SDKROOT=/Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk + ls /Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + yarn electron-rebuild + cd ./node_modules/keytar + node-gyp rebuild + displayName: Rebuild native modules for ARM64 + condition: eq(variables['VSCODE_ARCH'], 'arm64') -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ - ./resources/server/test/test-remote-integration.sh - displayName: Run remote integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - yarn smoketest --build "$APP_ROOT/$APP_NAME" - continueOnError: true - displayName: Run smoke tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ - yarn smoketest --web --headless - continueOnError: true - displayName: Run smoke tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-darwin-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-darwin-min-ci + displayName: Build reh + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-macos - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - script: | + set -e + yarn electron $(VSCODE_ARCH) + displayName: Download Electron + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - script: | + set -e + security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + security default-keychain -s $(agent.tempdirectory)/buildagent.keychain + security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 + security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain + VSCODE_ARCH="$(VSCODE_ARCH)" DEBUG=electron-osx-sign* node build/darwin/sign.js + displayName: Set Hardened Entitlements -- script: | - set -e - security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - security default-keychain -s $(agent.tempdirectory)/buildagent.keychain - security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 - security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain - DEBUG=electron-osx-sign* node build/darwin/sign.js - displayName: Set Hardened Entitlements + - script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - pushd $(agent.builddirectory)/VSCode-darwin && zip -r -X -y $(agent.builddirectory)/VSCode-darwin.zip * && popd - displayName: Archive build + - script: | + set -e + yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)' - Pattern: 'VSCode-darwin.zip' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401337-Apple", - "operationSetCode": "MacAppDeveloperSign", - "parameters": [ - { - "parameterName": "Hardening", - "parameterValue": "--options=runtime" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 60 - displayName: Codesign + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - zip -d $(agent.builddirectory)/VSCode-darwin.zip "*.pkg" - displayName: Clean Archive + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + ./resources/server/test/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - BUNDLE_IDENTIFIER=$(node -p "require(\"$APP_ROOT/$APP_NAME/Contents/Resources/app/product.json\").darwinBundleIdentifier") - echo "##vso[task.setvariable variable=BundleIdentifier]$BUNDLE_IDENTIFIER" - displayName: Export bundle identifier + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ + ./resources/server/test/test-remote-integration.sh + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)' - Pattern: 'VSCode-darwin.zip' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401337-Apple", - "operationSetCode": "MacAppNotarize", - "parameters": [ - { - "parameterName": "BundleId", - "parameterValue": "$(BundleIdentifier)" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 60 - displayName: Notarization + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + yarn smoketest --build "$APP_ROOT/$APP_NAME" + continueOnError: true + displayName: Run smoke tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin - APP_NAME="`ls $APP_ROOT | head -n 1`" - "$APP_ROOT/$APP_NAME/Contents/Resources/app/bin/code" --export-default-configuration=.build - displayName: Verify start after signing (export configuration) + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + yarn smoketest --web --headless + continueOnError: true + displayName: Run smoke tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_ARCH="$(VSCODE_ARCH)" \ - ./build/azure-pipelines/darwin/publish.sh - displayName: Publish + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-macos-$(VSCODE_ARCH) + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- script: | - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - yarn gulp upload-vscode-configuration - displayName: Upload configuration (for Bing settings search) - continueOnError: true + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true + - script: | + set -e + pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd + displayName: Archive build + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: "$(agent.builddirectory)" + Pattern: "VSCode-darwin-$(VSCODE_ARCH).zip" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401337-Apple", + "operationSetCode": "MacAppDeveloperSign", + "parameters": [ + { + "parameterName": "Hardening", + "parameterValue": "--options=runtime" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 60 + displayName: Codesign + + - script: | + zip -d $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip "*.pkg" + displayName: Clean Archive + + - script: | + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + BUNDLE_IDENTIFIER=$(node -p "require(\"$APP_ROOT/$APP_NAME/Contents/Resources/app/product.json\").darwinBundleIdentifier") + echo "##vso[task.setvariable variable=BundleIdentifier]$BUNDLE_IDENTIFIER" + displayName: Export bundle identifier + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: "$(agent.builddirectory)" + Pattern: "VSCode-darwin-$(VSCODE_ARCH).zip" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401337-Apple", + "operationSetCode": "MacAppNotarize", + "parameters": [ + { + "parameterName": "BundleId", + "parameterValue": "$(BundleIdentifier)" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 60 + displayName: Notarization + + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + "$APP_ROOT/$APP_NAME/Contents/Resources/app/bin/code" --export-default-configuration=.build + displayName: Verify start after signing (export configuration) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + ./build/azure-pipelines/darwin/publish.sh + displayName: Publish + + - script: | + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + yarn gulp upload-vscode-configuration + displayName: Upload configuration (for Bing settings search) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + continueOnError: true + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh index 51ac2e63..c9f5b4ba 100755 --- a/build/azure-pipelines/darwin/publish.sh +++ b/build/azure-pipelines/darwin/publish.sh @@ -1,12 +1,18 @@ #!/usr/bin/env bash set -e +# Publish DEB +case $VSCODE_ARCH in + x64) ASSET_ID="darwin" ;; + arm64) ASSET_ID="darwin-arm64" ;; +esac + # publish the build node build/azure-pipelines/common/createAsset.js \ - darwin \ + "$ASSET_ID" \ archive \ - "VSCode-darwin-$VSCODE_QUALITY.zip" \ - ../VSCode-darwin.zip + "VSCode-$ASSET_ID.zip" \ + ../VSCode-darwin-$VSCODE_ARCH.zip if [ "$VSCODE_ARCH" == "x64" ]; then # package Remote Extension Host diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index f9bdf7fe..99bad6b7 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -1,42 +1,42 @@ trigger: branches: - include: ['master', 'release/*'] + include: ["master", "release/*"] pr: branches: - include: ['master', 'release/*'] + include: ["master", "release/*"] steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" - git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" - git fetch distro + git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" + git fetch distro - # Push master branch into oss/master - git push distro origin/master:refs/heads/oss/master + # Push master branch into oss/master + git push distro origin/master:refs/heads/oss/master - # Push every release branch into oss/release - git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro + # Push every release branch into oss/release + git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro - git merge $(node -p "require('./package.json').distro") + git merge $(node -p "require('./package.json').distro") - displayName: Sync & Merge Distro + displayName: Sync & Merge Distro diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index cf1ced09..4a7acf12 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,40 +1,35 @@ pool: - vmImage: 'Ubuntu-16.04' + vmImage: "Ubuntu-16.04" -trigger: - branches: - include: ['master'] -pr: - branches: - include: ['master'] +trigger: none steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" - git checkout origin/electron-11.x.y - git merge origin/master + git checkout origin/electron-11.x.y + git merge origin/master - # Push master branch into exploration branch - git push origin HEAD:electron-11.x.y + # Push master branch into exploration branch + git push origin HEAD:electron-11.x.y - displayName: Sync & Merge Exploration + displayName: Sync & Merge Exploration diff --git a/build/azure-pipelines/linux/alpine/install-dependencies.sh b/build/azure-pipelines/linux/alpine/install-dependencies.sh new file mode 100755 index 00000000..1d2a2325 --- /dev/null +++ b/build/azure-pipelines/linux/alpine/install-dependencies.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -e + +echo "Installing remote dependencies" +(cd remote && rm -rf node_modules && yarn) \ No newline at end of file diff --git a/build/azure-pipelines/linux/alpine/publish.sh b/build/azure-pipelines/linux/alpine/publish.sh new file mode 100755 index 00000000..4bf874c6 --- /dev/null +++ b/build/azure-pipelines/linux/alpine/publish.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e +REPO="$(pwd)" +ROOT="$REPO/.." + +PLATFORM_LINUX="linux-alpine" + +# Publish Remote Extension Host +LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX" +SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX" +SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz" +SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" + +rm -rf $ROOT/vscode-server-*.tar.* +(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) + +node build/azure-pipelines/common/createAsset.js "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$SERVER_TARBALL_PATH" + +# Publish Remote Extension Host (Web) +LEGACY_SERVER_BUILD_NAME="vscode-reh-web-$PLATFORM_LINUX" +SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX-web" +SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX-web.tar.gz" +SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" + +rm -rf $ROOT/vscode-server-*.tar.* +(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) + +node build/azure-pipelines/common/createAsset.js "server-$PLATFORM_LINUX-web" archive-unsigned "$SERVER_TARBALL_FILENAME" "$SERVER_TARBALL_PATH" diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 58e14ab0..e777f821 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -1,92 +1,92 @@ steps: -- script: | - set -e - sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start + - script: | + set -e + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" -- script: | - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install Dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install Dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - yarn electron x64 - displayName: Download Electron + - script: | + yarn electron x64 + displayName: Download Electron -- script: | - yarn gulp hygiene - displayName: Run Hygiene Checks + - script: | + yarn gulp hygiene + displayName: Run Hygiene Checks -- script: | - yarn monaco-compile-check - displayName: Run Monaco Editor Checks + - script: | + yarn monaco-compile-check + displayName: Run Monaco Editor Checks -- script: | - yarn valid-layers-check - displayName: Run Valid Layers Checks + - script: | + yarn valid-layers-check + displayName: Run Valid Layers Checks -- script: | - yarn compile - displayName: Compile Sources + - script: | + yarn compile + displayName: Compile Sources -- script: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions + - script: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions -- script: | - DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" - displayName: Run Unit Tests (Electron) + - script: | + DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" + displayName: Run Unit Tests (Electron) -- script: | - DISPLAY=:10 yarn test-browser --browser chromium --tfs "Browser Unit Tests" - displayName: Run Unit Tests (Browser) + - script: | + DISPLAY=:10 yarn test-browser --browser chromium --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) -- script: | - DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" - displayName: Run Integration Tests (Electron) + - script: | + DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" + displayName: Run Integration Tests (Electron) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-linux - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-linux + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/linux/multiarch/alpine/build.sh b/build/azure-pipelines/linux/multiarch/alpine/build.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/alpine/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh b/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/alpine/publish.sh b/build/azure-pipelines/linux/multiarch/alpine/publish.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/alpine/publish.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/arm64/build.sh b/build/azure-pipelines/linux/multiarch/arm64/build.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/arm64/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/arm64/prebuild.sh b/build/azure-pipelines/linux/multiarch/arm64/prebuild.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/arm64/prebuild.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/arm64/publish.sh b/build/azure-pipelines/linux/multiarch/arm64/publish.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/arm64/publish.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/build.sh b/build/azure-pipelines/linux/multiarch/armhf/build.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/armhf/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh b/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/publish.sh b/build/azure-pipelines/linux/multiarch/armhf/publish.sh deleted file mode 100755 index 4f01bc9a..00000000 --- a/build/azure-pipelines/linux/multiarch/armhf/publish.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -set -e -echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml new file mode 100644 index 00000000..9a54bc2b --- /dev/null +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -0,0 +1,135 @@ +steps: + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flags + + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" + + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" + + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" + + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + + - task: Docker@1 + displayName: "Pull image" + inputs: + azureSubscriptionEndpoint: "vscode-builds-subscription" + azureContainerRegistry: vscodehub.azurecr.io + command: "Run an image" + imageName: "vscode-linux-build-agent:alpine" + containerCommand: uname + + - script: | + set -e + + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) + + - script: | + echo -n "alpine" > .build/arch + displayName: Prepare yarn cache flags + + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + + - script: | + set -e + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality + + - script: | + set -e + docker run -e VSCODE_QUALITY -e CHILD_CONCURRENCY=1 -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:alpine /root/vscode/build/azure-pipelines/linux/alpine/install-dependencies.sh + displayName: Prebuild + + - script: | + set -e + yarn gulp vscode-reh-linux-alpine-min-ci + yarn gulp vscode-reh-web-linux-alpine-min-ci + displayName: Build + + - script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + ./build/azure-pipelines/linux/alpine/publish.sh + displayName: Publish + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/linux/product-build-linux-multiarch.yml b/build/azure-pipelines/linux/product-build-linux-multiarch.yml deleted file mode 100644 index 258f87ea..00000000 --- a/build/azure-pipelines/linux/product-build-linux-multiarch.yml +++ /dev/null @@ -1,115 +0,0 @@ -steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) - -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" - -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - -- task: Docker@1 - displayName: 'Pull image' - inputs: - azureSubscriptionEndpoint: 'vscode-builds-subscription' - azureContainerRegistry: vscodehub.azurecr.io - command: 'Run an image' - imageName: 'vscode-linux-build-agent:$(VSCODE_ARCH)' - containerCommand: uname - -- script: | - set -e - - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF - - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling - -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality - -- script: | - set -e - CHILD_CONCURRENCY=1 ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/prebuild.sh - displayName: Prebuild - -- script: | - set -e - ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/build.sh - displayName: Build - -- script: | - set -e - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/publish.sh - displayName: Publish - -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 031a4916..1f7884a1 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -1,217 +1,233 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flags -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro -- script: | - echo -n $VSCODE_ARCH > .build/arch - displayName: Prepare arch cache flag + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' + - script: | + echo -n $(VSCODE_ARCH) > .build/arch + displayName: Prepare yarn cache flags -- script: | - set -e - CHILD_CONCURRENCY=1 npm_config_arch=$(NPM_ARCH) yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + set -e + export npm_config_arch=$(NPM_ARCH) + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci - displayName: Build + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality -- script: | - set -e - service xvfb start - displayName: Start xvfb - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + displayName: Build -- script: | - set -e - DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + service xvfb start + displayName: Start xvfb + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - DISPLAY=:10 yarn test-browser --build --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - DISPLAY=:10 ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + DISPLAY=:10 yarn test-browser --build --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - DISPLAY=:10 ./resources/server/test/test-remote-integration.sh - displayName: Run remote integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: 'crash-dump-linux-$(VSCODE_ARCH)' - targetPath: .build/crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + DISPLAY=:10 ./resources/server/test/test-remote-integration.sh + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: "crash-dump-linux-$(VSCODE_ARCH)" + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" - displayName: Build deb, rpm packages + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" - displayName: Prepare snap package - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + displayName: Build deb, rpm packages -# needed for code signing -- task: UseDotNet@2 - displayName: 'Install .NET Core SDK 2.x' - inputs: - version: 2.x + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + displayName: Prepare snap package -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '.build/linux/rpm' - Pattern: '*.rpm' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-450779-Pgp", - "operationSetCode": "LinuxSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 120 - displayName: Codesign rpm + # needed for code signing + - task: UseDotNet@2 + displayName: "Install .NET Core SDK 2.x" + inputs: + version: 2.x -- script: | - set -e - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - VSCODE_ARCH="$(VSCODE_ARCH)" \ - ./build/azure-pipelines/linux/publish.sh - displayName: Publish + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: ".build/linux/rpm" + Pattern: "*.rpm" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-450779-Pgp", + "operationSetCode": "LinuxSign", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 120 + displayName: Codesign rpm -- task: PublishPipelineArtifact@0 - displayName: 'Publish Pipeline Artifact' - inputs: - artifactName: 'snap-$(VSCODE_ARCH)' - targetPath: .build/linux/snap-tarball - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + ./build/azure-pipelines/linux/publish.sh + displayName: Publish -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true + - task: PublishPipelineArtifact@0 + displayName: "Publish Pipeline Artifact" + inputs: + artifactName: "snap-$(VSCODE_ARCH)" + targetPath: .build/linux/snap-tarball + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh index 72fe2ad7..e0f1ade3 100755 --- a/build/azure-pipelines/linux/publish.sh +++ b/build/azure-pipelines/linux/publish.sh @@ -52,11 +52,9 @@ RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" node build/azure-pipelines/common/createAsset.js "$PLATFORM_RPM" package "$RPM_FILENAME" "$RPM_PATH" -if [ "$VSCODE_ARCH" == "x64" ]; then - # Publish Snap - # Pack snap tarball artifact, in order to preserve file perms - mkdir -p $REPO/.build/linux/snap-tarball - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" - rm -rf $SNAP_TARBALL_PATH - (cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) -fi +# Publish Snap +# Pack snap tarball artifact, in order to preserve file perms +mkdir -p $REPO/.build/linux/snap-tarball +SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" +rm -rf $SNAP_TARBALL_PATH +(cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 39c39e86..f08c7b3c 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -1,52 +1,56 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- task: DownloadPipelineArtifact@0 - displayName: 'Download Pipeline Artifact' - inputs: - artifactName: snap-x64 - targetPath: .build/linux/snap-tarball + - task: DownloadPipelineArtifact@0 + displayName: "Download Pipeline Artifact" + inputs: + artifactName: snap-$(VSCODE_ARCH) + targetPath: .build/linux/snap-tarball -- script: | - set -e + - script: | + set -e - # Get snapcraft version - snapcraft --version + # Get snapcraft version + snapcraft --version - # Make sure we get latest packages - sudo apt-get update - sudo apt-get upgrade -y + # Make sure we get latest packages + sudo apt-get update + sudo apt-get upgrade -y - # Define variables - REPO="$(pwd)" - SNAP_ROOT="$REPO/.build/linux/snap/x64" + # Define variables + REPO="$(pwd)" + SNAP_ROOT="$REPO/.build/linux/snap/$(VSCODE_ARCH)" - # Install build dependencies - (cd build && yarn) + # Install build dependencies + (cd build && yarn) - # Unpack snap tarball artifact, in order to preserve file perms - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-x64.tar.gz" - (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) + # Unpack snap tarball artifact, in order to preserve file perms + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" + (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) - # Create snap package - BUILD_VERSION="$(date +%s)" - SNAP_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.snap" - SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" - (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft snap --output "$SNAP_PATH") + # Create snap package + BUILD_VERSION="$(date +%s)" + SNAP_FILENAME="code-$VSCODE_QUALITY-$(VSCODE_ARCH)-$BUILD_VERSION.snap" + SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" + case $(VSCODE_ARCH) in + x64) SNAPCRAFT_TARGET_ARGS="" ;; + *) SNAPCRAFT_TARGET_ARGS="--target-arch $(VSCODE_ARCH)" ;; + esac + (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft snap $SNAPCRAFT_TARGET_ARGS --output "$SNAP_PATH") - # Publish snap package - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - node build/azure-pipelines/common/createAsset.js "linux-snap-x64" package "$SNAP_FILENAME" "$SNAP_PATH" + # Publish snap package + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + node build/azure-pipelines/common/createAsset.js "linux-snap-$(VSCODE_ARCH)" package "$SNAP_FILENAME" "$SNAP_PATH" diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 83966344..ebe2fe6a 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -2,160 +2,201 @@ trigger: none pr: none schedules: -- cron: "0 5 * * Mon-Fri" - displayName: Mon-Fri at 7:00 - branches: - include: - - master + - cron: "0 5 * * Mon-Fri" + displayName: Mon-Fri at 7:00 + branches: + include: + - master resources: containers: - - container: vscode-x64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 - endpoint: VSCodeHub - - container: vscode-arm64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-arm64 - endpoint: VSCodeHub - - container: vscode-armhf - image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-armhf - endpoint: VSCodeHub - - container: snapcraft - image: snapcore/snapcraft:stable + - container: vscode-x64 + image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 + endpoint: VSCodeHub + - container: vscode-arm64 + image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-arm64 + endpoint: VSCodeHub + - container: vscode-armhf + image: vscodehub.azurecr.io/vscode-linux-build-agent:stretch-armhf + endpoint: VSCodeHub + - container: snapcraft + image: snapcore/snapcraft:stable stages: -- stage: Compile - jobs: - - job: Compile - pool: - vmImage: 'Ubuntu-16.04' - container: vscode-x64 - steps: - - template: product-compile.yml + - stage: Compile + jobs: + - job: Compile + pool: + vmImage: "Ubuntu-16.04" + container: vscode-x64 + variables: + VSCODE_ARCH: x64 + steps: + - template: product-compile.yml -- stage: Windows - dependsOn: - - Compile - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: VS2017-Win2016 - jobs: - - job: Windows - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) - variables: - VSCODE_ARCH: x64 - steps: - - template: win32/product-build-win32.yml - - - job: Windows32 - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) - variables: - VSCODE_ARCH: ia32 - steps: - - template: win32/product-build-win32.yml - - - job: WindowsARM64 - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) - variables: - VSCODE_ARCH: arm64 - steps: - - template: win32/product-build-win32-arm64.yml - -- stage: Linux - dependsOn: - - Compile - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: 'Ubuntu-16.04' - jobs: - - job: Linux - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - container: vscode-x64 - variables: - VSCODE_ARCH: x64 - NPM_ARCH: x64 - steps: - - template: linux/product-build-linux.yml - - - job: LinuxSnap + - stage: Windows dependsOn: - - Linux - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) - container: snapcraft - variables: - VSCODE_ARCH: x64 - steps: - - template: linux/snap-build-linux.yml + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: VS2017-Win2016 + jobs: + - job: Windows + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml - - job: LinuxArmhf - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) - container: vscode-armhf - variables: - VSCODE_ARCH: armhf - NPM_ARCH: armv7l - steps: - - template: linux/product-build-linux.yml + - job: Windows32 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: ia32 + steps: + - template: win32/product-build-win32.yml - - job: LinuxArm64 - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) - container: vscode-arm64 - variables: - VSCODE_ARCH: arm64 - NPM_ARCH: arm64 - steps: - - template: linux/product-build-linux.yml + - job: WindowsARM64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: arm64 + steps: + - template: win32/product-build-win32.yml - - job: LinuxAlpine - condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) - variables: - VSCODE_ARCH: alpine - steps: - - template: linux/product-build-linux-multiarch.yml + - stage: Linux + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: "Ubuntu-16.04" + jobs: + - job: Linux + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: vscode-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + steps: + - template: linux/product-build-linux.yml - - job: LinuxWeb - condition: and(succeeded(), eq(variables['VSCODE_BUILD_WEB'], 'true')) - variables: - VSCODE_ARCH: x64 - steps: - - template: web/product-build-web.yml + - job: LinuxSnap + dependsOn: + - Linux + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX'], 'true')) + container: snapcraft + variables: + VSCODE_ARCH: x64 + steps: + - template: linux/snap-build-linux.yml -- stage: macOS - dependsOn: - - Compile - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: macOS-latest - jobs: - - job: macOS - condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true')) - variables: - VSCODE_ARCH: x64 - steps: - - template: darwin/product-build-darwin.yml + - job: LinuxArmhf + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) + container: vscode-armhf + variables: + VSCODE_ARCH: armhf + NPM_ARCH: armv7l + steps: + - template: linux/product-build-linux.yml -- stage: Mooncake - dependsOn: - - Windows - - Linux - - macOS - condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) - pool: - vmImage: 'Ubuntu-16.04' - jobs: - - job: SyncMooncake - displayName: Sync Mooncake - steps: - - template: sync-mooncake.yml + - job: LinuxSnapArmhf + dependsOn: + - LinuxArmhf + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')) + container: snapcraft + variables: + VSCODE_ARCH: armhf + steps: + - template: linux/snap-build-linux.yml -- stage: Publish - dependsOn: - - Windows - - Linux - - macOS - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) - pool: - vmImage: 'Ubuntu-16.04' - jobs: - - job: BuildService - displayName: Build Service - steps: - - template: release.yml + - job: LinuxArm64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) + container: vscode-arm64 + variables: + VSCODE_ARCH: arm64 + NPM_ARCH: arm64 + steps: + - template: linux/product-build-linux.yml + + - job: LinuxSnapArm64 + dependsOn: + - LinuxArm64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ARM64'], 'true')) + container: snapcraft + variables: + VSCODE_ARCH: arm64 + steps: + - template: linux/snap-build-linux.yml + + - job: LinuxAlpine + condition: and(succeeded(), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true')) + steps: + - template: linux/product-build-alpine.yml + + - job: LinuxWeb + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WEB'], 'true')) + variables: + VSCODE_ARCH: x64 + steps: + - template: web/product-build-web.yml + + - stage: macOS + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: macOS-latest + jobs: + - job: macOS + condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin.yml + + - stage: macOSARM64 + dependsOn: + - Compile + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: macOS-latest + jobs: + - job: macOSARM64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS_ARM64'], 'true')) + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: arm64 + steps: + - template: darwin/product-build-darwin.yml + + - stage: Mooncake + dependsOn: + - Windows + - Linux + - macOS + - macOSARM64 + condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + pool: + vmImage: "Ubuntu-16.04" + jobs: + - job: SyncMooncake + displayName: Sync Mooncake + steps: + - template: sync-mooncake.yml + + - stage: Publish + dependsOn: + - Windows + - Linux + - macOS + - macOSARM64 + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) + pool: + vmImage: "Ubuntu-16.04" + jobs: + - job: BuildService + displayName: Build Service + steps: + - template: release.yml diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 2eff40b5..eaf0a65e 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -1,146 +1,161 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flag -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - dryRun: true + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" + dryRun: true -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - echo -n $VSCODE_ARCH > .build/arch - displayName: Prepare arch cache flag + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + echo -n $(VSCODE_ARCH) > .build/arch + displayName: Prepare yarn cache flags -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.build/arch, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + - script: | + set -e + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) -# Mixin must run before optimize, because the CSS loader will -# inline small SVGs -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - yarn gulp hygiene - yarn monaco-compile-check - yarn valid-layers-check - displayName: Run hygiene, monaco compile & valid layers checks - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + # Mixin must run before optimize, because the CSS loader will + # inline small SVGs + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set - - ./build/azure-pipelines/common/extract-telemetry.sh - displayName: Extract Telemetry - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + yarn gulp hygiene + yarn monaco-compile-check + yarn valid-layers-check + displayName: Run hygiene, monaco compile & valid layers checks + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \ - ./build/azure-pipelines/common/publish-webview.sh - displayName: Publish Webview - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set - + ./build/azure-pipelines/common/extract-telemetry.sh + displayName: Extract Telemetry + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - yarn gulp compile-build - yarn gulp compile-extensions-build - yarn gulp minify-vscode - yarn gulp minify-vscode-reh - yarn gulp minify-vscode-reh-web - displayName: Compile - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \ + ./build/azure-pipelines/common/publish-webview.sh + displayName: Publish Webview + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - node build/azure-pipelines/upload-sourcemaps - displayName: Upload sourcemaps - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + yarn gulp compile-build + yarn gulp compile-extensions-build + yarn gulp minify-vscode + yarn gulp minify-vscode-reh + yarn gulp minify-vscode-reh-web + displayName: Compile + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- script: | - set -e - VERSION=`node -p "require(\"./package.json\").version"` - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - node build/azure-pipelines/common/createBuild.js $VERSION - displayName: Create build - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + node build/azure-pipelines/upload-sourcemaps + displayName: Upload sourcemaps + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + - script: | + set -e + VERSION=`node -p "require(\"./package.json\").version"` + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + node build/azure-pipelines/common/createBuild.js $VERSION + displayName: Create build + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) + + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 10b6aa4e..5f1cf6c2 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -2,82 +2,82 @@ trigger: branches: - include: ['refs/tags/*'] + include: ["refs/tags/*"] pr: none steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- bash: | - TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - CHANNEL="G1C14HJ2F" + - bash: | + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + CHANNEL="G1C14HJ2F" - if [ "$TAG_VERSION" == "1.999.0" ]; then - MESSAGE=". Someone pushed 1.999.0 tag. Please delete it ASAP from remote and local." + if [ "$TAG_VERSION" == "1.999.0" ]; then + MESSAGE=". Someone pushed 1.999.0 tag. Please delete it ASAP from remote and local." + + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \ + https://slack.com/api/chat.postMessage + + exit 1 + fi + displayName: Check 1.999.0 tag + + - bash: | + # Install build dependencies + (cd build && yarn) + node build/azure-pipelines/publish-types/check-version.js + displayName: Check version + + - bash: | + git config --global user.email "vscode@microsoft.com" + git config --global user.name "VSCode" + + git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1 + node build/azure-pipelines/publish-types/update-types.js + + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + + cd DefinitelyTyped + + git diff --color | cat + git add -A + git status + git checkout -b "vscode-types-$TAG_VERSION" + git commit -m "VS Code $TAG_VERSION Extension API" + git push origin "vscode-types-$TAG_VERSION" + + displayName: Push update to DefinitelyTyped + + - bash: | + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + CHANNEL="G1C14HJ2F" + + MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" + LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." + MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ -H 'Content-type: application/json; charset=utf-8' \ --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \ https://slack.com/api/chat.postMessage - exit 1 - fi - displayName: Check 1.999.0 tag + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \ + https://slack.com/api/chat.postMessage -- bash: | - # Install build dependencies - (cd build && yarn) - node build/azure-pipelines/publish-types/check-version.js - displayName: Check version + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \ + https://slack.com/api/chat.postMessage -- bash: | - git config --global user.email "vscode@microsoft.com" - git config --global user.name "VSCode" - - git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1 - node build/azure-pipelines/publish-types/update-types.js - - TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - - cd DefinitelyTyped - - git diff --color | cat - git add -A - git status - git checkout -b "vscode-types-$TAG_VERSION" - git commit -m "VS Code $TAG_VERSION Extension API" - git push origin "vscode-types-$TAG_VERSION" - - displayName: Push update to DefinitelyTyped - -- bash: | - TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - CHANNEL="G1C14HJ2F" - - MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" - LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." - MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." - - curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ - -H 'Content-type: application/json; charset=utf-8' \ - --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \ - https://slack.com/api/chat.postMessage - - curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ - -H 'Content-type: application/json; charset=utf-8' \ - --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \ - https://slack.com/api/chat.postMessage - - curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ - -H 'Content-type: application/json; charset=utf-8' \ - --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \ - https://slack.com/api/chat.postMessage - - displayName: Send message on Slack + displayName: Send message on Slack diff --git a/build/azure-pipelines/release.yml b/build/azure-pipelines/release.yml index f5365b80..edd293c0 100644 --- a/build/azure-pipelines/release.yml +++ b/build/azure-pipelines/release.yml @@ -1,22 +1,22 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "10.x" + - task: NodeTool@0 + inputs: + versionSpec: "10.x" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - (cd build ; yarn) + (cd build ; yarn) - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - node build/azure-pipelines/common/releaseBuild.js + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + node build/azure-pipelines/common/releaseBuild.js diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index 49dfc9ce..10970941 100644 --- a/build/azure-pipelines/sync-mooncake.yml +++ b/build/azure-pipelines/sync-mooncake.yml @@ -1,24 +1,24 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e + - script: | + set -e - (cd build ; yarn) + (cd build ; yarn) - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \ - node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \ + node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index aded6717..33282adb 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -1,113 +1,132 @@ steps: -- script: | - mkdir -p .build - echo -n $BUILD_SOURCEVERSION > .build/commit - echo -n $VSCODE_QUALITY > .build/quality - displayName: Prepare cache flag + - script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + echo -n $VSCODE_QUALITY > .build/quality + echo -n $ENABLE_TERRAPIN > .build/terrapin + displayName: Prepare compilation cache flag -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- script: | - set -e - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling -- script: | - set -e - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro -# - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 -# inputs: -# keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: 'npm-vscode' + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- script: | - set -e - CHILD_CONCURRENCY=1 yarn --frozen-lockfile - displayName: Install dependencies - # condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | + echo -n "web" > .build/arch + displayName: Prepare yarn cache flag -# - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 -# inputs: -# keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: 'npm-vscode' -# condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -# - script: | -# set -e -# yarn postinstall -# displayName: Run postinstall scripts -# condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - script: | + set -e + export CHILD_CONCURRENCY="1" + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-web-min-ci - displayName: Build + - script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- script: | - set -e - AZURE_STORAGE_ACCOUNT="$(web-storage-account)" \ - AZURE_STORAGE_ACCESS_KEY="$(web-storage-key)" \ - node build/azure-pipelines/upload-cdn.js - displayName: Upload to CDN + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - # upload only the workbench.web.api.js source maps because - # we just compiled these bits in the previous step and the - # general task to upload source maps has already been run -- script: | - set -e - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.api.js.map - displayName: Upload sourcemaps (Web) + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-web-min-ci + displayName: Build -- script: | - set -e - AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - ./build/azure-pipelines/web/publish.sh - displayName: Publish + - script: | + set -e + AZURE_STORAGE_ACCOUNT="$(web-storage-account)" \ + AZURE_STORAGE_ACCESS_KEY="$(web-storage-key)" \ + node build/azure-pipelines/upload-cdn.js + displayName: Upload to CDN + + # upload only the workbench.web.api.js source maps because + # we just compiled these bits in the previous step and the + # general task to upload source maps has already been run + - script: | + set -e + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.api.js.map + displayName: Upload sourcemaps (Web) + + - script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + ./build/azure-pipelines/web/publish.sh + displayName: Publish diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 6107eacc..87b820cb 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -1,87 +1,87 @@ steps: -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: UsePythonVersion@0 - inputs: - versionSpec: '2.x' - addToPath: true + - task: UsePythonVersion@0 + inputs: + versionSpec: "2.x" + addToPath: true -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" -- powershell: | - yarn --frozen-lockfile - env: - CHILD_CONCURRENCY: "1" - displayName: Install Dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - powershell: | + yarn --frozen-lockfile + env: + CHILD_CONCURRENCY: "1" + displayName: Install Dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'vscode-build-cache' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "vscode-build-cache" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn postinstall } - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn postinstall } + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- powershell: | - yarn electron - displayName: Download Electron + - powershell: | + yarn electron + displayName: Download Electron -- powershell: | - yarn monaco-compile-check - displayName: Run Monaco Editor Checks + - powershell: | + yarn monaco-compile-check + displayName: Run Monaco Editor Checks -- script: | - yarn valid-layers-check - displayName: Run Valid Layers Checks + - script: | + yarn valid-layers-check + displayName: Run Valid Layers Checks -- powershell: | - yarn compile - displayName: Compile Sources + - powershell: | + yarn compile + displayName: Compile Sources -- powershell: | - yarn download-builtin-extensions - displayName: Download Built-in Extensions + - powershell: | + yarn download-builtin-extensions + displayName: Download Built-in Extensions -- powershell: | - .\scripts\test.bat --tfs "Unit Tests" - displayName: Run Unit Tests (Electron) + - powershell: | + .\scripts\test.bat --tfs "Unit Tests" + displayName: Run Unit Tests (Electron) -- powershell: | - yarn test-browser --browser chromium --browser firefox --tfs "Browser Unit Tests" - displayName: Run Unit Tests (Browser) + - powershell: | + yarn test-browser --browser chromium --browser firefox --tfs "Browser Unit Tests" + displayName: Run Unit Tests (Browser) -- powershell: | - .\scripts\test-integration.bat --tfs "Integration Tests" - displayName: Run Integration Tests (Electron) + - powershell: | + .\scripts\test-integration.bat --tfs "Integration Tests" + displayName: Run Integration Tests (Electron) -- task: PublishPipelineArtifact@0 - displayName: 'Publish Crash Reports' - inputs: - artifactName: crash-dump-windows - targetPath: .build\crashes - continueOnError: true - condition: failed() + - task: PublishPipelineArtifact@0 + displayName: "Publish Crash Reports" + inputs: + artifactName: crash-dump-windows + targetPath: .build\crashes + continueOnError: true + condition: failed() -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/win32/product-build-win32-arm64.yml b/build/azure-pipelines/win32/product-build-win32-arm64.yml deleted file mode 100644 index 2e53167e..00000000 --- a/build/azure-pipelines/win32/product-build-win32-arm64.yml +++ /dev/null @@ -1,192 +0,0 @@ -steps: -- powershell: | - mkdir .build -ea 0 - "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit - "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality - displayName: Prepare cache flag - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' - -- powershell: | - $ErrorActionPreference = "Stop" - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) - -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" - -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - -- task: UsePythonVersion@0 - inputs: - versionSpec: '2.x' - addToPath: true - -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - - exec { git config user.email "vscode@microsoft.com" } - exec { git config user.name "VSCode" } - - mkdir .build -ea 0 - "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch - displayName: Prepare tooling - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } - exec { git fetch distro } - exec { git merge $(node -p "require('./package.json').distro") } - displayName: Merge distro - -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" - exec { yarn --frozen-lockfile } - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn postinstall } - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/mixin } - displayName: Mix in quality - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } - displayName: Build - -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)' - Pattern: '*.dll,*.exe,*.node' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "VS Code" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "https://code.visualstudio.com/" - }, - { - "parameterName": "Append", - "parameterValue": "/as" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ - { - "parameterName": "VerifyAll", - "parameterValue": "/all" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 120 - -- task: NuGetCommand@2 - displayName: Install ESRPClient.exe - inputs: - restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' - feedsToUse: config - nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' - externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b - restoreDirectory: packages - -- task: ESRPImportCertTask@1 - displayName: Import ESRP Request Signing Certificate - inputs: - ESRP: 'ESRP CodeSign' - -- task: PowerShell@2 - inputs: - targetType: filePath - filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 - arguments: "$(ESRP-SSL-AADAuth)" - displayName: Import ESRP Auth Certificate - -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - .\build\azure-pipelines\win32\publish.ps1 - displayName: Publish - -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 43bd2479..1992ac1a 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,254 +1,272 @@ steps: -- powershell: | - mkdir .build -ea 0 - "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit - "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality - displayName: Prepare cache flag + - powershell: | + mkdir .build -ea 0 + "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit + "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality + "$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin + displayName: Prepare compilation cache flags -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/commit, .build/quality' - targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' - vstsFeed: 'npm-vscode' - platformIndependent: true - alias: 'Compilation' + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: "build/.cachesalt, .build/commit, .build/quality, .build/terrapin" + targetfolder: ".build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min" + vstsFeed: "npm-vscode" + platformIndependent: true + alias: "Compilation" -- powershell: | - $ErrorActionPreference = "Stop" - exit 1 - displayName: Check RestoreCache - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - powershell: | + $ErrorActionPreference = "Stop" + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) -- task: NodeTool@0 - inputs: - versionSpec: "12.14.1" + - task: NodeTool@0 + inputs: + versionSpec: "12.14.1" -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" -- task: UsePythonVersion@0 - inputs: - versionSpec: '2.x' - addToPath: true + - task: UsePythonVersion@0 + inputs: + versionSpec: "2.x" + addToPath: true -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - exec { git config user.email "vscode@microsoft.com" } - exec { git config user.name "VSCode" } + exec { git config user.email "vscode@microsoft.com" } + exec { git config user.name "VSCode" } + displayName: Prepare tooling - mkdir .build -ea 0 - "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch - displayName: Prepare tooling + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } + exec { git fetch distro } + exec { git merge $(node -p "require('./package.json').distro") } + displayName: Merge distro -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } - exec { git fetch distro } - exec { git merge $(node -p "require('./package.json').distro") } - displayName: Merge distro + - script: | + npx https://aka.ms/enablesecurefeed standAlone + displayName: Switch to Terrapin packages + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) -- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' + - powershell: | + "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch + displayName: Prepare yarn cache flags -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" - exec { yarn --frozen-lockfile } - displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" -- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 - inputs: - keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' - targetfolder: '**/node_modules, !**/node_modules/**/node_modules' - vstsFeed: 'npm-vscode' - condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + . build/azure-pipelines/win32/retry.ps1 + $ErrorActionPreference = "Stop" + $env:npm_config_arch="$(VSCODE_ARCH)" + $env:CHILD_CONCURRENCY="1" + retry { exec { yarn --frozen-lockfile } } + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn postinstall } - displayName: Run postinstall scripts - condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: ".build/arch, .build/terrapin, build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" + targetfolder: "**/node_modules, !**/node_modules/**/node_modules" + vstsFeed: "npm-vscode" + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/mixin } - displayName: Mix in quality + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn postinstall } + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-reh-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-reh-web-win32-$env:VSCODE_ARCH-min-ci" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" } - exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } - displayName: Build + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/mixin } + displayName: Mix in quality -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn electron $(VSCODE_ARCH) } - exec { .\scripts\test.bat --build --tfs "Unit Tests" } - displayName: Run unit tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-code-helper" } + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" + displayName: Build -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn test-browser --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } - displayName: Run unit tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } + exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)" + displayName: Build Server + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) -- powershell: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } - displayName: Run integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn electron $(VSCODE_ARCH) } + exec { .\scripts\test.bat --build --tfs "Unit Tests" } + displayName: Run unit tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } - displayName: Run integration tests (Browser) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-browser --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } + displayName: Run unit tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } - displayName: Run remote integration tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - powershell: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } + displayName: Run integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-windows-$(VSCODE_ARCH) - targetPath: .build\crashes - displayName: 'Publish Crash Reports' - continueOnError: true - condition: failed() + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: '*-results.xml' - searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } + displayName: Run remote integration tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - inputs: - ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)' - Pattern: '*.dll,*.exe,*.node' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "VS Code" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "https://code.visualstudio.com/" - }, - { - "parameterName": "Append", - "parameterValue": "/as" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ - { - "parameterName": "VerifyAll", - "parameterValue": "/all" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 120 + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-windows-$(VSCODE_ARCH) + targetPath: .build\crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() -- task: NuGetCommand@2 - displayName: Install ESRPClient.exe - inputs: - restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' - feedsToUse: config - nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' - externalFeedCredentials: 'ESRP Nuget' - restoreDirectory: packages + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: and(succeededOrFailed(), ne(variables['VSCODE_ARCH'], 'arm64')) -- task: ESRPImportCertTask@1 - displayName: Import ESRP Request Signing Certificate - inputs: - ESRP: 'ESRP CodeSign' + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + inputs: + ConnectedServiceName: "ESRP CodeSign" + FolderPath: "$(CodeSigningFolderPath)" + Pattern: "*.dll,*.exe,*.node" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "VS Code" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "https://code.visualstudio.com/" + }, + { + "parameterName": "Append", + "parameterValue": "/as" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd \"SHA256\"" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolVerify", + "parameters": [ + { + "parameterName": "VerifyAll", + "parameterValue": "/all" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 120 -- task: PowerShell@2 - inputs: - targetType: filePath - filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 - arguments: "$(ESRP-SSL-AADAuth)" - displayName: Import ESRP Auth Certificate + - task: NuGetCommand@2 + displayName: Install ESRPClient.exe + inputs: + restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' + feedsToUse: config + nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' + externalFeedCredentials: "ESRP Nuget" + restoreDirectory: packages -- powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - .\build\azure-pipelines\win32\publish.ps1 - displayName: Publish + - task: ESRPImportCertTask@1 + displayName: Import ESRP Request Signing Certificate + inputs: + ESRP: "ESRP CodeSign" -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true + - task: PowerShell@2 + inputs: + targetType: filePath + filePath: .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 + arguments: "$(ESRP-SSL-AADAuth)" + displayName: Import ESRP Auth Certificate + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" + $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + .\build\azure-pipelines\win32\publish.ps1 + displayName: Publish + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/win32/retry.ps1 b/build/azure-pipelines/win32/retry.ps1 new file mode 100644 index 00000000..002a5e27 --- /dev/null +++ b/build/azure-pipelines/win32/retry.ps1 @@ -0,0 +1,19 @@ +function Retry +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd + ) + $retry = 0 + + while ($retry++ -lt 3) { + try { + & $cmd + return + } catch { + # noop + } + } + + throw "Max retries reached" +} diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts index ee5d2eeb..538dc97a 100644 --- a/build/darwin/sign.ts +++ b/build/darwin/sign.ts @@ -13,6 +13,7 @@ import * as product from '../../product.json'; async function main(): Promise { const buildDir = process.env['AGENT_BUILDDIRECTORY']; const tempDir = process.env['AGENT_TEMPDIRECTORY']; + const arch = process.env['VSCODE_ARCH']; if (!buildDir) { throw new Error('$AGENT_BUILDDIRECTORY not set'); @@ -23,7 +24,7 @@ async function main(): Promise { } const baseDir = path.dirname(__dirname); - const appRoot = path.join(buildDir, 'VSCode-darwin'); + const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); const appName = product.nameLong + '.app'; const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks'); const helperAppBaseName = product.nameShort; diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index aa0282bf..118fe6e8 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -434,7 +434,7 @@ function createTscCompileTask(watch) { // stdio: [null, 'pipe', 'inherit'] }); let errors = []; - let reporter = createReporter(); + let reporter = createReporter('monaco'); let report; // eslint-disable-next-line no-control-regex let magic = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 6ae72e4c..c204e84f 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -64,7 +64,7 @@ const tasks = compilations.map(function (tsconfigFile) { } function createPipeline(build, emitError) { - const reporter = createReporter(); + const reporter = createReporter('extensions'); overrideOptions.inlineSources = Boolean(build); overrideOptions.base = path.dirname(absolutePath); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3c8bbe5b..42a9c2b2 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -58,7 +58,7 @@ const vscodeResources = [ 'out-build/bootstrap-node.js', 'out-build/bootstrap-window.js', 'out-build/paths.js', - 'out-build/vs/**/*.{svg,png,html}', + 'out-build/vs/**/*.{svg,png,html,jpg}', '!out-build/vs/code/browser/**/*.html', '!out-build/vs/editor/standalone/**/*.svg', 'out-build/vs/base/common/performance.js', @@ -201,13 +201,12 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true }); - const jsFilter = util.filter(data => !data.isDirectory() &&/\.js$/.test(data.path)); + const jsFilter = util.filter(data => !data.isDirectory() && /\.js$/.test(data.path)); const root = path.resolve(path.join(__dirname, '..')); const dependenciesSrc = _.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])); const deps = gulp.src(dependenciesSrc, { base: '.', dot: true }) .pipe(filter(['**', '!**/package-lock.json', '!**/yarn.lock', '!**/*.js.map'])) - .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))) .pipe(util.cleanNodeModules(path.join(__dirname, '.moduleignore'))) .pipe(jsFilter) .pipe(util.rewriteSourceMappingURL(sourceMappingURLBase)) @@ -326,7 +325,8 @@ const BUILD_TARGETS = [ { platform: 'win32', arch: 'ia32' }, { platform: 'win32', arch: 'x64' }, { platform: 'win32', arch: 'arm64' }, - { platform: 'darwin', arch: null, opts: { stats: true } }, + { platform: 'darwin', arch: 'x64', opts: { stats: true } }, + { platform: 'darwin', arch: 'arm64', opts: { stats: true } }, { platform: 'linux', arch: 'ia32' }, { platform: 'linux', arch: 'x64' }, { platform: 'linux', arch: 'armhf' }, @@ -338,7 +338,7 @@ BUILD_TARGETS.forEach(buildTarget => { const arch = buildTarget.arch; const opts = buildTarget.opts; - ['', 'min'].forEach(minified => { + const [vscode, vscodeMin] = ['', 'min'].map(minified => { const sourceFolderName = `out-vscode${dashed(minified)}`; const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`; @@ -355,7 +355,14 @@ BUILD_TARGETS.forEach(buildTarget => { vscodeTaskCI )); gulp.task(vscodeTask); + + return vscodeTask; }); + + if (process.platform === platform && process.arch === arch) { + gulp.task(task.define('vscode', task.series(vscode))); + gulp.task(task.define('vscode-min', task.series(vscodeMin))); + } }); // Transifex Localizations @@ -456,8 +463,10 @@ const generateVSCodeConfigurationTask = task.define('generate-vscode-configurati const userDataDir = path.join(os.tmpdir(), 'tmpuserdata'); const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); + const arch = process.env['VSCODE_ARCH']; + const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app'; - const appPath = path.join(buildDir, `VSCode-darwin/${appName}/Contents/Resources/app/bin/code`); + const appPath = path.join(appRoot, appName, 'Contents', 'Resources', 'app', 'bin', 'code'); const codeProc = cp.exec( `${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`, (err, stdout, stderr) => { diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 1d8a09e4..b6732b7f 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -98,6 +98,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@ARCHITECTURE@@', debArch)) .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) + .pipe(replace('@@REPOSITORY_NAME@@', arch === 'x64' ? 'vscode' : 'code')) .pipe(rename('DEBIAN/postinst')); const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); @@ -237,6 +238,8 @@ function prepareSnapPackage(arch) { const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@VERSION@@', commit.substr(0, 8))) + // Possible run-on values https://snapcraft.io/docs/architectures + .pipe(replace('@@ARCHITECTURE@@', arch === 'x64' ? 'amd64' : arch)) .pipe(rename('snap/snapcraft.yaml')); const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' }) diff --git a/build/hygiene.js b/build/hygiene.js index b95b4ee9..ee659891 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -83,7 +83,7 @@ const indentationFilter = [ '!src/vs/*/**/*.d.ts', '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', - '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}', + '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}', '!build/{lib,download,darwin}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', @@ -119,6 +119,7 @@ const copyrightFilter = [ '!resources/win32/bin/code.js', '!resources/web/code-web.js', '!resources/completions/**', + '!extensions/configuration-editing/build/inline-allOf.ts', '!extensions/markdown-language-features/media/highlight.css', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*', diff --git a/build/lib/eslint/code-no-unexternalized-strings.ts b/build/lib/eslint/code-no-unexternalized-strings.ts index 29db884c..8d9e142b 100644 --- a/build/lib/eslint/code-no-unexternalized-strings.ts +++ b/build/lib/eslint/code-no-unexternalized-strings.ts @@ -47,7 +47,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { // extract key so that it can be checked later let key: string | undefined; if (isStringLiteral(keyNode)) { - doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider + doubleQuotedStringLiterals.delete(keyNode); key = keyNode.value; } else if (keyNode.type === AST_NODE_TYPES.ObjectExpression) { @@ -55,7 +55,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { if (property.type === AST_NODE_TYPES.Property && !property.computed) { if (property.key.type === AST_NODE_TYPES.Identifier && property.key.name === 'key') { if (isStringLiteral(property.value)) { - doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider + doubleQuotedStringLiterals.delete(property.value); key = property.value.value; break; } @@ -123,4 +123,3 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { }; } }; - diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 47f0155d..9ec89898 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -62,6 +62,10 @@ "name": "vs/workbench/contrib/debug", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/dialogs", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/emmet", "project": "vscode-workbench" @@ -365,6 +369,14 @@ { "name": "vs/workbench/services/authentication", "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/extensionRecommendations", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/gettingStarted", + "project": "vscode-workbench" } ] } diff --git a/build/lib/reporter.ts b/build/lib/reporter.ts index ec908817..f875fce9 100644 --- a/build/lib/reporter.ts +++ b/build/lib/reporter.ts @@ -12,72 +12,89 @@ import * as ansiColors from 'ansi-colors'; import * as fs from 'fs'; import * as path from 'path'; -const allErrors: string[][] = []; -let startTime: number | null = null; -let count = 0; +class ErrorLog { + constructor(public id: string) { + } + allErrors: string[][] = []; + startTime: number | null = null; + count = 0; -function onStart(): void { - if (count++ > 0) { - return; + onStart(): void { + if (this.count++ > 0) { + return; + } + + this.startTime = new Date().getTime(); + fancyLog(`Starting ${ansiColors.green('compilation')}${this.id ? ansiColors.blue(` ${this.id}`) : ''}...`); } - startTime = new Date().getTime(); - fancyLog(`Starting ${ansiColors.green('compilation')}...`); -} + onEnd(): void { + if (--this.count > 0) { + return; + } -function onEnd(): void { - if (--count > 0) { - return; + this.log(); + } + + log(): void { + const errors = _.flatten(this.allErrors); + const seen = new Set(); + + errors.map(err => { + if (!seen.has(err)) { + seen.add(err); + fancyLog(`${ansiColors.red('Error')}: ${err}`); + } + }); + + fancyLog(`Finished ${ansiColors.green('compilation')}${this.id ? ansiColors.blue(` ${this.id}`) : ''} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - this.startTime!) + ' ms')}`); + + const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/s; + const messages = errors + .map(err => regex.exec(err)) + .filter(match => !!match) + .map(x => x as string[]) + .map(([, path, line, column, message]) => ({ path, line: parseInt(line), column: parseInt(column), message })); + + try { + const logFileName = 'log' + (this.id ? `_${this.id}` : ''); + fs.writeFileSync(path.join(buildLogFolder, logFileName), JSON.stringify(messages)); + } catch (err) { + //noop + } } - log(); } -const buildLogPath = path.join(path.dirname(path.dirname(__dirname)), '.build', 'log'); +const errorLogsById = new Map(); +function getErrorLog(id: string = '') { + let errorLog = errorLogsById.get(id); + if (!errorLog) { + errorLog = new ErrorLog(id); + errorLogsById.set(id, errorLog); + } + return errorLog; +} + +const buildLogFolder = path.join(path.dirname(path.dirname(__dirname)), '.build'); try { - fs.mkdirSync(path.dirname(buildLogPath)); + fs.mkdirSync(buildLogFolder); } catch (err) { // ignore } -function log(): void { - const errors = _.flatten(allErrors); - const seen = new Set(); - - errors.map(err => { - if (!seen.has(err)) { - seen.add(err); - fancyLog(`${ansiColors.red('Error')}: ${err}`); - } - }); - - const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; - const messages = errors - .map(err => regex.exec(err)) - .filter(match => !!match) - .map(x => x as string[]) - .map(([, path, line, column, message]) => ({ path, line: parseInt(line), column: parseInt(column), message })); - - try { - - fs.writeFileSync(buildLogPath, JSON.stringify(messages)); - } catch (err) { - //noop - } - - fancyLog(`Finished ${ansiColors.green('compilation')} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - startTime!) + ' ms')}`); -} - export interface IReporter { (err: string): void; hasErrors(): boolean; end(emitError: boolean): NodeJS.ReadWriteStream; } -export function createReporter(): IReporter { +export function createReporter(id?: string): IReporter { + const errorLog = getErrorLog(id); + const errors: string[] = []; - allErrors.push(errors); + errorLog.allErrors.push(errors); const result = (err: string) => errors.push(err); @@ -85,14 +102,14 @@ export function createReporter(): IReporter { result.end = (emitError: boolean): NodeJS.ReadWriteStream => { errors.length = 0; - onStart(); + errorLog.onStart(); return es.through(undefined, function () { - onEnd(); + errorLog.onEnd(); if (emitError && errors.length > 0) { if (!(errors as any).__logged__) { - log(); + errorLog.log(); } (errors as any).__logged__ = true; diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index cb88d37a..94dbb3b9 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -7,8 +7,8 @@ let err = false; const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]); -if (majorNodeVersion < 10 || majorNodeVersion >= 13) { - console.error('\033[1;31m*** Please use node >=10 and <=12.\033[0;0m'); +if (majorNodeVersion < 10 || majorNodeVersion >= 16) { + console.error('\033[1;31m*** Please use node >=10 and <=16.\033[0;0m'); err = true; } diff --git a/build/package.json b/build/package.json index d7e68e7c..20fc702f 100644 --- a/build/package.json +++ b/build/package.json @@ -48,7 +48,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.1.0-dev.20201018", + "typescript": "^4.2.0-dev.20201119", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" @@ -60,6 +60,6 @@ "npmCheckJs": "tsc --noEmit" }, "dependencies": { - "@azure/cosmos": "^3.4.0" + "@azure/cosmos": "^3.9.3" } } diff --git a/build/win32/code.iss b/build/win32/code.iss index 7f4b36e7..50dcf76b 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -108,6 +108,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ascx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASCX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ascx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.asp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -115,6 +116,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.asp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASP}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.asp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.aspx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -122,6 +124,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.aspx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ASPX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.aspx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -129,6 +132,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_login\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -136,6 +140,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_login\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Login}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_login\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_logout\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -143,6 +148,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_logout\OpenWith Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Logout}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_logout\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_profile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -150,6 +156,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bash_profile\OpenWit Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bash_profile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bashrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -157,6 +164,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bashrc\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bash RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bashrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bib\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -164,6 +172,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bib\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,BibTeX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bib\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bowerrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -171,6 +180,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.bowerrc\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Bower RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\bower.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bowerrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -185,6 +195,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.c\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\c.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.c\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -192,6 +203,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cc\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -199,6 +211,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -206,6 +219,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -213,6 +227,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljs\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ClojureScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -220,6 +235,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cljx\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CLJX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cljx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clojure\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -227,6 +243,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clojure\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clojure\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.code-workspace\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -234,6 +251,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.code-workspace\OpenW Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Code Workspace}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.code-workspace\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.coffee\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -241,6 +259,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.coffee\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CoffeeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.coffee\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.config\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -248,6 +267,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.config\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Configuration}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -262,6 +282,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -269,6 +290,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C#}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\csharp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cshtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -276,6 +298,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cshtml\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CSHTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cshtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csproj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -283,6 +306,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csproj\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C# Project}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csproj\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.css\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -290,6 +314,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.css\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CSS}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\css.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.css\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -297,6 +322,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.csx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\csharp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.csx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ctp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -304,6 +330,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ctp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,CakePHP Template}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ctp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cxx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -311,6 +338,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cxx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cxx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dockerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -318,6 +346,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dockerfile\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Dockerfile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dockerfile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dot\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -325,6 +354,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dot\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Dot}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dot\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dtd\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -332,6 +362,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.dtd\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Document Type Definition}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.dtd\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.editorconfig\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -339,6 +370,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.editorconfig\OpenWit Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Editor Config}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.editorconfig\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.edn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -346,6 +378,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.edn\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Extensible Data Notation}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.edn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -353,6 +386,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyaml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Hiera Eyaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -360,6 +394,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.eyml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Hiera Eyaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.eyml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -367,6 +402,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F#}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -374,6 +410,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsi\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Signature}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsscript\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -381,6 +418,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsscript\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsscript\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -388,6 +426,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.fsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,F# Script}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.fsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gemspec\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -395,6 +434,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gemspec\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Gemspec}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\ruby.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gemspec\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitattributes\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -403,6 +443,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitat Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitattributes\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitconfig\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -411,6 +452,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitco Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitconfig\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.gitignore\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -419,6 +461,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitig Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.gitignore\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.go\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -426,6 +469,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.go\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Go}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\go.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.go\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -433,6 +477,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\c.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.h\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.handlebars\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -440,6 +485,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.handlebars\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Handlebars}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.handlebars\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hbs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -447,6 +493,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hbs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Handlebars}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.h++\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -461,6 +508,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hh\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hpp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -468,6 +516,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hpp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hpp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.htm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -475,6 +524,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.htm\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.htm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.html\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -482,6 +532,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.html\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.html\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hxx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -489,6 +540,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.hxx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++ Header}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hxx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ini\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -496,6 +548,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ini\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,INI}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ini\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jade\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -503,6 +556,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jade\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Jade}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\jade.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jade\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jav\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -510,6 +564,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jav\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\java.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jav\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.java\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -517,6 +572,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.java\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\java.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.java\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.js\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -524,6 +580,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.js\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.js\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -531,6 +588,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\react.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jscsrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -538,6 +596,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jscsrc\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSCS RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jscsrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshintrc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -545,6 +604,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshintrc\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSHint RC}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshintrc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshtm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -552,6 +612,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jshtm\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript HTML Template}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jshtm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.json\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -559,6 +620,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.json\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JSON}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\json.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.json\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -566,6 +628,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.jsp\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Java Server Pages}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.jsp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.less\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -573,6 +636,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.less\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,LESS}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\less.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.less\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.lua\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -580,6 +644,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.lua\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Lua}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.lua\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.m\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -587,6 +652,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.m\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Objective C}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.m\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.makefile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -594,6 +660,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.makefile\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Makefile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.makefile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.markdown\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -601,6 +668,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.markdown\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.markdown\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.md\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -608,6 +676,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.md\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.md\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdoc\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -615,6 +684,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdoc\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,MDoc}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdoc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdown\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -622,6 +692,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdown\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdown\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtext\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -629,6 +700,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtext\OpenWithProgi Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtext\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtxt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -636,6 +708,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdtxt\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdtxt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdwn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -643,6 +716,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mdwn\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mdwn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkd\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -650,6 +724,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkd\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkd\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkdn\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -657,6 +732,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mkdn\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Markdown}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\markdown.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mkdn\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -664,6 +740,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ml\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,OCaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mli\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -671,6 +748,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mli\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,OCaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mli\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -678,6 +756,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.mjs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.mjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.npmignore\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -686,6 +765,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmig Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore"; ValueType: string; ValueName: "AlwaysShowExt"; ValueData: ""; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.npmignore\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.php\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -693,6 +773,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.php\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PHP}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\php.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.php\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.phtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -700,6 +781,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.phtml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PHP HTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.phtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -707,6 +789,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl6\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -714,6 +797,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pl6\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl 6}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pl6\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -721,6 +805,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm6\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -728,6 +813,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pm6\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl 6 Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pm6\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pod\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -735,6 +821,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pod\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl POD}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pod\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -742,6 +829,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.pp\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.pp\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.profile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -749,6 +837,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.profile\OpenWithProg Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.profile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.properties\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -756,6 +845,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.properties\OpenWithP Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Properties}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.properties\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ps1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -763,6 +853,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ps1\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ps1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psd1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -770,6 +861,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psd1\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell Module Manifest}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psd1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psgi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -777,6 +869,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psgi\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl CGI}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psgi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psm1\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -784,6 +877,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.psm1\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,PowerShell Module}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\powershell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.psm1\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.py\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -791,6 +885,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.py\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Python}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\python.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.py\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.r\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -798,6 +893,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.r\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.r\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rb\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -805,6 +901,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rb\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Ruby}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\ruby.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rb\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rhistory\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -812,6 +909,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rhistory\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R History}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rhistory\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rprofile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -819,6 +917,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rprofile\OpenWithPro Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,R Profile}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rprofile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -826,6 +925,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rs\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Rust}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -833,6 +933,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.rt\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Rich Text}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.rt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.scss\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -840,6 +941,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.scss\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Sass}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\sass.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.scss\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -847,6 +949,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sh\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SH}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.shtml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -854,6 +957,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.shtml\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SHTML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\html.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.shtml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -861,6 +965,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.sql\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SQL}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\sql.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.sql\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svg\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -868,6 +973,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svg\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SVG}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -875,6 +981,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SVGZ}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -882,6 +989,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tex\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -889,6 +997,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tex\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,LaTeX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tex\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ts\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -896,6 +1005,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.ts\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,TypeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\typescript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.ts\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tsx\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -903,6 +1013,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.tsx\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,TypeScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\react.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.tsx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.txt\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -910,6 +1021,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.txt\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Text}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.txt\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vb\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -917,6 +1029,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vb\OpenWithProgids"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Visual Basic}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vb\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vue\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -924,6 +1037,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.vue\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,VUE}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\vue.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.vue\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxi\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -931,6 +1045,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxi\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX Include}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxi\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxl\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -938,6 +1053,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxl\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX Localization}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxl\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -945,6 +1061,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.wxs\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,WiX}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.wxs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -952,6 +1069,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xaml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,XAML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -959,6 +1077,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.xml\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,XML}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\xml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.xml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yaml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -966,6 +1085,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yaml\OpenWithProgids Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Yaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yaml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yml\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -973,6 +1093,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.yml\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Yaml}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\yaml.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.yml\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.zsh\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles @@ -980,14 +1101,17 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.zsh\OpenWithProgids" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,ZSH}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\shell.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.zsh\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,{#NameLong}}"; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico" +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\shell\open"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}SourceFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe"; ValueType: none; ValueName: ""; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico" +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBasename}.exe\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufiles; Flags: uninsdeletekey diff --git a/build/yarn.lock b/build/yarn.lock index afb6ff0b..17c5c201 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2,21 +2,22 @@ # yarn lockfile v1 -"@azure/cosmos@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.4.0.tgz#96f36a4522be23e1389d0516ea4d77e5fc153221" - integrity sha512-4ym+ezk7qBe4s7/tb6IJ5kmXE4xgEbAPbraT3382oeCRlYpGrblIZIDoWbthMCJfLyLBDX5T05Fhm18QeY1R/w== +"@azure/cosmos@^3.9.3": + version "3.9.3" + resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.9.3.tgz#7e95ff92e5c3e9da7e8316bc50c9cc928be6c1d6" + integrity sha512-1mh8a6LAIykz24tJvQpafXiABUfq+HSAZBFJVZXea0Rd0qG8Ia9z8AK9FtPbC1nPvDC2RID2mRIjJvYbxRM/BA== dependencies: "@types/debug" "^4.1.4" debug "^4.1.1" fast-json-stable-stringify "^2.0.0" + jsbi "^3.1.3" node-abort-controller "^1.0.4" node-fetch "^2.6.0" - os-name "^3.1.0" priorityqueuejs "^1.0.0" semaphore "^1.0.5" - tslib "^1.9.3" - uuid "^3.3.2" + tslib "^2.0.0" + universal-user-agent "^6.0.0" + uuid "^8.3.0" "@dsherret/to-absolute-glob@^2.0.2": version "2.0.2" @@ -926,17 +927,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1004,13 +994,20 @@ debug@2.X, debug@^2.6.8: dependencies: ms "2.0.0" -debug@4, debug@^4.1.1: +debug@4: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" +debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1241,19 +1238,6 @@ event-stream@3.3.4: stream-combiner "~0.0.4" through "~2.3.1" -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" @@ -1324,9 +1308,9 @@ fast-glob@^3.0.3: micromatch "^4.0.2" fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fastq@^1.6.0: version "1.6.0" @@ -1441,13 +1425,6 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -1922,11 +1899,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -1978,11 +1950,6 @@ isbinaryfile@^3.0.2: dependencies: buffer-alloc "^1.2.0" -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -1993,6 +1960,11 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +jsbi@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.4.tgz#9654dd02207a66a4911b4e4bb74265bc2cbc9dd0" + integrity sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -2216,11 +2188,6 @@ lodash@^4.15.0, lodash@^4.17.10: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== -macos-release@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" - integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== - make-error-cause@^1.1.1: version "1.2.2" resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" @@ -2338,7 +2305,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -2366,20 +2333,15 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - node-abort-controller@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-1.0.4.tgz#4095e41d58b2fae169d2f9892904d603e11c7a39" - integrity sha512-7cNtLKTAg0LrW3ViS2C7UfIzbL3rZd8L0++5MidbKqQVJ8yrH6+1VRSHl33P0ZjBTbOJd37d9EYekvHyKkB0QQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-1.1.0.tgz#8a734a631b022af29963be7245c1483cbb9e070d" + integrity sha512-dEYmUqjtbivotqjraOe8UvhT/poFfog1BQRNsZm/MSEDDESk2cQ1tvD8kGyuN07TM/zoW+n42odL8zTeJupYdQ== node-fetch@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" @@ -2395,13 +2357,6 @@ now-and-later@^2.0.0: dependencies: once "^1.3.2" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -2476,14 +2431,6 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-name@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" - integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg== - dependencies: - macos-release "^2.2.0" - windows-release "^3.1.0" - os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -2497,11 +2444,6 @@ osenv@^0.1.3: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -2555,11 +2497,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -2662,14 +2599,6 @@ pump@^2.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - pumpify@^1.3.5: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" @@ -2971,11 +2900,6 @@ semver@^5.1.0, semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -2986,23 +2910,6 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3175,11 +3082,6 @@ strip-bom@2.X: dependencies: is-utf8 "^0.2.0" -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -3307,10 +3209,10 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@^1.9.3: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== tsutils@^3.17.1: version "3.17.1" @@ -3349,10 +3251,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.1.0-dev.20201018: - version "4.1.0-dev.20201018" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20201018.tgz#1a4b8e3f9b640218a44299773371354d75bcfa34" - integrity sha512-cOFYP1I+IrMWa6ZfefxcacZha1pQMxrq8DGMBLkvrl8k3CqIdD8APq9LXaMj/PWrB8IPgDprY6jHwqiHg0/oGA== +typescript@^4.2.0-dev.20201119: + version "4.2.0-dev.20201119" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.0-dev.20201119.tgz#d4a43511cd9931adac05e1a47b6425f6b0e76cc3" + integrity sha512-HIgv+D/0VpVYRTbcVVf9oac/0GtLKMqaufTcPgohNaFWlCOh4lq8syefANgENXTG5Q4VEC6xwDGzHW6EJAVr3A== typical@^4.0.0: version "4.0.0" @@ -3395,6 +3297,11 @@ unique-stream@^2.0.2: json-stable-stringify-without-jsonify "^1.0.1" through2-filter "^3.0.0" +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -3433,9 +3340,14 @@ uuid@^3.1.0: integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== uuid@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0: + version "8.3.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" + integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== validator@~3.35.0: version "3.35.0" @@ -3589,20 +3501,6 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -windows-release@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" - integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA== - dependencies: - execa "^1.0.0" - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" diff --git a/cglicenses.json b/cglicenses.json index 56f92bef..a0131e4f 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -389,5 +389,35 @@ "", "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] + }, + { + // Reason: The license at https://github.com/jquery/esprima/blob/master/LICENSE.BSD + // cannot be found by the OSS tool automatically. + "name": "esprima", + "fullLicenseText": [ + "BSD 2-Clause \"Simplified\" License", + "Copyright JS Foundation and other contributors, https://js.foundation/", + "", + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:", + " * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.", + " * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.", + "", + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ] + }, + { + // Reason: The license at https://github.com/LinusU/load-yaml-file/blob/master/readme.md + // cannot be found by the OSS tool automatically. + "name": "load-yaml-file", + "fullLicenseText": [ + "MIT License", + "Copyright (C) 2012-2018 by various contributors (see AUTHORS)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] } -] \ No newline at end of file +] diff --git a/cgmanifest.json b/cgmanifest.json index bd95a0d8..656e3f82 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "0b5f24002b4f18adee112ed39fe269aa51f6705c" + "commitHash": "415c1f9e9b35d9599b1a8ad1200476afa47a3323" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "9.3.3" + "version": "9.3.5" }, { "component": { diff --git a/extensions/configuration-editing/.vscodeignore b/extensions/configuration-editing/.vscodeignore index de8e6dc5..c5234c34 100644 --- a/extensions/configuration-editing/.vscodeignore +++ b/extensions/configuration-editing/.vscodeignore @@ -5,3 +5,5 @@ out/** extension.webpack.config.js extension-browser.webpack.config.js yarn.lock +build/** +schemas/*.schema.src.json diff --git a/extensions/configuration-editing/build/inline-allOf.ts b/extensions/configuration-editing/build/inline-allOf.ts new file mode 100755 index 00000000..7da64fdd --- /dev/null +++ b/extensions/configuration-editing/build/inline-allOf.ts @@ -0,0 +1,103 @@ +#!/usr/bin/env ts-node + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Inlines "allOf"s to allow for "additionalProperties": false. (https://github.com/microsoft/vscode-remote-release/issues/2967) +// Run this manually after updating devContainer.schema.src.json. + +import * as fs from 'fs'; + +function transform(schema: any) { + + const definitions = Object.keys(schema.definitions) + .reduce((d, k) => { + d[`#/definitions/${k}`] = (schema.definitions as any)[k]; + return d; + }, {} as any); + + function copy(from: any) { + const type = Array.isArray(from) ? 'array' : typeof from; + switch (type) { + case 'object': { + const to: any = {}; + for (const key in from) { + switch (key) { + case 'definitions': + break; + case 'oneOf': + const list = copy(from[key]) + .reduce((a: any[], o: any) => { + if (o.oneOf) { + a.push(...o.oneOf); + } else { + a.push(o); + } + return a; + }, [] as any[]); + if (list.length === 1) { + Object.assign(to, list[0]); + } else { + to.oneOf = list; + } + break; + case 'allOf': + const all = copy(from[key]); + const leaves = all.map((one: any) => (one.oneOf ? one.oneOf : [one])); + function cross(res: any, leaves: any[][]): any[] { + if (leaves.length) { + const rest = leaves.slice(1); + return ([] as any[]).concat(...leaves[0].map(leaf => { + const intermediate = { ...res, ...leaf }; + if ('properties' in res && 'properties' in leaf) { + intermediate.properties = { + ...res.properties, + ...leaf.properties, + }; + } + return cross(intermediate, rest); + })); + } + return [res]; + } + const list2 = cross({}, leaves); + if (list2.length === 1) { + Object.assign(to, list2[0]); + } else { + to.oneOf = list2; + } + break; + case '$ref': + const ref = from[key]; + const definition = definitions[ref]; + if (definition) { + Object.assign(to, copy(definition)); + } else { + to[key] = ref; + } + break; + default: + to[key] = copy(from[key]); + break; + } + } + if (to.type === 'object' && !('additionalProperties' in to)) { + to.additionalProperties = false; + } + return to; + } + case 'array': { + return from.map(copy); + } + default: + return from; + } + } + + return copy(schema); +} + +const devContainer = JSON.parse(fs.readFileSync('../schemas/devContainer.schema.src.json', 'utf8')); +fs.writeFileSync('../schemas/devContainer.schema.generated.json', JSON.stringify(transform(devContainer), undefined, ' ')); diff --git a/extensions/configuration-editing/build/tsconfig.json b/extensions/configuration-editing/build/tsconfig.json new file mode 100644 index 00000000..0ac2f0de --- /dev/null +++ b/extensions/configuration-editing/build/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../shared.tsconfig.json", + "compilerOptions": { + "resolveJsonModule": true, + "outDir": "./out" + } +} diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 3afe9fa4..42340041 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -111,11 +111,11 @@ }, { "fileMatch": "/.devcontainer/devcontainer.json", - "url": "./schemas/devContainer.schema.json" + "url": "./schemas/devContainer.schema.generated.json" }, { "fileMatch": "/.devcontainer.json", - "url": "./schemas/devContainer.schema.json" + "url": "./schemas/devContainer.schema.generated.json" }, { "fileMatch": "%APP_SETTINGS_HOME%/globalStorage/ms-vscode-remote.remote-containers/nameConfigs/*.json", diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index b408c46f..b3b86533 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -31,7 +31,7 @@ "null" ] }, - "description": "Remote environment variables." + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." }, "remoteUser": { "type": "string", diff --git a/extensions/configuration-editing/schemas/devContainer.schema.generated.json b/extensions/configuration-editing/schemas/devContainer.schema.generated.json new file mode 100644 index 00000000..8cdf4009 --- /dev/null +++ b/extensions/configuration-editing/schemas/devContainer.schema.generated.json @@ -0,0 +1,832 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Defines a dev container", + "allowComments": true, + "allowTrailingCommas": true, + "oneOf": [ + { + "type": "object", + "properties": { + "build": { + "type": "object", + "description": "Docker build-related options.", + "properties": { + "dockerfile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + }, + "target": { + "type": "string", + "description": "Target stage in a multi-stage build." + }, + "args": { + "type": "object", + "additionalProperties": { + "type": [ + "string" + ] + }, + "description": "Build arguments." + } + }, + "required": [ + "dockerfile" + ], + "additionalProperties": false + }, + "appPort": { + "type": [ + "integer", + "string", + "array" + ], + "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", + "items": { + "type": [ + "integer", + "string" + ] + } + }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "updateRemoteUserUID": { + "type": "boolean", + "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "type": "string" + } + }, + "runArgs": { + "type": "array", + "description": "The arguments required when starting in the container.", + "items": { + "type": "string" + } + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopContainer" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the container." + }, + "overrideCommand": { + "type": "boolean", + "description": "Whether to overwrite the command specified in the image. The default is true." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "workspaceMount": { + "type": "string", + "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "build" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "dockerFile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + }, + "build": { + "description": "Docker build-related options.", + "type": "object", + "properties": { + "target": { + "type": "string", + "description": "Target stage in a multi-stage build." + }, + "args": { + "type": "object", + "additionalProperties": { + "type": [ + "string" + ] + }, + "description": "Build arguments." + } + }, + "additionalProperties": false + }, + "appPort": { + "type": [ + "integer", + "string", + "array" + ], + "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", + "items": { + "type": [ + "integer", + "string" + ] + } + }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "updateRemoteUserUID": { + "type": "boolean", + "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "type": "string" + } + }, + "runArgs": { + "type": "array", + "description": "The arguments required when starting in the container.", + "items": { + "type": "string" + } + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopContainer" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the container." + }, + "overrideCommand": { + "type": "boolean", + "description": "Whether to overwrite the command specified in the image. The default is true." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "workspaceMount": { + "type": "string", + "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "dockerFile" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "image": { + "type": "string", + "description": "The docker image that will be used to create the container." + }, + "appPort": { + "type": [ + "integer", + "string", + "array" + ], + "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", + "items": { + "type": [ + "integer", + "string" + ] + } + }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "updateRemoteUserUID": { + "type": "boolean", + "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "type": "string" + } + }, + "runArgs": { + "type": "array", + "description": "The arguments required when starting in the container.", + "items": { + "type": "string" + } + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopContainer" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the container." + }, + "overrideCommand": { + "type": "boolean", + "description": "Whether to overwrite the command specified in the image. The default is true." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "workspaceMount": { + "type": "string", + "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "image" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "dockerComposeFile": { + "type": [ + "string", + "array" + ], + "description": "The name of the docker-compose file(s) used to start the services.", + "items": { + "type": "string" + } + }, + "service": { + "type": "string", + "description": "The service you want to work on." + }, + "runServices": { + "type": "array", + "description": "An array of services that should be started and stopped.", + "items": { + "type": "string" + } + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container. This is typically the target path of a volume mount in the docker-compose.yml." + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopCompose" + ], + "description": "Action to take when the VS Code window is closed. The default is to stop the containers." + }, + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "required": [ + "dockerComposeFile", + "service", + "workspaceFolder" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postStartCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after starting the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "postAttachCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after attaching to the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + }, + "userEnvProbe": { + "type": "string", + "enum": [ + "none", + "loginShell", + "loginInteractiveShell", + "interactiveShell" + ], + "description": "User environment probe to run. The default is none." + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration." + } + }, + "additionalProperties": false + } + ] +} \ No newline at end of file diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.src.json similarity index 97% rename from extensions/configuration-editing/schemas/devContainer.schema.json rename to extensions/configuration-editing/schemas/devContainer.schema.src.json index 4719d53e..ba140ba9 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.src.json @@ -3,7 +3,6 @@ "description": "Defines a dev container", "allowComments": true, "allowTrailingCommas": true, - "type": "object", "definitions": { "devContainerCommon": { "type": "object", @@ -42,7 +41,7 @@ "null" ] }, - "description": "Remote environment variables." + "description": "Remote environment variables. If these are used in the Integrated Terminal, make sure the 'Terminal > Integrated: Inherit Env' setting is enabled." }, "remoteUser": { "type": "string", @@ -104,6 +103,7 @@ }, "codespaces": { "type": "object", + "additionalProperties": true, "description": "Codespaces-specific configuration." } } diff --git a/extensions/cpp/build/update-grammars.js b/extensions/cpp/build/update-grammars.js index e6293eb2..28b4ca92 100644 --- a/extensions/cpp/build/update-grammars.js +++ b/extensions/cpp/build/update-grammars.js @@ -6,9 +6,9 @@ var updateGrammar = require('../../../build/npm/update-grammar'); -updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/c.tmLanguage.json', './syntaxes/c.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); -updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); -updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/cpp.embedded.macro.tmLanguage.json', './syntaxes/cpp.embedded.macro.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', 'syntaxes/c.tmLanguage.json', './syntaxes/c.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', 'syntaxes/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', 'syntaxes/cpp.embedded.macro.tmLanguage.json', './syntaxes/cpp.embedded.macro.tmLanguage.json', undefined, 'master', 'source/languages/cpp/'); // `source.c.platform` which is still included by other grammars updateGrammar.update('textmate/c.tmbundle', 'Syntaxes/Platform.tmLanguage', './syntaxes/platform.tmLanguage.json'); diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index 070312a6..05938de6 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/cpp-textmate-grammar", "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", - "commitHash": "666808cab3907fc91ed4d3901060ee6b045cca58" + "commitHash": "ad9f1541fd376740c30a7257614c9cb5ed25005d" } }, "license": "MIT", - "version": "1.14.15", + "version": "1.15.3", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/syntaxes/c.tmLanguage.json b/extensions/cpp/syntaxes/c.tmLanguage.json index eef07eeb..952730f5 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master//syntaxes/c.tmLanguage.json", + "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master/syntaxes/c.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/72b309aabb63bf14a3cdf0280149121db005d616", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/afa42b3f5529833714ae354fbc638ece8f253074", "name": "C", "scopeName": "source.c", "patterns": [ @@ -364,7 +364,7 @@ { "name": "meta.function.c", "begin": "(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\n|$)", + "captures": { "1": { - "name": "meta.encoding.c" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "2": { - "name": "punctuation.definition.string.begin.assembly.c" - } - }, - "end": "(\")", - "endCaptures": { - "1": { - "name": "punctuation.definition.string.end.assembly.c" - } - }, - "patterns": [ - { - "include": "source.asm" + "name": "comment.block.c punctuation.definition.comment.begin.c" }, - { - "include": "source.x86" + "3": { + "name": "comment.block.c" }, - { - "include": "source.x86_64" - }, - { - "include": "source.arm" - }, - { - "include": "#backslash_escapes" - }, - { - "include": "#string_escaped_char" - }, - { - "match": "(?=not)possible" + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] } - ] - }, - { - "begin": "(\\()", - "beginCaptures": { - "1": { - "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.c" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "punctuation.section.parens.end.bracket.round.assembly.inner.c" - } - }, - "patterns": [ - { - "include": "#evaluation_context" - } - ] - }, - { - "match": ":", - "name": "punctuation.separator.delimiter.colon.assembly.c" + } }, { "include": "#comments_context" }, { "include": "#comments" + }, + { + "begin": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.c" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.c punctuation.definition.comment.begin.c" + }, + "4": { + "name": "comment.block.c" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.parens.end.bracket.round.assembly.c" + } + }, + "patterns": [ + { + "name": "string.quoted.double.c", + "contentName": "meta.embedded.assembly.c", + "begin": "(R?)(\")", + "beginCaptures": { + "1": { + "name": "meta.encoding.c" + }, + "2": { + "name": "punctuation.definition.string.begin.assembly.c" + } + }, + "end": "(\")", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.assembly.c" + } + }, + "patterns": [ + { + "include": "source.asm" + }, + { + "include": "source.x86" + }, + { + "include": "source.x86_64" + }, + { + "include": "source.arm" + }, + { + "include": "#backslash_escapes" + }, + { + "include": "#string_escaped_char" + } + ] + }, + { + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.c" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.parens.end.bracket.round.assembly.inner.c" + } + }, + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + { + "match": "\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))([a-zA-Z_]\\w*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.c punctuation.definition.comment.begin.c" + }, + "3": { + "name": "comment.block.c" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] + }, + "5": { + "name": "variable.other.asm.label.c" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.c punctuation.definition.comment.begin.c" + }, + "8": { + "name": "comment.block.c" + }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.c punctuation.definition.comment.end.c" + }, + { + "match": "\\*", + "name": "comment.block.c" + } + ] + } + } + }, + { + "match": ":", + "name": "punctuation.separator.delimiter.colon.assembly.c" + }, + { + "include": "#comments_context" + }, + { + "include": "#comments" + } + ] } ] } @@ -3194,4 +3342,4 @@ "name": "punctuation.vararg-ellipses.c" } } -} +} \ No newline at end of file diff --git a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json index 4c0e9a58..151cad6f 100644 --- a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master//syntaxes/cpp.embedded.macro.tmLanguage.json", + "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master/syntaxes/cpp.embedded.macro.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/666808cab3907fc91ed4d3901060ee6b045cca58", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/ad9f1541fd376740c30a7257614c9cb5ed25005d", "name": "C++", "scopeName": "source.cpp.embedded.macro", "patterns": [ @@ -27,13 +27,13 @@ "include": "#using_namespace" }, { - "include": "#type_alias" + "include": "source.cpp#type_alias" }, { - "include": "#using_name" + "include": "source.cpp#using_name" }, { - "include": "#namespace_alias" + "include": "source.cpp#namespace_alias" }, { "include": "#namespace_block" @@ -51,10 +51,10 @@ "include": "#typedef_union" }, { - "include": "#typedef_keyword" + "include": "source.cpp#misc_keywords" }, { - "include": "#standard_declares" + "include": "source.cpp#standard_declares" }, { "include": "#class_block" @@ -69,13 +69,13 @@ "include": "#enum_block" }, { - "include": "#template_isolated_definition" + "include": "source.cpp#template_isolated_definition" }, { "include": "#template_definition" }, { - "include": "#access_control_keywords" + "include": "source.cpp#access_control_keywords" }, { "include": "#block" @@ -94,56 +94,20 @@ } ], "repository": { - "access_control_keywords": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?:protected|private|public))\\s*(:))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "storage.type.modifier.access.control.$6.cpp" - }, - "7": { - "name": "punctuation.separator.colon.access.control.cpp" - } - } - }, "alignas_attribute": { - "name": "support.other.attribute.cpp", - "begin": "(alignas\\()", + "begin": "alignas\\(", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\n)|$)", + "captures": { "1": { - "name": "meta.encoding.cpp" + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] }, "2": { - "name": "punctuation.definition.string.begin.assembly.cpp" - } - }, - "end": "(\")|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\(", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]", + "captures": { + "1": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "5": { + "name": "variable.other.asm.label.cpp" + }, + "6": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": ":", + "name": "punctuation.separator.delimiter.colon.assembly.cpp" + }, + { + "include": "#comments_context" + }, + { + "include": "#comments" + } + ] } ] }, - "assignment_operator": { - "match": "\\=", - "name": "keyword.operator.assignment.cpp" - }, "attributes_context": { "patterns": [ { @@ -402,24 +489,20 @@ } ] }, - "backslash_escapes": { - "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3]\\d{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", - "name": "constant.character.escape.cpp" - }, "block": { - "name": "meta.block.cpp", - "begin": "({)", + "begin": "{", + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(?:\\s)*+(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -768,134 +804,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.class.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.class.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "comma": { - "match": ",", - "name": "punctuation.separator.delimiter.comma.cpp" - }, - "comma_in_template_argument": { - "match": ",", - "name": "punctuation.separator.delimiter.comma.template.argument.cpp" - }, "comments": { "patterns": [ { - "name": "comment.line.double-slash.documentation.cpp", - "begin": "(?:^)(?>\\s*)(\\/\\/[!\\/]+)", + "begin": "^(?:(?:\\s)+)?+(\\/\\/[!\\/]+)", + "end": "(?<=\\n)(?|%|\"|\\.|=|::|\\||\\-\\-|\\-\\-\\-)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1186,7 +939,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1197,7 +950,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1216,22 +969,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1253,7 +1014,7 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1264,7 +1025,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1275,7 +1036,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1294,22 +1055,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1325,26 +1094,26 @@ "name": "comment.block.documentation.cpp" }, { - "name": "comment.block.documentation.cpp", - "begin": "((?>\\s*)\\/\\*[!*]+(?:(?:\\n|$)|(?=\\s)))", + "begin": "(?:(?:\\s)+)?+\\/\\*[!*]+(?:(?:(?:\\n)|$)|(?=\\s))", + "end": "[!*]*\\*\\/|(?=(?|%|\"|\\.|=|::|\\||\\-\\-|\\-\\-\\-)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1355,7 +1124,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1366,7 +1135,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1385,22 +1154,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1410,7 +1187,7 @@ ] }, { - "include": "#emacs_file_banner" + "include": "source.cpp#emacs_file_banner" }, { "include": "#block_comment" @@ -1419,31 +1196,31 @@ "include": "#line_comment" }, { - "include": "#invalid_comment_end" + "include": "source.cpp#invalid_comment_end" } ] }, "constructor_inline": { - "name": "meta.function.definition.special.constructor.cpp", - "begin": "(^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" - } - } - } - ] - }, - { - "include": "#functional_specifiers_pre_parameters" - }, - { - "begin": "(:)", - "beginCaptures": { + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" + } + } + }, + { + "include": "source.cpp#functional_specifiers_pre_parameters" + }, + { + "begin": ":", + "end": "(?=\\{)|(?=(?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" - } - } - } - ] - }, - { - "include": "#functional_specifiers_pre_parameters" - }, - { - "begin": "(:)", - "beginCaptures": { + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" + } + } + }, + { + "include": "source.cpp#functional_specifiers_pre_parameters" + }, + { + "begin": ":", + "end": "(?=\\{)|(?=(?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "end": "\\}|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(~(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(~(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))~\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:error|warning)))\\b\\s*", + "begin": "(^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", + "end": "(?[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*(?:\\n|$)))|(^\\s*((\\/\\*)\\s*?((?>[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*\\*\\/)))", - "captures": { - "1": { - "name": "meta.toc-list.banner.double-slash.cpp" - }, - "2": { - "name": "comment.line.double-slash.cpp" - }, - "3": { - "name": "punctuation.definition.comment.cpp" - }, - "4": { - "name": "meta.banner.character.cpp" - }, - "5": { - "name": "meta.toc-list.banner.block.cpp" - }, - "6": { - "name": "comment.line.banner.cpp" - }, - "7": { - "name": "punctuation.definition.comment.cpp" - }, - "8": { - "name": "meta.banner.character.cpp" - } - } - }, - "empty_square_brackets": { - "name": "storage.modifier.array.bracket.square.cpp", - "match": "(?-mix:(?-mix:(?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?(::))?\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.enum.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.enum.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "enumerator_list": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(extern)(?=\\s*\\\"))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(extern)(?=\\s*\\\")", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)|(?=(?))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\())", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))", + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", "captures": { "1": { "name": "storage.modifier.$1.cpp" @@ -4002,7 +3521,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4029,12 +3548,12 @@ ] }, "12": { - "name": "storage.modifier.$1.cpp" + "name": "storage.modifier.$12.cpp" }, "13": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4060,32 +3579,50 @@ "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4264,45 +3782,95 @@ } ] }, - "45": { + "36": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "46": { + "37": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "47": { + "38": { "name": "comment.block.cpp" }, + "39": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "40": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "41": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "42": { + "name": "comment.block.cpp" + }, + "43": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "44": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "45": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "46": { + "name": "comment.block.cpp" + }, + "47": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "48": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "49": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "50": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "51": { + "50": { "name": "comment.block.cpp" }, - "52": { + "51": { "patterns": [ { "match": "\\*\\/", @@ -4314,10 +3882,13 @@ } ] }, + "52": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "53": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4342,35 +3913,28 @@ "57": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#scope_resolution_function_definition_inner_generated" } ] }, "58": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, "59": { - "name": "comment.block.cpp" - }, - "60": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#template_call_range" } ] }, + "60": {}, "61": { - "name": "storage.type.modifier.calling-convention.cpp" + "name": "entity.name.function.definition.cpp" }, "62": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4391,83 +3955,39 @@ "name": "comment.block.cpp" } ] - }, - "66": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "67": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "69": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "71": { - "name": "entity.name.function.definition.cpp" - }, - "72": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "73": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "74": { - "name": "comment.block.cpp" - }, - "75": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "captures": { + "1": { + "name": "punctuation.definition.function.return-type.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -4739,111 +4459,110 @@ } ] }, - "29": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.other.definition.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -5091,111 +4810,110 @@ } ] }, - "29": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.parameter.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", - "captures": { - "1": { - "name": "keyword.control.goto.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.label.call.cpp" - } - } - }, - "include": { - "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.control.directive.$7.cpp" - }, - "6": { - "name": "punctuation.definition.directive.cpp" - }, - "8": { - "name": "string.quoted.other.lt-gt.include.cpp" - }, - "9": { - "name": "punctuation.definition.string.begin.cpp" - }, - "10": { - "name": "punctuation.definition.string.end.cpp" - }, - "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "name": "string.quoted.double.include.cpp" - }, - "16": { - "name": "punctuation.definition.string.begin.cpp" - }, - "17": { - "name": "punctuation.definition.string.end.cpp" - }, - "18": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "19": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "20": { - "name": "comment.block.cpp" - }, - "21": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "22": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "23": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "24": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "25": { - "name": "comment.block.cpp" - }, - "26": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "27": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "28": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "29": { - "name": "comment.block.cpp" - }, - "30": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "31": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "32": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "33": { - "name": "comment.block.cpp" - }, - "34": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "meta.preprocessor.include.cpp" - }, "inheritance_context": { "patterns": [ { @@ -5527,7 +5024,7 @@ "name": "punctuation.separator.delimiter.comma.inheritance.cpp" }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "5": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] }, "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "7": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "13": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "15": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "17": { - "name": "entity.name.scope-resolution.cpp" - }, - "18": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "20": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "25": { - "name": "entity.name.type.cpp" - }, - "26": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } + "12": {} } } ] }, - "inline_comment": { - "match": "(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/))", - "captures": { - "1": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "2": { - "name": "comment.block.cpp" - }, - "3": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "invalid_comment_end": { - "match": "\\*\\/", - "name": "invalid.illegal.unexpected.punctuation.definition.comment.end.cpp" - }, - "label": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "entity.name.label.cpp" - }, - "6": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { - "name": "comment.block.cpp" - }, - "9": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "10": { - "name": "punctuation.separator.label.cpp" - } - } - }, "lambdas": { - "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[| *+\"| *+\\d))((?>(?:[^\\[\\]]|((?(?:(?>[^\\[\\]]*)\\g<4>?)+)\\]))*))(\\](?!\\[)))", + "begin": "(?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))(?:(?:\\s)+)?(\\[(?!\\[| *+\"| *+\\d))((?:[^\\[\\]]|((??)++\\]))*+)(\\](?!((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))[\\[\\];]))", + "end": "(?<=[;}])|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", + "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", "captures": { "1": { "name": "variable.parameter.capture.cpp" @@ -5815,7 +5251,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -5850,26 +5286,52 @@ } ] }, - "5": { + "3": {}, + "4": { "name": "punctuation.definition.capture.end.lambda.cpp" + }, + "5": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "6": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "7": { + "name": "comment.block.cpp" + }, + "8": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] } }, - "end": "(?<=})|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*line\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*define\\b)\\s*((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "variable.language.this.cpp" - }, - "6": { - "name": "variable.other.object.access.cpp" - }, - "7": { - "name": "punctuation.separator.dot-access.cpp" - }, - "8": { - "name": "punctuation.separator.pointer-access.cpp" - }, - "9": { - "patterns": [ - { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "variable.language.this.cpp" - }, - "6": { - "name": "variable.other.object.property.cpp" - }, - "7": { - "name": "punctuation.separator.dot-access.cpp" - }, - "8": { - "name": "punctuation.separator.pointer-access.cpp" - } - } - }, - { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "variable.language.this.cpp" - }, - "6": { - "name": "variable.other.object.access.cpp" - }, - "7": { - "name": "punctuation.separator.dot-access.cpp" - }, - "8": { - "name": "punctuation.separator.pointer-access.cpp" - } - } - }, - { - "include": "#member_access" - }, - { - "include": "#method_access" - } - ] - }, - "10": { - "name": "variable.other.property.cpp" - } - } - }, - "memory_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.operator.wordlike.cpp" - }, - "6": { - "name": "keyword.operator.delete.array.cpp" - }, - "7": { - "name": "keyword.operator.delete.array.bracket.cpp" - }, - "8": { - "name": "keyword.operator.delete.cpp" - }, - "9": { - "name": "keyword.operator.new.cpp" - } - } - }, "method_access": { - "begin": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", + "begin": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\()", + "end": "\\)|(?=(?|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:(?:\\s)+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -6350,12 +5615,12 @@ } }, { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -6392,7 +5657,7 @@ } }, { - "include": "#member_access" + "include": "source.cpp#member_access" }, { "include": "#method_access" @@ -6406,9 +5671,8 @@ "name": "punctuation.section.arguments.begin.bracket.round.function.member.cpp" } }, - "end": "(\\))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.control.directive.import.cpp" - }, - "7": { - "name": "string.quoted.other.lt-gt.include.cpp" - }, - "8": { - "name": "punctuation.definition.string.begin.cpp" - }, - "9": { - "name": "punctuation.definition.string.end.cpp" - }, - "10": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "11": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "12": { - "name": "comment.block.cpp" - }, - "13": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "14": { - "name": "string.quoted.double.include.cpp" - }, - "15": { - "name": "punctuation.definition.string.begin.cpp" - }, - "16": { - "name": "punctuation.definition.string.end.cpp" - }, - "17": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "19": { - "name": "comment.block.cpp" - }, - "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "21": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "22": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "23": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "24": { - "name": "comment.block.cpp" - }, - "25": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "26": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "27": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "28": { - "name": "comment.block.cpp" - }, - "29": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "punctuation.terminator.statement.cpp" - } - }, - "name": "meta.preprocessor.import.cpp" - }, "ms_attributes": { - "name": "support.other.attribute.cpp", - "begin": "(__declspec\\()", + "begin": "__declspec\\(", + "end": "\\)|(?=(?(?:(?>[^<>]*)\\g<9>?)+)>)\\s*)?::)*\\s*+)\\s*((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:(?>[^<>]*)\\g<5>?)+)>)\\s*)?::)*\\s*+)\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:\\[\\])?)))|(\"\")((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\<|\\())", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -7366,20 +6103,20 @@ } ] }, - "30": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "31": { + "21": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "32": { + "22": { "name": "comment.block.cpp" }, - "33": { + "23": { "patterns": [ { "match": "\\*\\/", @@ -7391,135 +6128,135 @@ } ] }, - "34": { + "24": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "35": { + "25": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "36": { + "26": { "name": "comment.block.cpp" }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "30": { + "name": "comment.block.cpp" + }, + "31": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "32": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "33": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "34": { + "name": "comment.block.cpp" + }, + "35": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "36": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "37": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "38": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "39": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "40": { + "39": { "name": "comment.block.cpp" }, + "40": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "41": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "42": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "43": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "44": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "45": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "46": { - "name": "storage.type.modifier.calling-convention.cpp" - }, - "47": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "48": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "49": { - "name": "comment.block.cpp" - }, - "50": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "51": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "52": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "53": { - "name": "comment.block.cpp" - }, - "54": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "55": { "patterns": [ { "match": "::", @@ -7534,31 +6271,31 @@ } ] }, - "57": { - "name": "meta.template.call.cpp", + "46": { "patterns": [ { "include": "#template_call_range" } ] }, - "59": { + "47": {}, + "48": { "name": "keyword.other.operator.overload.cpp" }, - "60": { + "49": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "61": { + "50": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "62": { + "51": { "name": "comment.block.cpp" }, - "63": { + "52": { "patterns": [ { "match": "\\*\\/", @@ -7570,7 +6307,7 @@ } ] }, - "64": { + "53": { "patterns": [ { "match": "::", @@ -7585,33 +6322,33 @@ } ] }, - "66": { - "name": "meta.template.call.cpp", + "54": { "patterns": [ { "include": "#template_call_range" } ] }, - "68": { + "55": {}, + "56": { "name": "entity.name.operator.cpp" }, - "69": { + "57": { "name": "entity.name.operator.type.cpp" }, - "70": { + "58": { "patterns": [ { "match": "\\*", "name": "entity.name.operator.type.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -7642,20 +6379,20 @@ } ] }, - "71": { + "59": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "72": { + "60": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "73": { + "61": { "name": "comment.block.cpp" }, - "74": { + "62": { "patterns": [ { "match": "\\*\\/", @@ -7667,104 +6404,104 @@ } ] }, - "75": { + "63": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "76": { + "64": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "77": { + "65": { "name": "comment.block.cpp" }, + "66": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "67": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "68": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "69": { + "name": "comment.block.cpp" + }, + "70": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "71": { + "name": "entity.name.operator.type.array.cpp" + }, + "72": { + "name": "entity.name.operator.custom-literal.cpp" + }, + "73": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "74": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "75": { + "name": "comment.block.cpp" + }, + "76": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "77": { + "name": "entity.name.operator.custom-literal.cpp" + }, "78": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "79": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "80": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "81": { - "name": "comment.block.cpp" - }, - "82": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "83": { - "name": "entity.name.operator.type.array.cpp" - }, - "84": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "85": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "86": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "87": { - "name": "comment.block.cpp" - }, - "88": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "89": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "90": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "91": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "92": { - "name": "comment.block.cpp" - }, - "93": { "patterns": [ { "match": "\\*\\/", @@ -7777,17 +6514,19 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7998,7 +6994,7 @@ "3": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8023,7 +7019,7 @@ "7": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8046,159 +7042,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -8216,15 +7087,16 @@ "include": "#storage_types" }, { - "include": "#scope_resolution_parameter_inner_generated" + "include": "source.cpp#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\)|,|\\[|=|\\n)", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\)|,|\\[|=|\\n)", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8273,7 +7146,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8301,19 +7174,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -8337,12 +7210,12 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8376,7 +7249,7 @@ "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8401,7 +7274,7 @@ "5": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -8427,528 +7300,14 @@ } ] }, - "parameter_class": { - "match": "(class)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.class.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.class.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "parameter_enum": { - "match": "(enum)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.enum.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.enum.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, "parameter_or_maybe_value": { - "name": "meta.parameter.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -9015,7 +7374,7 @@ "3": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9040,7 +7399,7 @@ "7": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9063,159 +7422,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -9236,15 +7470,16 @@ "include": "#function_call" }, { - "include": "#scope_resolution_parameter_inner_generated" + "include": "source.cpp#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=(?:\\)|,|\\[|=|\\/\\/|(?:(?:\\n)|$)))", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9290,7 +7525,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9318,19 +7553,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -9354,12 +7589,12 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9393,7 +7628,7 @@ "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9418,7 +7653,7 @@ "5": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -9447,537 +7682,23 @@ } ] }, - "parameter_struct": { - "match": "(struct)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.struct.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.struct.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "parameter_union": { - "match": "(union)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", - "captures": { - "1": { - "name": "storage.type.union.parameter.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.union.parameter.cpp" - }, - "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, "parentheses": { - "name": "meta.parens.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\s+mark)\\s+(.*)", - "captures": { - "1": { - "name": "keyword.control.directive.pragma.pragma-mark.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "punctuation.definition.directive.cpp" - }, - "7": { - "name": "entity.name.tag.pragma-mark.cpp" - } - }, - "name": "meta.preprocessor.pragma.cpp" - }, - "predefined_macros": { - "patterns": [ - { - "match": "\\b(__cplusplus|__DATE__|__FILE__|__LINE__|__STDC__|__STDC_HOSTED__|__STDC_NO_COMPLEX__|__STDC_VERSION__|__STDCPP_THREADS__|__TIME__|NDEBUG|__OBJC__|__ASSEMBLER__|__ATOM__|__AVX__|__AVX2__|_CHAR_UNSIGNED|__CLR_VER|_CONTROL_FLOW_GUARD|__COUNTER__|__cplusplus_cli|__cplusplus_winrt|_CPPRTTI|_CPPUNWIND|_DEBUG|_DLL|__FUNCDNAME__|__FUNCSIG__|__FUNCTION__|_INTEGRAL_MAX_BITS|__INTELLISENSE__|_ISO_VOLATILE|_KERNEL_MODE|_M_AMD64|_M_ARM|_M_ARM_ARMV7VE|_M_ARM_FP|_M_ARM64|_M_CEE|_M_CEE_PURE|_M_CEE_SAFE|_M_FP_EXCEPT|_M_FP_FAST|_M_FP_PRECISE|_M_FP_STRICT|_M_IX86|_M_IX86_FP|_M_X64|_MANAGED|_MSC_BUILD|_MSC_EXTENSIONS|_MSC_FULL_VER|_MSC_VER|_MSVC_LANG|__MSVC_RUNTIME_CHECKS|_MT|_NATIVE_WCHAR_T_DEFINED|_OPENMP|_PREFAST|__TIMESTAMP__|_VC_NO_DEFAULTLIB|_WCHAR_T_DEFINED|_WIN32|_WIN64|_WINRT_DLL|_ATL_VER|_MFC_VER|__GFORTRAN__|__GNUC__|__GNUC_MINOR__|__GNUC_PATCHLEVEL__|__GNUG__|__STRICT_ANSI__|__BASE_FILE__|__INCLUDE_LEVEL__|__ELF__|__VERSION__|__OPTIMIZE__|__OPTIMIZE_SIZE__|__NO_INLINE__|__GNUC_STDC_INLINE__|__CHAR_UNSIGNED__|__WCHAR_UNSIGNED__|__REGISTER_PREFIX__|__REGISTER_PREFIX__|__SIZE_TYPE__|__PTRDIFF_TYPE__|__WCHAR_TYPE__|__WINT_TYPE__|__INTMAX_TYPE__|__UINTMAX_TYPE__|__SIG_ATOMIC_TYPE__|__INT8_TYPE__|__INT16_TYPE__|__INT32_TYPE__|__INT64_TYPE__|__UINT8_TYPE__|__UINT16_TYPE__|__UINT32_TYPE__|__UINT64_TYPE__|__INT_LEAST8_TYPE__|__INT_LEAST16_TYPE__|__INT_LEAST32_TYPE__|__INT_LEAST64_TYPE__|__UINT_LEAST8_TYPE__|__UINT_LEAST16_TYPE__|__UINT_LEAST32_TYPE__|__UINT_LEAST64_TYPE__|__INT_FAST8_TYPE__|__INT_FAST16_TYPE__|__INT_FAST32_TYPE__|__INT_FAST64_TYPE__|__UINT_FAST8_TYPE__|__UINT_FAST16_TYPE__|__UINT_FAST32_TYPE__|__UINT_FAST64_TYPE__|__INTPTR_TYPE__|__UINTPTR_TYPE__|__CHAR_BIT__|__SCHAR_MAX__|__WCHAR_MAX__|__SHRT_MAX__|__INT_MAX__|__LONG_MAX__|__LONG_LONG_MAX__|__WINT_MAX__|__SIZE_MAX__|__PTRDIFF_MAX__|__INTMAX_MAX__|__UINTMAX_MAX__|__SIG_ATOMIC_MAX__|__INT8_MAX__|__INT16_MAX__|__INT32_MAX__|__INT64_MAX__|__UINT8_MAX__|__UINT16_MAX__|__UINT32_MAX__|__UINT64_MAX__|__INT_LEAST8_MAX__|__INT_LEAST16_MAX__|__INT_LEAST32_MAX__|__INT_LEAST64_MAX__|__UINT_LEAST8_MAX__|__UINT_LEAST16_MAX__|__UINT_LEAST32_MAX__|__UINT_LEAST64_MAX__|__INT_FAST8_MAX__|__INT_FAST16_MAX__|__INT_FAST32_MAX__|__INT_FAST64_MAX__|__UINT_FAST8_MAX__|__UINT_FAST16_MAX__|__UINT_FAST32_MAX__|__UINT_FAST64_MAX__|__INTPTR_MAX__|__UINTPTR_MAX__|__WCHAR_MIN__|__WINT_MIN__|__SIG_ATOMIC_MIN__|__SCHAR_WIDTH__|__SHRT_WIDTH__|__INT_WIDTH__|__LONG_WIDTH__|__LONG_LONG_WIDTH__|__PTRDIFF_WIDTH__|__SIG_ATOMIC_WIDTH__|__SIZE_WIDTH__|__WCHAR_WIDTH__|__WINT_WIDTH__|__INT_LEAST8_WIDTH__|__INT_LEAST16_WIDTH__|__INT_LEAST32_WIDTH__|__INT_LEAST64_WIDTH__|__INT_FAST8_WIDTH__|__INT_FAST16_WIDTH__|__INT_FAST32_WIDTH__|__INT_FAST64_WIDTH__|__INTPTR_WIDTH__|__INTMAX_WIDTH__|__SIZEOF_INT__|__SIZEOF_LONG__|__SIZEOF_LONG_LONG__|__SIZEOF_SHORT__|__SIZEOF_POINTER__|__SIZEOF_FLOAT__|__SIZEOF_DOUBLE__|__SIZEOF_LONG_DOUBLE__|__SIZEOF_SIZE_T__|__SIZEOF_WCHAR_T__|__SIZEOF_WINT_T__|__SIZEOF_PTRDIFF_T__|__BYTE_ORDER__|__ORDER_LITTLE_ENDIAN__|__ORDER_BIG_ENDIAN__|__ORDER_PDP_ENDIAN__|__FLOAT_WORD_ORDER__|__DEPRECATED|__EXCEPTIONS|__GXX_RTTI|__USING_SJLJ_EXCEPTIONS__|__GXX_EXPERIMENTAL_CXX0X__|__GXX_WEAK__|__NEXT_RUNTIME__|__LP64__|_LP64|__SSP__|__SSP_ALL__|__SSP_STRONG__|__SSP_EXPLICIT__|__SANITIZE_ADDRESS__|__SANITIZE_THREAD__|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8|__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16|__HAVE_SPECULATION_SAFE_VALUE|__GCC_HAVE_DWARF2_CFI_ASM|__FP_FAST_FMA|__FP_FAST_FMAF|__FP_FAST_FMAL|__FP_FAST_FMAF16|__FP_FAST_FMAF32|__FP_FAST_FMAF64|__FP_FAST_FMAF128|__FP_FAST_FMAF32X|__FP_FAST_FMAF64X|__FP_FAST_FMAF128X|__GCC_IEC_559|__GCC_IEC_559_COMPLEX|__NO_MATH_ERRNO__|__has_builtin|__has_feature|__has_extension|__has_cpp_attribute|__has_c_attribute|__has_attribute|__has_declspec_attribute|__is_identifier|__has_include|__has_include_next|__has_warning|__BASE_FILE__|__FILE_NAME__|__clang__|__clang_major__|__clang_minor__|__clang_patchlevel__|__clang_version__|__fp16|_Float16)\\b", - "captures": { - "1": { - "name": "entity.name.other.preprocessor.macro.predefined.$1.cpp" - } - } - }, - { - "match": "\\b__([A-Z_]+)__\\b", - "name": "entity.name.other.preprocessor.macro.predefined.probably.$1.cpp" + "include": "source.cpp#line_continuation_character" } ] }, @@ -10142,30 +7775,31 @@ "include": "#comments" }, { - "include": "#language_constants" + "include": "source.cpp#language_constants" }, { - "include": "#string_context_c" + "include": "#string_context" }, { - "include": "#preprocessor_number_literal" + "include": "source.cpp#d9bc4796b0b_preprocessor_number_literal" }, { "include": "#operators" }, { - "include": "#predefined_macros" + "include": "source.cpp#predefined_macros" }, { - "include": "#macro_name" + "include": "source.cpp#macro_name" }, { - "include": "#line_continuation_character" + "include": "source.cpp#line_continuation_character" } ] }, "preprocessor_conditional_defined": { "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:(?:ifndef|ifdef)|if)))", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:(?:ifndef|ifdef)|if))", + "end": "^(?!\\s*+#\\s*(?:else|endif))|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", - "captures": { - "0": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_function_call_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_function_call_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.function.call.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" - } - } - }, - "scope_resolution_function_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.function.definition.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - } - } - }, - "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_operator_overload_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_operator_overload_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.function.definition.operator-overload.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" - } - } - }, - "scope_resolution_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - } - } - }, - "scope_resolution_namespace_alias": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_namespace_alias_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_namespace_alias_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.namespace.alias.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" - } - } - }, - "scope_resolution_namespace_block": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_namespace_block_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_namespace_block_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.namespace.block.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" - } - } - }, - "scope_resolution_namespace_using": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_namespace_using_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_namespace_using_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.namespace.using.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" - } - } - }, - "scope_resolution_parameter": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_parameter_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_parameter_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.parameter.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" - } - } - }, - "scope_resolution_template_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_template_call_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_template_call_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.template.call.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - } - } - }, - "scope_resolution_template_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_template_definition_inner_generated" - } - ] - }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - }, - "3": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", - "captures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_template_definition_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - }, - "4": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "6": { - "name": "entity.name.scope-resolution.template.definition.cpp" - }, - "7": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "9": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - } - } - }, - "semicolon": { - "match": ";", - "name": "punctuation.terminator.statement.cpp" - }, - "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?", - "captures": { - "1": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "single_line_macro": { - "match": "^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))#define.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -12252,134 +8531,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.struct.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.struct.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, "switch_conditional_parentheses": { - "name": "meta.conditional.switch.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?>[^<>]*)\\g<1>?)+)>)\\s*", - "captures": { - "0": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, "template_call_range": { - "name": "meta.template.call.cpp", - "begin": "(<)", + "begin": "<", + "end": ">|(?=(?)|(?=(?|(?=(?)|(?=(?|(?=(?)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "storage.type.template.argument.$5.cpp" - }, - "6": { - "patterns": [ - { - "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", - "name": "storage.type.template.argument.$0.cpp" - } - ] - }, - "7": { - "name": "entity.name.type.template.cpp" - }, - "8": { - "name": "storage.type.template.cpp" - }, - "9": { - "name": "punctuation.vararg-ellipses.template.definition.cpp" - }, - "10": { - "name": "entity.name.type.template.cpp" - }, - "11": { - "name": "punctuation.separator.delimiter.comma.template.argument.cpp" - } - } - }, "template_definition_context": { "patterns": [ { - "include": "#scope_resolution_template_definition_inner_generated" + "include": "source.cpp#scope_resolution_template_definition_inner_generated" }, { - "include": "#template_definition_argument" + "include": "source.cpp#template_definition_argument" }, { - "include": "#template_argument_defaulted" + "include": "source.cpp#template_argument_defaulted" }, { - "include": "#template_call_innards" + "include": "source.cpp#template_call_innards" }, { "include": "#evaluation_context" } ] }, - "template_isolated_definition": { - "match": "(?\\s*$)", - "captures": { - "1": { - "name": "storage.type.template.cpp" - }, - "2": { - "name": "punctuation.section.angle-brackets.start.template.definition.cpp" - }, - "3": { - "name": "meta.template.definition.cpp", - "patterns": [ - { - "include": "#template_definition_context" - } - ] - }, - "4": { - "name": "punctuation.section.angle-brackets.end.template.definition.cpp" - } - } - }, "ternary_operator": { - "applyEndPatternLast": true, - "begin": "(\\?)", + "begin": "\\?", + "end": ":|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", - "captures": { - "1": { - "name": "keyword.other.using.directive.cpp" - }, - "2": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "61": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "62": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "63": { - "name": "comment.block.cpp" - }, - "64": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "65": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "66": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "67": { - "name": "comment.block.cpp" - }, - "68": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "69": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "70": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "71": { - "name": "comment.block.cpp" - }, - "72": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "73": { - "name": "punctuation.definition.begin.bracket.square.cpp" - }, - "74": { - "patterns": [ - { - "include": "#evaluation_context" - } - ] - }, - "75": { - "name": "punctuation.definition.end.bracket.square.cpp" - }, - "76": { - "name": "punctuation.terminator.statement.cpp" - } - }, - "name": "meta.declaration.type.alias.cpp" - }, - "type_casting_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -13759,134 +9200,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -13998,7 +9345,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14023,7 +9370,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14048,7 +9395,7 @@ "10": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14085,47 +9432,67 @@ ] }, "typedef_function_pointer": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14304,111 +9652,110 @@ } ] }, - "29": { + "20": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "entity.name.type.alias.cpp entity.name.type.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -14587,134 +10005,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14826,7 +10150,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14851,7 +10175,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14876,7 +10200,7 @@ "10": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -14913,101 +10237,205 @@ ] }, "typedef_union": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "source.cpp#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -15019,134 +10447,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "source.cpp#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15258,7 +10592,7 @@ "2": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15283,7 +10617,7 @@ "6": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15308,7 +10642,7 @@ "10": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15345,8 +10679,8 @@ ] }, "typeid_operator": { - "contentName": "meta.arguments.operator.typeid.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", - "captures": { + "union_block": { + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } }, { - "include": "#attributes_context" + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "source.cpp#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } }, { - "include": "#function_type" - }, - { - "include": "#storage_types" - }, - { - "include": "#number_literal" - }, - { - "include": "#string_context" - }, - { - "include": "#comma" - }, - { - "include": "#scope_resolution_inner_generated" - }, - { - "include": "#template_call_range" + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", - "name": "entity.name.type.cpp" - } - ] - }, - "11": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" } ] }, "12": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15523,7 +10930,7 @@ "16": { "patterns": [ { - "include": "#inline_comment" + "include": "source.cpp#inline_comment" } ] }, @@ -15545,367 +10952,10 @@ } ] }, - "21": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "22": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "24": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "26": { - "name": "entity.name.scope-resolution.cpp" - }, - "27": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "29": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "entity.name.type.cpp" - }, - "35": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } - } - }, - "undef": { - "match": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*undef\\b)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", - "beginCaptures": { - "1": { - "name": "meta.head.union.cpp" - }, - "3": { - "name": "storage.type.$3.cpp" - }, - "4": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "5": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "6": { - "name": "comment.block.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "11": { - "name": "comment.block.cpp" - }, - "12": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "16": { - "name": "comment.block.cpp" - }, - "17": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "18": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "19": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))|(?=(?|\\?\\?>)|(?=(?|\\?\\?>|(?=(?|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=(?|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", - "captures": { - "1": { - "name": "storage.type.union.declare.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "entity.name.type.union.cpp" - }, - "7": { - "patterns": [ - { - "match": "\\*", - "name": "storage.modifier.pointer.cpp" - }, - { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", - "captures": { - "1": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - }, - "name": "invalid.illegal.reference-type.cpp" - }, - { - "match": "\\&", - "name": "storage.modifier.reference.cpp" - } - ] - }, - "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "10": { - "name": "comment.block.cpp" - }, - "11": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { - "name": "variable.other.object.declare.cpp" - }, - "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - } - } - }, - "using_name": { - "match": "(using)\\s+(?!namespace\\b)", - "captures": { - "1": { - "name": "keyword.other.using.directive.cpp" - } - } - }, "using_namespace": { - "name": "meta.using-namespace.cpp", - "begin": "(?(?:(?>[^<>]*)\\g<7>?)+)>)\\s*)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?:protected|private|public))\\s*(:))", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?:(?:protected)|(?:private)|(?:public)))(?:(?:\\s)+)?(:))", "captures": { "1": { "patterns": [ @@ -105,45 +105,55 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "storage.type.modifier.access.control.$6.cpp" + "3": { + "name": "storage.type.modifier.access.control.$4.cpp" }, - "7": { + "4": {}, + "5": { "name": "punctuation.separator.colon.access.control.cpp" } } }, "alignas_attribute": { - "name": "support.other.attribute.cpp", - "begin": "(alignas\\()", + "begin": "alignas\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.begin.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.end.cpp" } }, + "name": "support.other.attribute.cpp", "patterns": [ { "include": "#attributes_context" @@ -151,6 +161,8 @@ { "begin": "\\(", "end": "\\)", + "beginCaptures": {}, + "endCaptures": {}, "patterns": [ { "include": "#attributes_context" @@ -161,7 +173,7 @@ ] }, { - "match": "(using)\\s+((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignas.cpp" @@ -228,12 +240,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.alignas.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.alignas.cpp" } }, + "contentName": "meta.arguments.operator.alignas", "patterns": [ { "include": "#evaluation_context" @@ -241,8 +253,8 @@ ] }, "alignof_operator": { - "contentName": "meta.arguments.operator.alignof.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignof.cpp" @@ -276,12 +288,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.alignof.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.alignof.cpp" } }, + "contentName": "meta.arguments.operator.alignof", "patterns": [ { "include": "#evaluation_context" @@ -289,96 +301,221 @@ ] }, "assembly": { - "name": "meta.asm.cpp", - "begin": "(\\b(?:__asm__|asm)\\b)\\s*((?:volatile)?)\\s*(\\()", + "begin": "(\\b(?:__asm__|asm)\\b)(?:(?:\\s)+)?((?:volatile)?)", + "end": "(?!\\G)", "beginCaptures": { "1": { "name": "storage.type.asm.cpp" }, "2": { "name": "storage.modifier.cpp" - }, - "3": { - "name": "punctuation.section.parens.begin.bracket.round.assembly.cpp" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "punctuation.section.parens.end.bracket.round.assembly.cpp" } }, + "endCaptures": {}, + "name": "meta.asm.cpp", "patterns": [ { - "name": "string.quoted.double.cpp", - "contentName": "meta.embedded.assembly.cpp", - "begin": "(R?)(\")", - "beginCaptures": { + "match": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\n)|$)", + "captures": { "1": { - "name": "meta.encoding.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "2": { - "name": "punctuation.definition.string.begin.assembly.cpp" - } - }, - "end": "(\")", - "endCaptures": { - "1": { - "name": "punctuation.definition.string.end.assembly.cpp" - } - }, - "patterns": [ - { - "include": "source.asm" + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - { - "include": "source.x86" + "3": { + "name": "comment.block.cpp" }, - { - "include": "source.x86_64" - }, - { - "include": "source.arm" - }, - { - "include": "#backslash_escapes" - }, - { - "include": "#string_escaped_char" - }, - { - "match": "(?=not)possible" + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] } - ] - }, - { - "begin": "(\\()", - "beginCaptures": { - "1": { - "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.cpp" - } - }, - "end": "(\\))", - "endCaptures": { - "1": { - "name": "punctuation.section.parens.end.bracket.round.assembly.inner.cpp" - } - }, - "patterns": [ - { - "include": "#evaluation_context" - } - ] - }, - { - "match": ":", - "name": "punctuation.separator.delimiter.colon.assembly.cpp" + } }, { "include": "#comments_context" }, { "include": "#comments" + }, + { + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\(", + "end": "\\)", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.cpp" + }, + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.assembly.cpp" + } + }, + "patterns": [ + { + "begin": "(R?)(\")", + "end": "\"", + "beginCaptures": { + "1": { + "name": "meta.encoding.cpp" + }, + "2": { + "name": "punctuation.definition.string.begin.assembly.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.assembly.cpp" + } + }, + "name": "string.quoted.double.cpp", + "contentName": "meta.embedded.assembly", + "patterns": [ + { + "include": "source.asm" + }, + { + "include": "source.x86" + }, + { + "include": "source.x86_64" + }, + { + "include": "source.arm" + }, + { + "include": "#backslash_escapes" + }, + { + "include": "#string_escaped_char" + } + ] + }, + { + "begin": "\\(", + "end": "\\)", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.assembly.inner.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.assembly.inner.cpp" + } + }, + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + { + "match": "\\[((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "5": { + "name": "variable.other.asm.label.cpp" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": ":", + "name": "punctuation.separator.delimiter.colon.assembly.cpp" + }, + { + "include": "#comments_context" + }, + { + "include": "#comments" + } + ] } ] }, @@ -403,23 +540,23 @@ ] }, "backslash_escapes": { - "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3]\\d{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", - "name": "constant.character.escape.cpp" + "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3][0-7]{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", + "name": "constant.character.escape" }, "block": { - "name": "meta.block.cpp", - "begin": "({)", + "begin": "{", + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.cpp" } }, - "end": "(}|(?=\\s*#\\s*(?:elif|else|endif)\\b))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.cpp" } }, + "name": "meta.block.cpp", "patterns": [ { "include": "#function_body_context" @@ -427,192 +564,42 @@ ] }, "block_comment": { - "name": "comment.block.cpp", "begin": "\\s*+(\\/\\*)", + "end": "\\*\\/", "beginCaptures": { "1": { "name": "punctuation.definition.comment.begin.cpp" } }, - "end": "(\\*\\/)", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.comment.end.cpp" } - } + }, + "name": "comment.block.cpp" }, "builtin_storage_type_initilizer": { - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(?:\\s)*+(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.class.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -768,134 +858,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -904,16 +895,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.class.cpp", "patterns": [ { - "name": "meta.head.class.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.class.cpp" } }, + "name": "meta.head.class.cpp", "patterns": [ { "include": "#ever_present_context" @@ -927,14 +920,15 @@ ] }, { - "name": "meta.body.class.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.class.cpp" } }, + "name": "meta.body.class.cpp", "patterns": [ { "include": "#function_pointer" @@ -954,9 +948,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.class.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -966,7 +962,7 @@ ] }, "class_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.class.declare.cpp" @@ -979,34 +975,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.class.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -1042,6 +1047,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -1050,98 +1089,100 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } @@ -1158,14 +1199,15 @@ "comments": { "patterns": [ { - "name": "comment.line.double-slash.documentation.cpp", - "begin": "(?:^)(?>\\s*)(\\/\\/[!\\/]+)", + "begin": "^(?:(?:\\s)+)?+(\\/\\/[!\\/]+)", + "end": "(?<=\\n)(?\\s*)\\/\\*[!*]+(?:(?:\\n|$)|(?=\\s)))", + "begin": "(?:(?:\\s)+)?+\\/\\*[!*]+(?:(?:(?:\\n)|$)|(?=\\s))", + "end": "[!*]*\\*\\/", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.comment.begin.documentation.cpp" } }, - "end": "([!*]*\\*\\/)", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.comment.end.documentation.cpp" } }, + "name": "comment.block.documentation.cpp", "patterns": [ { "match": "(?<=[\\s*!\\/])[\\\\@](?:callergraph|callgraph|else|endif|f\\$|f\\[|f\\]|hidecallergraph|hidecallgraph|hiderefby|hiderefs|hideinitializer|htmlinclude|n|nosubgrouping|private|privatesection|protected|protectedsection|public|publicsection|pure|showinitializer|showrefby|showrefs|tableofcontents|\\$|\\#|<|>|%|\"|\\.|=|::|\\||\\-\\-|\\-\\-\\-)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:a|em|e))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1355,7 +1413,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@]b)\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@]b)(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1366,7 +1424,7 @@ } }, { - "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))\\s+(\\S+)", + "match": "((?<=[\\s*!\\/])[\\\\@](?:c|p))(?:\\s)+(\\S+)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" @@ -1385,22 +1443,30 @@ "name": "storage.type.class.doxygen.cpp" }, { - "match": "((?<=[\\s*!\\/])[\\\\@]param)\\s+(\\b\\w+\\b)", + "match": "((?<=[\\s*!\\/])[\\\\@]param)(?:\\s*\\[((?:,?(?:(?:\\s)+)?(?:in|out)(?:(?:\\s)+)?)+)\\])?(?:\\s)+(\\b\\w+\\b)", "captures": { "1": { "name": "storage.type.class.doxygen.cpp" }, "2": { + "patterns": [ + { + "match": "in|out", + "name": "keyword.other.parameter.direction.$0.cpp" + } + ] + }, + "3": { "name": "variable.parameter.cpp" } } }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:arg|attention|author|authors|brief|bug|copyright|date|deprecated|details|exception|invariant|li|note|par|paragraph|param|post|pre|remark|remarks|result|return|returns|retval|sa|see|short|since|test|throw|throws|todo|tparam|version|warning|xrefitem)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { - "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|uml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", + "match": "(?<=[\\s*!\\/])[\\\\@](?:code|cond|docbookonly|dot|htmlonly|internal|latexonly|link|manonly|msc|parblock|rtfonly|secreflist|startuml|verbatim|xmlonly|endcode|endcond|enddocbookonly|enddot|endhtmlonly|endinternal|endlatexonly|endlink|endmanonly|endmsc|endparblock|endrtfonly|endsecreflist|enduml|endverbatim|endxmlonly)\\b(?:\\{[^}]*\\})?", "name": "storage.type.class.doxygen.cpp" }, { @@ -1424,26 +1490,26 @@ ] }, "constructor_inline": { - "name": "meta.function.definition.special.constructor.cpp", - "begin": "(^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.constructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -1455,52 +1521,52 @@ } ] }, + "5": { + "patterns": [ + { + "include": "#functional_specifiers_pre_parameters" + } + ] + }, "6": { "patterns": [ { - "include": "#functional_specifiers_pre_parameters" + "include": "#inline_comment" } ] }, "7": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "8": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "10": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { "name": "comment.block.cpp" }, - "14": { + "13": { "patterns": [ { "match": "\\*\\/", @@ -1512,23 +1578,23 @@ } ] }, - "15": { + "14": { "name": "storage.type.modifier.calling-convention.cpp" }, - "16": { + "15": { "patterns": [ { "include": "#inline_comment" } ] }, - "17": { + "16": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "18": { + "17": { "name": "comment.block.cpp" }, - "19": { + "18": { "patterns": [ { "match": "\\*\\/", @@ -1540,83 +1606,82 @@ } ] }, - "20": { + "19": { "name": "entity.name.function.constructor.cpp entity.name.function.definition.special.constructor.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.constructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.constructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.head.function.definition.special.constructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { "include": "#functional_specifiers_pre_parameters" }, { - "begin": "(:)", + "begin": ":", + "end": "(?=\\{)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.separator.initializers.cpp" } }, - "end": "(?=\\{)", + "endCaptures": {}, "patterns": [ { - "contentName": "meta.parameter.initialization.cpp", - "begin": "((?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "entity.name.function.call.initializer.cpp" @@ -1629,16 +1694,17 @@ } ] }, + "3": {}, "4": { "name": "punctuation.section.arguments.begin.bracket.round.function.call.initializer.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.call.initializer.cpp" } }, + "contentName": "meta.parameter.initialization", "patterns": [ { "include": "#evaluation_context" @@ -1646,8 +1712,8 @@ ] }, { - "contentName": "meta.parameter.initialization.cpp", "begin": "((?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.body.function.definition.special.constructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -1720,9 +1796,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.constructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -1732,26 +1810,26 @@ ] }, "constructor_root": { - "name": "meta.function.definition.special.constructor.cpp", - "begin": "(\\s*+((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.constructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -1763,23 +1841,23 @@ } ] }, - "6": { + "5": { "name": "storage.type.modifier.calling-convention.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, - "10": { + "9": { "patterns": [ { "match": "\\*\\/", @@ -1791,7 +1869,7 @@ } ] }, - "11": { + "10": { "patterns": [ { "match": "::", @@ -1806,15 +1884,15 @@ } ] }, - "13": { - "name": "meta.template.call.cpp", + "11": { "patterns": [ { "include": "#template_call_range" } ] }, - "15": { + "12": {}, + "13": { "patterns": [ { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?=:)", @@ -1830,45 +1908,46 @@ } ] }, - "17": { + "14": {}, + "15": { "patterns": [ { "include": "#inline_comment" } ] }, + "16": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "17": { + "name": "comment.block.cpp" + }, "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "19": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { "name": "comment.block.cpp" }, - "24": { + "22": { "patterns": [ { "match": "\\*\\/", @@ -1880,20 +1959,20 @@ } ] }, - "25": { + "23": { "patterns": [ { "include": "#inline_comment" } ] }, - "26": { + "24": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "27": { + "25": { "name": "comment.block.cpp" }, - "28": { + "26": { "patterns": [ { "match": "\\*\\/", @@ -1906,79 +1985,78 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.constructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.constructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.head.function.definition.special.constructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { "include": "#functional_specifiers_pre_parameters" }, { - "begin": "(:)", + "begin": ":", + "end": "(?=\\{)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.separator.initializers.cpp" } }, - "end": "(?=\\{)", + "endCaptures": {}, "patterns": [ { - "contentName": "meta.parameter.initialization.cpp", - "begin": "((?(?:(?>[^<>]*)\\g<3>?)+)>)\\s*)?(\\()", + "begin": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "entity.name.function.call.initializer.cpp" @@ -1991,16 +2069,17 @@ } ] }, + "3": {}, "4": { "name": "punctuation.section.arguments.begin.bracket.round.function.call.initializer.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.call.initializer.cpp" } }, + "contentName": "meta.parameter.initialization", "patterns": [ { "include": "#evaluation_context" @@ -2008,8 +2087,8 @@ ] }, { - "contentName": "meta.parameter.initialization.cpp", "begin": "((?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.constructor.cpp" } }, + "name": "meta.body.function.definition.special.constructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -2082,9 +2171,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.constructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -2094,7 +2185,7 @@ ] }, "control_flow_keywords": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.control.$5.cpp" + "3": { + "name": "keyword.control.$3.cpp" } } }, "cpp_attributes": { - "name": "support.other.attribute.cpp", - "begin": "(\\[\\[)", + "begin": "\\[\\[", + "end": "\\]\\]", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.begin.cpp" } }, - "end": "(\\]\\])", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.end.cpp" } }, + "name": "support.other.attribute.cpp", "patterns": [ { "include": "#attributes_context" @@ -2147,6 +2247,8 @@ { "begin": "\\(", "end": "\\)", + "beginCaptures": {}, + "endCaptures": {}, "patterns": [ { "include": "#attributes_context" @@ -2157,7 +2259,7 @@ ] }, { - "match": "(using)\\s+((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "end": "\\}", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -2289,109 +2409,90 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((import))(?:(?:\\s)+)?(?:(?:(?:((<)[^>]*(>?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/)))|((\\\")[^\\\"]*((?:\\\")?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/))))|(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;)))))|((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;))))(?:(?:\\s)+)?(;?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "3": { + "name": "keyword.control.directive.import.cpp" + }, + "5": { + "name": "string.quoted.other.lt-gt.include.cpp" + }, + "6": { + "name": "punctuation.definition.string.begin.cpp" + }, + "7": { + "name": "punctuation.definition.string.end.cpp" + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "name": "string.quoted.double.include.cpp" + }, + "11": { + "name": "punctuation.definition.string.begin.cpp" + }, + "12": { + "name": "punctuation.definition.string.end.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "15": { + "name": "entity.name.other.preprocessor.macro.include.cpp" + }, + "16": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "17": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "18": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "19": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "20": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "21": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "22": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "name": "meta.preprocessor.import.cpp" + }, + "d9bc4796b0b_preprocessor_number_literal": { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2437,12 +3030,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.decltype.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.decltype.cpp" } }, + "contentName": "meta.arguments.decltype", "patterns": [ { "include": "#evaluation_context" @@ -2450,8 +3043,8 @@ ] }, "decltype_specifier": { - "contentName": "meta.arguments.decltype.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2485,12 +3078,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.decltype.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.decltype.cpp" } }, + "contentName": "meta.arguments.decltype", "patterns": [ { "include": "#evaluation_context" @@ -2498,8 +3091,8 @@ ] }, "default_statement": { - "name": "meta.conditional.case.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(~(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:constexpr)|(?:explicit)|(?:mutable)|(?:virtual)|(?:inline)|(?:friend))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(~(?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.member.destructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "6": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { "name": "comment.block.cpp" }, - "9": { + "8": { "patterns": [ { "match": "\\*\\/", @@ -2602,23 +3195,23 @@ } ] }, - "10": { + "9": { "name": "storage.type.modifier.calling-convention.cpp" }, - "11": { + "10": { "patterns": [ { "include": "#inline_comment" } ] }, - "12": { + "11": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "13": { + "12": { "name": "comment.block.cpp" }, - "14": { + "13": { "patterns": [ { "match": "\\*\\/", @@ -2630,27 +3223,27 @@ } ] }, - "15": { + "14": { "patterns": [ { "include": "#functional_specifiers_pre_parameters" } ] }, - "16": { + "15": { "patterns": [ { "include": "#inline_comment" } ] }, - "17": { + "16": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "18": { + "17": { "name": "comment.block.cpp" }, - "19": { + "18": { "patterns": [ { "match": "\\*\\/", @@ -2662,81 +3255,88 @@ } ] }, - "20": { + "19": { "name": "entity.name.function.destructor.cpp entity.name.function.definition.special.member.destructor.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.member.destructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.member.destructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.head.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.special.member.destructor.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.special.member.destructor.cpp" } + }, + "contentName": "meta.function.definition.parameters.special.member.destructor", + "patterns": [] + }, + { + "match": "((?:(?:final)|(?:override)))+", + "captures": { + "1": { + "name": "keyword.operator.wordlike.cpp keyword.operator.$1.cpp" + } } }, { @@ -2745,14 +3345,15 @@ ] }, { - "name": "meta.body.function.definition.special.member.destructor.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.body.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -2760,9 +3361,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.member.destructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -2772,26 +3375,26 @@ ] }, "destructor_root": { - "name": "meta.function.definition.special.member.destructor.cpp", - "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))~\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.member.destructor.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -2803,23 +3406,23 @@ } ] }, - "6": { + "5": { "name": "storage.type.modifier.calling-convention.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, - "10": { + "9": { "patterns": [ { "match": "\\*\\/", @@ -2831,7 +3434,7 @@ } ] }, - "11": { + "10": { "patterns": [ { "match": "::", @@ -2846,15 +3449,15 @@ } ] }, - "13": { - "name": "meta.template.call.cpp", + "11": { "patterns": [ { "include": "#template_call_range" } ] }, - "15": { + "12": {}, + "13": { "patterns": [ { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?=:)", @@ -2870,45 +3473,46 @@ } ] }, - "17": { + "14": {}, + "15": { "patterns": [ { "include": "#inline_comment" } ] }, + "16": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "17": { + "name": "comment.block.cpp" + }, "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "19": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "21": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { "name": "comment.block.cpp" }, - "24": { + "22": { "patterns": [ { "match": "\\*\\/", @@ -2920,20 +3524,20 @@ } ] }, - "25": { + "23": { "patterns": [ { "include": "#inline_comment" } ] }, - "26": { + "24": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "27": { + "25": { "name": "comment.block.cpp" }, - "28": { + "26": { "patterns": [ { "match": "\\*\\/", @@ -2946,77 +3550,84 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.member.destructor.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.member.destructor.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.head.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "patterns": [ - { - "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", - "captures": { - "1": { - "name": "keyword.operator.assignment.cpp" - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "6": { - "name": "keyword.other.default.constructor.cpp" - }, - "7": { - "name": "keyword.other.delete.constructor.cpp" + "match": "(\\=)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(default)|(delete))", + "captures": { + "1": { + "name": "keyword.operator.assignment.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" } - } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "keyword.other.default.constructor.cpp" + }, + "7": { + "name": "keyword.other.delete.constructor.cpp" } - ] + } }, { - "contentName": "meta.function.definition.parameters.special.member.destructor.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.special.member.destructor.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.special.member.destructor.cpp" } + }, + "contentName": "meta.function.definition.parameters.special.member.destructor", + "patterns": [] + }, + { + "match": "((?:(?:final)|(?:override)))+", + "captures": { + "1": { + "name": "keyword.operator.wordlike.cpp keyword.operator.$1.cpp" + } } }, { @@ -3025,14 +3636,15 @@ ] }, { - "name": "meta.body.function.definition.special.member.destructor.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.member.destructor.cpp" } }, + "name": "meta.body.function.definition.special.member.destructor.cpp", "patterns": [ { "include": "#function_body_context" @@ -3040,9 +3652,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.member.destructor.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -3052,8 +3666,8 @@ ] }, "diagnostic": { - "name": "meta.preprocessor.diagnostic.$reference(directive).cpp", - "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:error|warning)))\\b\\s*", + "begin": "(^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", + "end": "(?[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*(?:\\n|$)))|(^\\s*((\\/\\*)\\s*?((?>[#;\\/=*C~]+)(?![#;\\/=*C~]))\\s*.+\\s*\\8\\s*\\*\\/)))", + "match": "(?:(^(?:(?:\\s)+)?((\\/\\/)(?:(?:\\s)+)?((?:[#;\\/=*C~]+)++(?![#;\\/=*C~]))(?:(?:\\s)+)?.+(?:(?:\\s)+)?\\4(?:(?:\\s)+)?(?:\\n|$)))|(^(?:(?:\\s)+)?((\\/\\*)(?:(?:\\s)+)?((?:[#;\\/=*C~]+)++(?![#;\\/=*C~]))(?:(?:\\s)+)?.+(?:(?:\\s)+)?\\8(?:(?:\\s)+)?\\*\\/)))", "captures": { "1": { "name": "meta.toc-list.banner.double-slash.cpp" @@ -3174,23 +3792,23 @@ } }, "empty_square_brackets": { - "name": "storage.modifier.array.bracket.square.cpp", - "match": "(?-mix:(?-mix:(?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?(::))?\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.enum.cpp" }, - "2": { + "1": { "name": "storage.type.enum.cpp" }, - "3": { - "name": "storage.type.enum.enum-key.$3.cpp" + "2": { + "name": "storage.type.enum.enum-key.$2.cpp" }, - "4": { + "3": { "patterns": [ { "include": "#attributes_context" @@ -3200,22 +3818,33 @@ } ] }, - "5": { + "4": { "name": "entity.name.type.enum.cpp" }, - "6": { + "5": { "name": "punctuation.separator.colon.type-specifier.cpp" }, - "8": { + "6": { "patterns": [ { "include": "#scope_resolution_inner_generated" } ] }, - "9": { + "7": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, + "8": { + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "9": {}, + "10": { + "name": "entity.name.scope-resolution.cpp" + }, "11": { "name": "meta.template.call.cpp", "patterns": [ @@ -3224,25 +3853,14 @@ } ] }, + "12": {}, "13": { - "name": "entity.name.scope-resolution.cpp" - }, - "14": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "16": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, - "17": { - "name": "storage.type.integral.$17.cpp" + "14": { + "name": "storage.type.integral.$14.cpp" } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -3251,16 +3869,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.enum.cpp", "patterns": [ { - "name": "meta.head.enum.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.enum.cpp" } }, + "name": "meta.head.enum.cpp", "patterns": [ { "include": "$self" @@ -3268,14 +3888,15 @@ ] }, { - "name": "meta.body.enum.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.enum.cpp" } }, + "name": "meta.body.enum.cpp", "patterns": [ { "include": "#ever_present_context" @@ -3295,9 +3916,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.enum.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -3307,7 +3930,7 @@ ] }, "enum_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.enum.declare.cpp" @@ -3320,34 +3943,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.enum.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -3383,6 +4015,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -3391,105 +4057,107 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "enumerator_list": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.control.exception.$5.cpp" + "3": { + "name": "keyword.control.exception.$3.cpp" } } }, "extern_block": { - "name": "meta.block.extern.cpp", - "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(extern)(?=\\s*\\\"))", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(extern)(?=\\s*\\\")", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.extern.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -3726,11 +4394,10 @@ } ] }, - "6": { + "5": { "name": "storage.type.extern.cpp" } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -3739,16 +4406,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.extern.cpp", "patterns": [ { - "name": "meta.head.extern.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.extern.cpp" } }, + "name": "meta.head.extern.cpp", "patterns": [ { "include": "$self" @@ -3756,14 +4425,15 @@ ] }, { - "name": "meta.body.extern.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.extern.cpp" } }, + "name": "meta.body.extern.cpp", "patterns": [ { "include": "$self" @@ -3771,9 +4441,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.extern.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -3812,7 +4484,7 @@ "include": "#typedef_union" }, { - "include": "#typedef_keyword" + "include": "#misc_keywords" }, { "include": "#standard_declares" @@ -3859,7 +4531,8 @@ ] }, "function_call": { - "begin": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -3871,31 +4544,31 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.function.call.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, - "10": { + "9": { "patterns": [ { "match": "\\*\\/", @@ -3907,7 +4580,7 @@ } ] }, - "11": { + "10": { "name": "meta.template.call.cpp", "patterns": [ { @@ -3915,13 +4588,13 @@ } ] }, - "13": { + "11": {}, + "12": { "name": "punctuation.section.arguments.begin.bracket.round.function.call.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.call.cpp" } }, @@ -3932,26 +4605,26 @@ ] }, "function_definition": { - "name": "meta.function.definition.cpp", - "begin": "((?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\())", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -3963,38 +4636,48 @@ } ] }, - "6": { + "5": { "name": "storage.type.template.cpp" }, - "7": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "9": { + "8": { "name": "comment.block.cpp" }, + "9": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "10": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + "include": "#attributes_context" }, { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#number_literal" } ] }, "11": { "patterns": [ { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))", + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", "captures": { "1": { "name": "storage.modifier.$1.cpp" @@ -4029,7 +4712,7 @@ ] }, "12": { - "name": "storage.modifier.$1.cpp" + "name": "storage.modifier.$12.cpp" }, "13": { "patterns": [ @@ -4060,15 +4743,16 @@ "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4153,52 +4854,43 @@ } ] }, + "27": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4264,45 +4946,95 @@ } ] }, - "45": { + "36": { "patterns": [ { "include": "#inline_comment" } ] }, - "46": { + "37": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "47": { + "38": { "name": "comment.block.cpp" }, + "39": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "40": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "41": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "42": { + "name": "comment.block.cpp" + }, + "43": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "44": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "45": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "46": { + "name": "comment.block.cpp" + }, + "47": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "48": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "49": { "patterns": [ { "include": "#inline_comment" } ] }, - "50": { + "49": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "51": { + "50": { "name": "comment.block.cpp" }, - "52": { + "51": { "patterns": [ { "match": "\\*\\/", @@ -4314,6 +5046,9 @@ } ] }, + "52": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "53": { "patterns": [ { @@ -4342,30 +5077,23 @@ "57": { "patterns": [ { - "include": "#inline_comment" + "include": "#scope_resolution_function_definition_inner_generated" } ] }, "58": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, "59": { - "name": "comment.block.cpp" - }, - "60": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#template_call_range" } ] }, + "60": {}, "61": { - "name": "storage.type.modifier.calling-convention.cpp" + "name": "entity.name.function.definition.cpp" }, "62": { "patterns": [ @@ -4391,83 +5119,39 @@ "name": "comment.block.cpp" } ] - }, - "66": { - "patterns": [ - { - "include": "#scope_resolution_function_definition_inner_generated" - } - ] - }, - "67": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" - }, - "69": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "71": { - "name": "entity.name.function.definition.cpp" - }, - "72": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "73": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "74": { - "name": "comment.block.cpp" - }, - "75": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.cpp", "patterns": [ { - "name": "meta.head.function.definition.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.cpp" } }, + "name": "meta.head.function.definition.cpp", "patterns": [ { "include": "#ever_present_context" }, { - "contentName": "meta.function.definition.parameters.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.cpp" } }, + "contentName": "meta.function.definition.parameters", "patterns": [ { "include": "#ever_present_context" @@ -4483,20 +5167,218 @@ } ] }, + { + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "captures": { + "1": { + "name": "punctuation.definition.function.return-type.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "7": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "10": { + "name": "comment.block.cpp" + }, + "11": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "14": { + "name": "comment.block.cpp" + }, + "15": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "16": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.cpp" } }, + "name": "meta.body.function.definition.cpp", "patterns": [ { "include": "#function_body_context" @@ -4504,9 +5386,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -4529,21 +5413,23 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4628,52 +5531,43 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4739,111 +5623,110 @@ } ] }, - "29": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.other.definition.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -4881,21 +5764,23 @@ ] }, "function_pointer_parameter": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4980,52 +5882,43 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -5091,111 +5974,110 @@ } ] }, - "29": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "variable.parameter.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -5233,23 +6115,23 @@ ] }, "functional_specifiers_pre_parameters": { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", "captures": { "1": { "name": "keyword.control.goto.cpp" @@ -5312,30 +6196,42 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.label.call.cpp" } } }, + "identifier": { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*" + }, "include": { - "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", + "match": "^((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((#)(?:(?:\\s)+)?((?:include|include_next))\\b)(?:(?:\\s)+)?(?:(?:(?:((<)[^>]*(>?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/)))|((\\\")[^\\\"]*((?:\\\")?)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=\\/\\/))))|(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;)))))|((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:\\n)|$)|(?=(?:\\/\\/|;))))", "captures": { "1": { "patterns": [ @@ -5345,172 +6241,226 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.control.directive.$7.cpp" + "3": { + "name": "keyword.control.directive.$5.cpp" }, - "6": { + "4": { "name": "punctuation.definition.directive.cpp" }, - "8": { + "6": { "name": "string.quoted.other.lt-gt.include.cpp" }, - "9": { + "7": { "name": "punctuation.definition.string.begin.cpp" }, - "10": { + "8": { "name": "punctuation.definition.string.end.cpp" }, - "11": { + "9": { "patterns": [ { "include": "#inline_comment" } ] }, + "10": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "11": { + "name": "string.quoted.double.include.cpp" + }, "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "punctuation.definition.string.begin.cpp" }, "13": { - "name": "comment.block.cpp" + "name": "punctuation.definition.string.end.cpp" }, "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "15": { - "name": "string.quoted.double.include.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "16": { - "name": "punctuation.definition.string.begin.cpp" + "name": "entity.name.other.preprocessor.macro.include.cpp" }, "17": { - "name": "punctuation.definition.string.end.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "19": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "20": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "21": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "22": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "23": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "24": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "25": { - "name": "comment.block.cpp" - }, - "26": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "27": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "28": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "29": { - "name": "comment.block.cpp" - }, - "30": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "31": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "32": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "33": { - "name": "comment.block.cpp" - }, - "34": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } @@ -5527,7 +6477,7 @@ "name": "punctuation.separator.delimiter.comma.inheritance.cpp" }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -5592,122 +6560,147 @@ ] }, "4": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "5": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "7": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "25": { - "name": "entity.name.type.cpp" - }, - "26": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } + "12": {} } } ] }, + "inline_builtin_storage_type": { + "match": "(?:\\s)*+(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/))", + "match": "(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/))", "captures": { "1": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" @@ -5734,7 +6727,7 @@ "name": "invalid.illegal.unexpected.punctuation.definition.comment.end.cpp" }, "label": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:)", "captures": { "1": { "patterns": [ @@ -5744,70 +6737,89 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "entity.name.label.cpp" }, - "6": { + "4": { "patterns": [ { "include": "#inline_comment" } ] }, - "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { - "name": "comment.block.cpp" - }, - "9": { + "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "10": { + "6": { "name": "punctuation.separator.label.cpp" } } }, "lambdas": { - "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[| *+\"| *+\\d))((?>(?:[^\\[\\]]|((?(?:(?>[^\\[\\]]*)\\g<4>?)+)\\]))*))(\\](?!\\[)))", + "begin": "(?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))(?:(?:\\s)+)?(\\[(?!\\[| *+\"| *+\\d))((?:[^\\[\\]]|((??)++\\]))*+)(\\](?!((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))[\\[\\];]))", + "end": "(?<=[;}])", "beginCaptures": { - "2": { + "1": { "name": "punctuation.definition.capture.begin.lambda.cpp" }, - "3": { + "2": { "name": "meta.lambda.capture.cpp", "patterns": [ { "include": "#the_this_keyword" }, { - "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", + "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", "captures": { "1": { "name": "variable.parameter.capture.cpp" @@ -5850,26 +6862,52 @@ } ] }, - "5": { + "3": {}, + "4": { "name": "punctuation.definition.capture.end.lambda.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "7": { + "name": "comment.block.cpp" + }, + "8": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] } }, - "end": "(?<=})", + "endCaptures": {}, "patterns": [ { - "name": "meta.function.definition.parameters.lambda.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.parameters.begin.lambda.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.parameters.end.lambda.cpp" } }, + "name": "meta.function.definition.parameters.lambda.cpp", "patterns": [ { "include": "#function_parameter_context" @@ -5877,7 +6915,7 @@ ] }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*line\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*define\\b)\\s*((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_least16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_least64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_least8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_least8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint_fast8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int_fast8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|suseconds_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|useconds_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|in_addr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|in_port_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uintptr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|blksize_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_quad_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|intmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|intmax_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|unsigned[^Pattern.new(\n match: \\/\\w\\/,\n)]|blkcnt_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|intptr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|swblk_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|wchar_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_short[^Pattern.new(\n match: \\/\\w\\/,\n)]|qaddr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|caddr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|daddr_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|fixpt_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|nlink_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|segsz_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|clock_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|ssize_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int16_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int32_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int64_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int8_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|mode_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|quad_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|ushort[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_long[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_char[^Pattern.new(\n match: \\/\\w\\/,\n)]|double[^Pattern.new(\n match: \\/\\w\\/,\n)]|signed[^Pattern.new(\n match: \\/\\w\\/,\n)]|time_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|size_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|key_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|div_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|ino_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uid_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|gid_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|off_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|pid_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|float[^Pattern.new(\n match: \\/\\w\\/,\n)]|dev_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|u_int[^Pattern.new(\n match: \\/\\w\\/,\n)]|short[^Pattern.new(\n match: \\/\\w\\/,\n)]|bool[^Pattern.new(\n match: \\/\\w\\/,\n)]|id_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|uint[^Pattern.new(\n match: \\/\\w\\/,\n)]|long[^Pattern.new(\n match: \\/\\w\\/,\n)]|char[^Pattern.new(\n match: \\/\\w\\/,\n)]|void[^Pattern.new(\n match: \\/\\w\\/,\n)]|auto[^Pattern.new(\n match: \\/\\w\\/,\n)]|id_t[^Pattern.new(\n match: \\/\\w\\/,\n)]|int[^Pattern.new(\n match: \\/\\w\\/,\n)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -6092,39 +7132,48 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "variable.language.this.cpp" }, - "6": { + "4": { "name": "variable.other.object.access.cpp" }, - "7": { + "5": { "name": "punctuation.separator.dot-access.cpp" }, - "8": { + "6": { "name": "punctuation.separator.pointer-access.cpp" }, - "9": { + "7": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:(?:\\s)+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6166,7 +7215,7 @@ } }, { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6215,13 +7264,13 @@ } ] }, - "10": { + "8": { "name": "variable.other.property.cpp" } } }, "memory_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(delete)(?:(?:\\s)+)?(\\[\\])|(delete))|(new))(?!\\w))", "captures": { "1": { "patterns": [ @@ -6231,42 +7280,52 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "keyword.operator.wordlike.cpp" }, - "6": { + "4": { "name": "keyword.operator.delete.array.cpp" }, - "7": { + "5": { "name": "keyword.operator.delete.array.bracket.cpp" }, - "8": { + "6": { "name": "keyword.operator.delete.cpp" }, - "9": { + "7": { "name": "keyword.operator.new.cpp" } } }, "method_access": { - "begin": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", + "begin": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -6308,7 +7367,7 @@ "9": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:(?:\\s)+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6350,7 +7409,7 @@ } }, { - "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", + "match": "(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6406,9 +7465,8 @@ "name": "punctuation.section.arguments.begin.bracket.round.function.member.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.function.member.cpp" } }, @@ -6418,12 +7476,8 @@ } ] }, - "misc_storage_modifiers": { - "match": "\\b(?:export|mutable|typename|thread_local|register|restrict|static|volatile|inline)\\b", - "name": "storage.modifier.$0.cpp" - }, - "module_import": { - "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", + "misc_keywords": { + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "3": { - "name": "comment.block.cpp" - }, - "4": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "5": { - "name": "keyword.control.directive.import.cpp" - }, - "7": { - "name": "string.quoted.other.lt-gt.include.cpp" - }, - "8": { - "name": "punctuation.definition.string.begin.cpp" - }, - "9": { - "name": "punctuation.definition.string.end.cpp" - }, - "10": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "11": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "12": { - "name": "comment.block.cpp" - }, - "13": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "14": { - "name": "string.quoted.double.include.cpp" - }, - "15": { - "name": "punctuation.definition.string.begin.cpp" - }, - "16": { - "name": "punctuation.definition.string.end.cpp" - }, - "17": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "18": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "19": { - "name": "comment.block.cpp" - }, - "20": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "21": { - "name": "entity.name.other.preprocessor.macro.include.cpp" - }, - "22": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "23": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "24": { - "name": "comment.block.cpp" - }, - "25": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "26": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "27": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "28": { - "name": "comment.block.cpp" - }, - "29": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "punctuation.terminator.statement.cpp" + "name": "keyword.other.$3.cpp" } - }, - "name": "meta.preprocessor.import.cpp" + } }, "ms_attributes": { - "name": "support.other.attribute.cpp", - "begin": "(__declspec\\()", + "begin": "__declspec\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.begin.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.attribute.end.cpp" } }, + "name": "support.other.attribute.cpp", "patterns": [ { "include": "#attributes_context" @@ -6626,6 +7539,8 @@ { "begin": "\\(", "end": "\\)", + "beginCaptures": {}, + "endCaptures": {}, "patterns": [ { "include": "#attributes_context" @@ -6636,7 +7551,7 @@ ] }, { - "match": "(using)\\s+((?(?:(?>[^<>]*)\\g<9>?)+)>)\\s*)?::)*\\s*+)\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.namespace.cpp" }, - "2": { + "1": { "name": "keyword.other.namespace.definition.cpp storage.type.namespace.definition.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.block.namespace.cpp", "patterns": [ { - "name": "meta.head.namespace.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.namespace.cpp" } }, + "name": "meta.head.namespace.cpp", "patterns": [ { "include": "#ever_present_context" @@ -6739,7 +7655,7 @@ "include": "#attributes_context" }, { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<5>?)+)>)\\s*)?::)*\\s*+)\\s*((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.namespace.cpp" } }, + "name": "meta.body.namespace.cpp", "patterns": [ { "include": "$self" @@ -6788,9 +7705,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.namespace.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -6800,8 +7719,8 @@ ] }, "noexcept_operator": { - "contentName": "meta.arguments.operator.noexcept.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.noexcept.cpp" @@ -6835,51 +7754,18 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.noexcept.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.noexcept.cpp" } }, + "contentName": "meta.arguments.operator.noexcept", "patterns": [ { "include": "#evaluation_context" } ] }, - "non_primitive_types": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:\\[\\])?)))|(\"\")((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\<|\\())", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.function.definition.special.operator-overload.cpp" }, - "2": { + "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -7195,7 +8101,7 @@ } ] }, - "3": { + "2": { "patterns": [ { "include": "#attributes_context" @@ -7205,102 +8111,93 @@ } ] }, - "4": { + "3": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "4": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "5": { "name": "comment.block.cpp" }, + "6": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "9": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "10": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" }, { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7366,20 +8253,20 @@ } ] }, - "30": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, - "31": { + "21": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "32": { + "22": { "name": "comment.block.cpp" }, - "33": { + "23": { "patterns": [ { "match": "\\*\\/", @@ -7391,135 +8278,135 @@ } ] }, - "34": { + "24": { "patterns": [ { "include": "#inline_comment" } ] }, - "35": { + "25": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "36": { + "26": { "name": "comment.block.cpp" }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "30": { + "name": "comment.block.cpp" + }, + "31": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "32": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "33": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "34": { + "name": "comment.block.cpp" + }, + "35": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "36": { + "name": "storage.type.modifier.calling-convention.cpp" + }, "37": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "38": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "39": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "40": { + "39": { "name": "comment.block.cpp" }, + "40": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, "41": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "42": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "43": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "44": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "45": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "46": { - "name": "storage.type.modifier.calling-convention.cpp" - }, - "47": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "48": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "49": { - "name": "comment.block.cpp" - }, - "50": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "51": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "52": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "53": { - "name": "comment.block.cpp" - }, - "54": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "55": { "patterns": [ { "match": "::", @@ -7534,31 +8421,31 @@ } ] }, - "57": { - "name": "meta.template.call.cpp", + "46": { "patterns": [ { "include": "#template_call_range" } ] }, - "59": { + "47": {}, + "48": { "name": "keyword.other.operator.overload.cpp" }, - "60": { + "49": { "patterns": [ { "include": "#inline_comment" } ] }, - "61": { + "50": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "62": { + "51": { "name": "comment.block.cpp" }, - "63": { + "52": { "patterns": [ { "match": "\\*\\/", @@ -7570,7 +8457,7 @@ } ] }, - "64": { + "53": { "patterns": [ { "match": "::", @@ -7585,28 +8472,28 @@ } ] }, - "66": { - "name": "meta.template.call.cpp", + "54": { "patterns": [ { "include": "#template_call_range" } ] }, - "68": { + "55": {}, + "56": { "name": "entity.name.operator.cpp" }, - "69": { + "57": { "name": "entity.name.operator.type.cpp" }, - "70": { + "58": { "patterns": [ { "match": "\\*", "name": "entity.name.operator.type.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7642,20 +8529,20 @@ } ] }, - "71": { + "59": { "patterns": [ { "include": "#inline_comment" } ] }, - "72": { + "60": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "73": { + "61": { "name": "comment.block.cpp" }, - "74": { + "62": { "patterns": [ { "match": "\\*\\/", @@ -7667,104 +8554,104 @@ } ] }, - "75": { + "63": { "patterns": [ { "include": "#inline_comment" } ] }, - "76": { + "64": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "77": { + "65": { "name": "comment.block.cpp" }, + "66": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "67": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "68": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "69": { + "name": "comment.block.cpp" + }, + "70": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "71": { + "name": "entity.name.operator.type.array.cpp" + }, + "72": { + "name": "entity.name.operator.custom-literal.cpp" + }, + "73": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "74": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "75": { + "name": "comment.block.cpp" + }, + "76": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "77": { + "name": "entity.name.operator.custom-literal.cpp" + }, "78": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "79": { - "patterns": [ - { - "include": "#inline_comment" - } - ] + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, "80": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "81": { - "name": "comment.block.cpp" - }, - "82": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "83": { - "name": "entity.name.operator.type.array.cpp" - }, - "84": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "85": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "86": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "87": { - "name": "comment.block.cpp" - }, - "88": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "89": { - "name": "entity.name.operator.custom-literal.cpp" - }, - "90": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "91": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "92": { - "name": "comment.block.cpp" - }, - "93": { "patterns": [ { "match": "\\*\\/", @@ -7777,17 +8664,19 @@ ] } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.function.definition.special.operator-overload.cpp", "patterns": [ { - "name": "meta.head.function.definition.special.operator-overload.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.function.definition.special.operator-overload.cpp" } }, + "name": "meta.head.function.definition.special.operator-overload.cpp", "patterns": [ { "include": "#ever_present_context" @@ -7796,19 +8685,19 @@ "include": "#template_call_range" }, { - "contentName": "meta.function.definition.parameters.special.operator-overload.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.begin.bracket.round.special.operator-overload.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parameters.end.bracket.round.special.operator-overload.cpp" } }, + "contentName": "meta.function.definition.parameters.special.operator-overload", "patterns": [ { "include": "#function_parameter_context" @@ -7827,14 +8716,15 @@ ] }, { - "name": "meta.body.function.definition.special.operator-overload.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.function.definition.special.operator-overload.cpp" } }, + "name": "meta.body.function.definition.special.operator-overload.cpp", "patterns": [ { "include": "#function_body_context" @@ -7842,9 +8732,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.function.definition.special.operator-overload.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -7856,22 +8748,292 @@ "operators": { "patterns": [ { - "include": "#sizeof_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.cpp" + } + }, + "contentName": "meta.arguments.operator.sizeof", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#alignof_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.alignof.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.alignof.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.alignof.cpp" + } + }, + "contentName": "meta.arguments.operator.alignof", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#alignas_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.alignas.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.alignas.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.alignas.cpp" + } + }, + "contentName": "meta.arguments.operator.alignas", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#typeid_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.typeid.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.typeid.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.typeid.cpp" + } + }, + "contentName": "meta.arguments.operator.typeid", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#noexcept_operator" + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.noexcept.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.noexcept.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.noexcept.cpp" + } + }, + "contentName": "meta.arguments.operator.noexcept", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { - "include": "#sizeof_variadic_operator" + "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.variadic.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.variadic.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.variadic.cpp" + } + }, + "contentName": "meta.arguments.operator.sizeof.variadic", + "patterns": [ + { + "include": "#evaluation_context" + } + ] }, { "match": "--", @@ -7920,22 +9082,1326 @@ "over_qualified_types": { "patterns": [ { - "include": "#parameter_struct" + "match": "(struct)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.struct.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.struct.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#parameter_enum" + "match": "(enum)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.enum.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.enum.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#parameter_union" + "match": "(union)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.union.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.union.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#parameter_class" + "match": "(class)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", + "captures": { + "1": { + "name": "storage.type.class.parameter.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.class.parameter.cpp" + }, + "5": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "6": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "7": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "14": { + "name": "variable.other.object.declare.cpp" + }, + "15": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "16": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "17": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "18": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } } ] }, "parameter": { - "name": "meta.parameter.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))", "beginCaptures": { "1": { "patterns": [ @@ -7963,12 +10429,12 @@ ] } }, - "end": "(?:(?=\\))|(,))", "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" } }, + "name": "meta.parameter.cpp", "patterns": [ { "include": "#ever_present_context" @@ -7983,7 +10449,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -8046,159 +10512,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { "include": "#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -8219,12 +10560,13 @@ "include": "#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))", + "beginCaptures": {}, "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" @@ -8237,10 +10579,11 @@ ] }, { - "include": "#assignment_operator" + "match": "\\=", + "name": "keyword.operator.assignment.cpp" }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\)|,|\\[|=|\\n)", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\)|,|\\[|=|\\n)", "captures": { "1": { "patterns": [ @@ -8301,19 +10644,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.begin.bracket.square.array.type.cpp" } }, - "end": "(\\])", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.end.bracket.square.array.type.cpp" } }, + "name": "meta.bracket.square.array.cpp", "patterns": [ { "include": "#evaluation_context" @@ -8328,7 +10671,7 @@ "include": "#template_call_range" }, { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -8337,7 +10680,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8428,7 +10771,7 @@ ] }, "parameter_class": { - "match": "(class)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(class)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.class.parameter.cpp" @@ -8441,59 +10784,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.class.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8529,6 +10890,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -8537,155 +10966,141 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parameter_enum": { - "match": "(enum)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(enum)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.enum.parameter.cpp" @@ -8698,59 +11113,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.enum.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8786,6 +11219,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -8794,156 +11295,142 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parameter_or_maybe_value": { - "name": "meta.parameter.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\w)", + "end": "(?:(?=\\))|(,))", "beginCaptures": { "1": { "patterns": [ @@ -8971,12 +11458,12 @@ ] } }, - "end": "(?:(?=\\))|(,))", "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" } }, + "name": "meta.parameter.cpp", "patterns": [ { "include": "#ever_present_context" @@ -9000,7 +11487,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", + "match": "((?:((?:(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -9063,159 +11550,34 @@ ] }, "11": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "15": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "16": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "17": { - "name": "comment.block.cpp" - }, - "18": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "19": { "name": "storage.type.primitive.cpp storage.type.built-in.primitive.cpp" }, - "20": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { + "12": { "name": "storage.type.cpp storage.type.built-in.cpp" }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { + "13": { "name": "support.type.posix-reserved.pthread.cpp support.type.built-in.posix-reserved.pthread.cpp" }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { + "14": { "name": "support.type.posix-reserved.cpp support.type.built-in.posix-reserved.cpp" }, - "35": { + "15": { "name": "entity.name.type.parameter.cpp" }, - "36": { + "16": { "patterns": [ { "include": "#inline_comment" } ] }, - "37": { + "17": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "38": { + "18": { "name": "comment.block.cpp" }, - "39": { + "19": { "patterns": [ { "match": "\\*\\/", @@ -9239,12 +11601,13 @@ "include": "#scope_resolution_parameter_inner_generated" }, { - "match": "(?:struct|class|union|enum)", + "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", "name": "storage.type.$0.cpp" }, { "begin": "(?<==)", "end": "(?:(?=\\))|(,))", + "beginCaptures": {}, "endCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" @@ -9257,7 +11620,7 @@ ] }, { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=(?:\\)|,|\\[|=|\\/\\/|(?:(?:\\n)|$)))", "captures": { "1": { "patterns": [ @@ -9318,19 +11681,19 @@ "include": "#attributes_context" }, { - "name": "meta.bracket.square.array.cpp", - "begin": "(\\[)", + "begin": "\\[", + "end": "\\]", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.begin.bracket.square.array.type.cpp" } }, - "end": "(\\])", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.end.bracket.square.array.type.cpp" } }, + "name": "meta.bracket.square.array.cpp", "patterns": [ { "include": "#evaluation_context" @@ -9345,7 +11708,7 @@ "include": "#template_call_range" }, { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", + "match": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*))", "captures": { "0": { "patterns": [ @@ -9354,7 +11717,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9448,7 +11811,7 @@ ] }, "parameter_struct": { - "match": "(struct)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(struct)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.struct.parameter.cpp" @@ -9461,59 +11824,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.struct.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9549,6 +11930,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -9557,155 +12006,141 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parameter_union": { - "match": "(union)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", + "match": "(union)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:\\[((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\]((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.union.parameter.cpp" @@ -9718,59 +12153,77 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.union.parameter.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9806,6 +12259,74 @@ } ] }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "12": { "patterns": [ { @@ -9814,167 +12335,153 @@ ] }, "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "14": { - "name": "comment.block.cpp" + "name": "variable.other.object.declare.cpp" }, "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "16": { "patterns": [ { - "include": "#inline_comment" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "18": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "19": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "20": { "patterns": [ { - "include": "#inline_comment" - } - ] - }, - "21": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "22": { - "name": "comment.block.cpp" - }, - "23": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "24": { - "name": "variable.other.object.declare.cpp" - }, - "25": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "26": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "27": { - "name": "comment.block.cpp" - }, - "28": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "parentheses": { - "name": "meta.parens.cpp", - "begin": "(\\()", + "begin": "\\(", + "end": "\\)", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.parens.begin.bracket.round.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parens.end.bracket.round.cpp" } }, + "name": "meta.parens.cpp", "patterns": [ { "include": "#over_qualified_types" @@ -9988,60 +12495,27 @@ } ] }, - "posix_reserved_types": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\b)", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", + "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\s+mark)\\s+(.*)", + "match": "(^((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma(?:\\s)+mark)(?:\\s)+(.*)", "captures": { "1": { "name": "keyword.control.directive.pragma.pragma-mark.cpp" @@ -10091,27 +12566,36 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "punctuation.definition.directive.cpp" }, - "7": { + "5": { "name": "entity.name.tag.pragma-mark.cpp" } }, @@ -10145,10 +12629,10 @@ "include": "#language_constants" }, { - "include": "#string_context_c" + "include": "#string_context" }, { - "include": "#preprocessor_number_literal" + "include": "#d9bc4796b0b_preprocessor_number_literal" }, { "include": "#operators" @@ -10166,6 +12650,7 @@ }, "preprocessor_conditional_defined": { "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:(?:ifndef|ifdef)|if)))", + "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:(?:ifndef|ifdef)|if))", + "end": "^(?!\\s*+#\\s*(?:else|endif))", "beginCaptures": { - "1": { - "name": "keyword.control.directive.conditional.$7.cpp" + "0": { + "name": "keyword.control.directive.conditional.$6.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -10232,16 +12717,19 @@ } ] }, - "6": { + "5": { "name": "punctuation.definition.directive.cpp" - } + }, + "6": {} }, - "end": "(?:^)(?!\\s*+#\\s*(?:else|endif))", + "endCaptures": {}, "patterns": [ { - "name": "meta.preprocessor.conditional.cpp", "begin": "\\G(?<=ifndef|ifdef|if)", "end": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "punctuation.definition.directive.cpp" } }, - "name": "keyword.control.directive.$6.cpp" + "name": "keyword.control.directive.$4.cpp" }, - "preprocessor_number_literal": { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", "captures": { "0": { - "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -10664,120 +12896,127 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "4": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "6": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } - } + }, + "name": "meta.qualified_type.cpp" }, "qualifiers_and_specifiers_post_parameters": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "3": { - "name": "comment.block.cpp" + "name": "storage.modifier.specifier.functional.post-parameters.$3.cpp" }, "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "5": { - "name": "storage.modifier.specifier.functional.post-parameters.$5.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] } } }, "scope_resolution": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10822,8 +13104,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10833,7 +13114,7 @@ } }, "scope_resolution_function_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10845,8 +13126,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10856,7 +13136,7 @@ } }, "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -10868,18 +13148,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.function.call.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -10887,13 +13167,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" } } }, "scope_resolution_function_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10905,8 +13186,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10916,7 +13196,7 @@ } }, "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -10928,18 +13208,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.function.definition.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -10947,13 +13227,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" } } }, "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10965,8 +13246,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -10976,7 +13256,7 @@ } }, "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -10988,18 +13268,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.function.definition.operator-overload.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11007,13 +13287,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" } } }, "scope_resolution_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11025,18 +13306,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11044,13 +13325,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" } } }, "scope_resolution_namespace_alias": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11062,8 +13344,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11073,7 +13354,7 @@ } }, "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11085,18 +13366,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.namespace.alias.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11104,13 +13385,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" } } }, "scope_resolution_namespace_block": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11122,8 +13404,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11133,7 +13414,7 @@ } }, "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11145,18 +13426,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.namespace.block.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11164,13 +13445,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" } } }, "scope_resolution_namespace_using": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11182,8 +13464,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11193,7 +13474,7 @@ } }, "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11205,18 +13486,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.namespace.using.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11224,13 +13505,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" } } }, "scope_resolution_parameter": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11242,8 +13524,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11253,7 +13534,7 @@ } }, "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11265,18 +13546,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.parameter.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11284,13 +13565,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.parameter.cpp" } } }, "scope_resolution_template_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11302,8 +13584,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11313,7 +13594,7 @@ } }, "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11325,18 +13606,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.template.call.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11344,13 +13625,14 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" } } }, "scope_resolution_template_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11362,8 +13644,7 @@ "1": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" }, - "3": { - "name": "meta.template.call.cpp", + "2": { "patterns": [ { "include": "#template_call_range" @@ -11373,7 +13654,7 @@ } }, "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -11385,18 +13666,18 @@ "2": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" }, - "4": { - "name": "meta.template.call.cpp", + "3": { "patterns": [ { "include": "#template_call_range" } ] }, - "6": { + "4": {}, + "5": { "name": "entity.name.scope-resolution.template.definition.cpp" }, - "7": { + "6": { "name": "meta.template.call.cpp", "patterns": [ { @@ -11404,7 +13685,8 @@ } ] }, - "9": { + "7": {}, + "8": { "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" } } @@ -11414,21 +13696,22 @@ "name": "punctuation.terminator.statement.cpp" }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", "captures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -11471,124 +13771,132 @@ ] }, "4": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "5": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "7": { "patterns": [ { - "include": "#inline_comment" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "25": { - "name": "entity.name.type.cpp" - }, - "26": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "28": { + "12": {}, + "13": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -11624,60 +13932,78 @@ } ] }, - "29": { + "14": { "patterns": [ { "include": "#inline_comment" } ] }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { + "15": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "33": { + "16": { "patterns": [ { "include": "#inline_comment" } ] }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { + "17": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "single_line_macro": { - "match": "^((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))#define.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))#define.*(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "sizeof_operator": { - "contentName": "meta.arguments.operator.sizeof.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.cpp" @@ -11752,12 +14087,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.cpp" } }, + "contentName": "meta.arguments.operator.sizeof", "patterns": [ { "include": "#evaluation_context" @@ -11765,8 +14100,8 @@ ] }, "sizeof_variadic_operator": { - "contentName": "meta.arguments.operator.sizeof.variadic.cpp", - "begin": "(\\bsizeof\\.\\.\\.)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.variadic.cpp" @@ -11800,12 +14135,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.variadic.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.sizeof.variadic.cpp" } }, + "contentName": "meta.arguments.operator.sizeof.variadic", "patterns": [ { "include": "#evaluation_context" @@ -11813,20 +14148,20 @@ ] }, "square_brackets": { - "name": "meta.bracket.square.access.cpp", + "name": "meta.bracket.square.access", "begin": "([a-zA-Z_][a-zA-Z_0-9]*|(?<=[\\]\\)]))?(\\[)(?!\\])", "beginCaptures": { "1": { - "name": "variable.other.object.cpp" + "name": "variable.other.object" }, "2": { - "name": "punctuation.definition.begin.bracket.square.cpp" + "name": "punctuation.definition.begin.bracket.square" } }, "end": "\\]", "endCaptures": { "0": { - "name": "punctuation.definition.end.bracket.square.cpp" + "name": "punctuation.definition.end.bracket.square" } }, "patterns": [ @@ -11838,21 +14173,918 @@ "standard_declares": { "patterns": [ { - "include": "#struct_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.struct.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.struct.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#union_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.union.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.union.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#enum_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.enum.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.enum.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } }, { - "include": "#class_declare" + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", + "captures": { + "1": { + "name": "storage.type.class.declare.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "4": { + "name": "entity.name.type.class.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*", + "name": "storage.modifier.pointer.cpp" + }, + { + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + }, + "name": "invalid.illegal.reference-type.cpp" + }, + { + "match": "\\&", + "name": "storage.modifier.reference.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "name": "variable.other.object.declare.cpp" + }, + "13": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "14": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } } ] }, "static_assert": { - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -11911,22 +15143,22 @@ "name": "punctuation.section.arguments.begin.bracket.round.static_assert.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.static_assert.cpp" } }, "patterns": [ { - "name": "meta.static_assert.message.cpp", - "begin": "(,)\\s*(?=(?:L|u8|u|U\\s*\\\")?)", + "begin": "(,)(?:(?:\\s)+)?(?=(?:L|u8|u|U(?:(?:\\s)+)?\\\")?)", + "end": "(?=\\))", "beginCaptures": { "1": { "name": "punctuation.separator.delimiter.comma.cpp" } }, - "end": "(?=\\))", + "endCaptures": {}, + "name": "meta.static_assert.message.cpp", "patterns": [ { "include": "#string_context" @@ -11938,8 +15170,47 @@ } ] }, + "std_space": { + "match": "(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))", + "captures": { + "0": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "1": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + } + } + }, "storage_specifiers": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "storage.modifier.specifier.$5.cpp" + "3": { + "name": "storage.modifier.specifier.$3.cpp" } } }, @@ -11977,16 +15257,7 @@ "include": "#storage_specifiers" }, { - "include": "#primitive_types" - }, - { - "include": "#non_primitive_types" - }, - { - "include": "#pthread_types" - }, - { - "include": "#posix_reserved_types" + "include": "#inline_builtin_storage_type" }, { "include": "#decltype" @@ -11999,22 +15270,22 @@ "string_context": { "patterns": [ { - "name": "string.quoted.double.cpp", - "begin": "(((?:u|u8|U|L)?)\")", + "begin": "((?:u|u8|U|L)?)\"", + "end": "\"", "beginCaptures": { - "1": { + "0": { "name": "punctuation.definition.string.begin.cpp" }, - "2": { + "1": { "name": "meta.encoding.cpp" } }, - "end": "(\")", "endCaptures": { - "1": { + "0": { "name": "punctuation.definition.string.end.cpp" } }, + "name": "string.quoted.double.cpp", "patterns": [ { "match": "(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8})", @@ -12029,8 +15300,15 @@ "name": "constant.character.escape.cpp" }, { - "match": "\\\\x[0-9a-fA-F]{2,2}", - "name": "constant.character.escape.cpp" + "match": "(?:(\\\\x0*[0-9a-fA-F]{2}(?![0-9a-fA-F]))|((?:\\\\x[0-9a-fA-F]*|\\\\x)))", + "captures": { + "1": { + "name": "constant.character.escape.cpp" + }, + "2": { + "name": "invalid.illegal.unknown-escape.cpp" + } + } }, { "include": "#string_escapes_context_c" @@ -12038,23 +15316,34 @@ ] }, { - "name": "string.quoted.single.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.struct.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -12252,134 +15664,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -12388,16 +15701,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.struct.cpp", "patterns": [ { - "name": "meta.head.struct.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.struct.cpp" } }, + "name": "meta.head.struct.cpp", "patterns": [ { "include": "#ever_present_context" @@ -12411,14 +15726,15 @@ ] }, { - "name": "meta.body.struct.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.struct.cpp" } }, + "name": "meta.body.struct.cpp", "patterns": [ { "include": "#function_pointer" @@ -12438,9 +15754,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.struct.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -12450,7 +15768,7 @@ ] }, "struct_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.struct.declare.cpp" @@ -12463,34 +15781,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.struct.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -12526,6 +15853,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -12534,106 +15895,108 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "switch_conditional_parentheses": { - "name": "meta.conditional.switch.cpp", - "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "patterns": [ @@ -12664,12 +16027,12 @@ "name": "punctuation.section.parens.begin.bracket.round.conditional.switch.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.parens.end.bracket.round.conditional.switch.cpp" } }, + "name": "meta.conditional.switch.cpp", "patterns": [ { "include": "#evaluation_context" @@ -12680,26 +16043,26 @@ ] }, "switch_statement": { - "name": "meta.block.switch.cpp", - "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.switch.cpp" }, - "2": { + "1": { "patterns": [ { "include": "#inline_comment" } ] }, - "3": { + "2": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "4": { + "3": { "name": "comment.block.cpp" }, - "5": { + "4": { "patterns": [ { "match": "\\*\\/", @@ -12711,21 +16074,23 @@ } ] }, - "6": { + "5": { "name": "keyword.control.switch.cpp" } }, - "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "endCaptures": {}, + "name": "meta.block.switch.cpp", "patterns": [ { - "name": "meta.head.switch.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.switch.cpp" } }, + "name": "meta.head.switch.cpp", "patterns": [ { "include": "#switch_conditional_parentheses" @@ -12736,14 +16101,15 @@ ] }, { - "name": "meta.body.switch.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.switch.cpp" } }, + "name": "meta.body.switch.cpp", "patterns": [ { "include": "#default_statement" @@ -12760,9 +16126,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.switch.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -12772,7 +16140,7 @@ ] }, "template_argument_defaulted": { - "match": "(?<=<|,)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*([=])", + "match": "(?<=<|,)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s)+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?([=])", "captures": { "1": { "name": "storage.type.template.cpp" @@ -12820,32 +16188,32 @@ ] }, "template_call_innards": { - "match": "((?(?:(?>[^<>]*)\\g<1>?)+)>)\\s*", + "match": "((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<1>?)+>)(?:\\s)*+", "captures": { "0": { - "name": "meta.template.call.cpp", "patterns": [ { "include": "#template_call_range" } ] } - } + }, + "name": "meta.template.call.cpp" }, "template_call_range": { - "name": "meta.template.call.cpp", - "begin": "(<)", + "begin": "<", + "end": ">", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.begin.template.call.cpp" } }, - "end": "(>)", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.end.template.call.cpp" } }, + "name": "meta.template.call.cpp", "patterns": [ { "include": "#template_call_context" @@ -12853,8 +16221,8 @@ ] }, "template_definition": { - "name": "meta.template.definition.cpp", - "begin": "(?", "beginCaptures": { "1": { "name": "storage.type.template.cpp" @@ -12863,23 +16231,23 @@ "name": "punctuation.section.angle-brackets.start.template.definition.cpp" } }, - "end": "(>)", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.end.template.definition.cpp" } }, + "name": "meta.template.definition.cpp", "patterns": [ { - "begin": "((?<=\\w)\\s*<)", + "begin": "(?<=\\w)(?:(?:\\s)+)?<", + "end": ">", "beginCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.begin.template.call.cpp" } }, - "end": "(>)", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.angle-brackets.begin.template.call.cpp" } }, @@ -12895,7 +16263,7 @@ ] }, "template_definition_argument": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", + "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s)+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:(?:\\s)+)?(\\.\\.\\.)(?:(?:\\s)+)?((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))(?:(?:\\s)+)?(?:(,)|(?=>|$))", "captures": { "1": { "patterns": [ @@ -12905,27 +16273,36 @@ ] }, "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "storage.type.template.argument.$5.cpp" + "3": { + "name": "storage.type.template.argument.$3.cpp" }, - "6": { + "4": { "patterns": [ { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -12933,19 +16310,19 @@ } ] }, - "7": { + "5": { "name": "entity.name.type.template.cpp" }, - "8": { + "6": { "name": "storage.type.template.cpp" }, - "9": { + "7": { "name": "punctuation.vararg-ellipses.template.definition.cpp" }, - "10": { + "8": { "name": "entity.name.type.template.cpp" }, - "11": { + "9": { "name": "punctuation.separator.delimiter.comma.template.argument.cpp" } } @@ -12970,7 +16347,7 @@ ] }, "template_isolated_definition": { - "match": "(?\\s*$)", + "match": "(?(?:(?:\\s)+)?$)", "captures": { "1": { "name": "storage.type.template.cpp" @@ -12992,16 +16369,15 @@ } }, "ternary_operator": { - "applyEndPatternLast": true, - "begin": "(\\?)", + "begin": "\\?", + "end": ":", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.ternary.cpp" } }, - "end": "(:)", "endCaptures": { - "1": { + "0": { "name": "keyword.operator.ternary.cpp" } }, @@ -13060,9 +16436,6 @@ { "include": "#storage_types" }, - { - "include": "#misc_storage_modifiers" - }, { "include": "#lambdas" }, @@ -13081,19 +16454,17 @@ { "include": "#square_brackets" }, - { - "include": "#empty_square_brackets" - }, { "include": "#semicolon" }, { "include": "#comma" } - ] + ], + "applyEndPatternLast": 1 }, "the_this_keyword": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { + "3": { "name": "variable.language.this.cpp" } } }, "type_alias": { - "match": "(using)\\s*(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", + "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -13135,15 +16515,16 @@ "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -13186,79 +16584,260 @@ ] }, "5": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "6": { - "name": "comment.block.cpp" - }, - "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "8": { + "6": { "patterns": [ { "include": "#inline_comment" } ] }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, - "10": { - "name": "comment.block.cpp" + "8": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "14": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + "name": "keyword.operator.assignment.cpp" + }, + "15": { + "name": "keyword.other.typename.cpp" }, "16": { - "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#storage_specifiers" + } + ] + }, + "17": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "18": { - "name": "entity.name.scope-resolution.cpp" - }, - "19": { - "name": "meta.template.call.cpp", + "name": "meta.qualified_type.cpp", "patterns": [ { - "include": "#template_call_range" + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "19": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "20": { + "patterns": [ + { + "include": "#inline_comment" } ] }, "21": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "22": { "patterns": [ @@ -13268,213 +16847,89 @@ ] }, "23": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "24": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "30": { - "name": "keyword.other.typename.cpp" - }, - "31": { - "patterns": [ - { - "include": "#storage_specifiers" - } - ] - }, - "32": { - "name": "meta.qualified_type.cpp", - "patterns": [ - { - "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -13525,102 +16980,129 @@ } ] }, - "61": { + "32": { "patterns": [ { "include": "#inline_comment" } ] }, - "62": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "63": { - "name": "comment.block.cpp" - }, - "64": { + "33": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "65": { + "34": { "patterns": [ { "include": "#inline_comment" } ] }, - "66": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "67": { - "name": "comment.block.cpp" - }, - "68": { + "35": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "69": { + "36": { "patterns": [ { "include": "#inline_comment" } ] }, - "70": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "71": { - "name": "comment.block.cpp" - }, - "72": { + "37": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "73": { + "38": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "74": { + "39": { "patterns": [ { "include": "#evaluation_context" } ] }, - "75": { + "40": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "76": { + "41": { "name": "punctuation.terminator.statement.cpp" } }, "name": "meta.declaration.type.alias.cpp" }, "type_casting_operators": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "5": { - "name": "keyword.operator.wordlike.cpp keyword.operator.cast.$5.cpp" + "3": { + "name": "keyword.operator.wordlike.cpp keyword.operator.cast.$3.cpp" } } }, "typedef_class": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.class.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.class.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -13759,134 +17354,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -13895,16 +17391,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.class.cpp", "patterns": [ { - "name": "meta.head.class.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.class.cpp" } }, + "name": "meta.head.class.cpp", "patterns": [ { "include": "#ever_present_context" @@ -13918,14 +17416,15 @@ ] }, { - "name": "meta.body.class.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.class.cpp" } }, + "name": "meta.body.class.cpp", "patterns": [ { "include": "#function_pointer" @@ -13945,12 +17444,14 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.class.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14085,30 +17586,33 @@ ] }, "typedef_function_pointer": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -14193,52 +17714,43 @@ } ] }, + "11": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14304,111 +17806,110 @@ } ] }, - "29": { + "20": { "patterns": [ { "include": "#inline_comment" } ] }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "24": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "25": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "26": { + "name": "comment.block.cpp" + }, + "27": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "28": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "name": "comment.block.cpp" }, "31": { - "name": "comment.block.cpp" + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] }, "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "38": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "39": { - "name": "comment.block.cpp" - }, - "40": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "41": { "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" }, - "42": { + "33": { "name": "punctuation.definition.function.pointer.dereference.cpp" }, - "43": { + "34": { "name": "entity.name.type.alias.cpp entity.name.type.pointer.function.cpp" }, - "44": { + "35": { "name": "punctuation.definition.begin.bracket.square.cpp" }, - "45": { + "36": { "patterns": [ { "include": "#evaluation_context" } ] }, - "46": { + "37": { "name": "punctuation.definition.end.bracket.square.cpp" }, - "47": { + "38": { "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" }, - "48": { + "39": { "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -14447,135 +17948,206 @@ } ] }, - "typedef_keyword": { - "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.struct.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.struct.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -14587,134 +18159,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -14723,16 +18196,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.struct.cpp", "patterns": [ { - "name": "meta.head.struct.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.struct.cpp" } }, + "name": "meta.head.struct.cpp", "patterns": [ { "include": "#ever_present_context" @@ -14746,14 +18221,15 @@ ] }, { - "name": "meta.body.struct.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.struct.cpp" } }, + "name": "meta.body.struct.cpp", "patterns": [ { "include": "#function_pointer" @@ -14773,12 +18249,14 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.struct.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14913,101 +18391,205 @@ ] }, "typedef_union": { - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.union.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -15019,134 +18601,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -15155,16 +18638,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.union.cpp", "patterns": [ { - "name": "meta.head.union.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.union.cpp" } }, + "name": "meta.head.union.cpp", "patterns": [ { "include": "#ever_present_context" @@ -15178,14 +18663,15 @@ ] }, { - "name": "meta.body.union.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.union.cpp" } }, + "name": "meta.body.union.cpp", "patterns": [ { "include": "#function_pointer" @@ -15205,12 +18691,14 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.union.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -15345,8 +18833,8 @@ ] }, "typeid_operator": { - "contentName": "meta.arguments.operator.typeid.cpp", - "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()", + "end": "\\)", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.typeid.cpp" @@ -15380,12 +18868,12 @@ "name": "punctuation.section.arguments.begin.bracket.round.operator.typeid.cpp" } }, - "end": "(\\))", "endCaptures": { - "1": { + "0": { "name": "punctuation.section.arguments.end.bracket.round.operator.typeid.cpp" } }, + "contentName": "meta.arguments.operator.typeid", "patterns": [ { "include": "#evaluation_context" @@ -15393,7 +18881,7 @@ ] }, "typename": { - "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|synchronized|dynamic_cast|thread_local|static_cast|const_cast|co_return|constexpr|constexpr|constexpr|co_return|protected|namespace|consteval|noexcept|decltype|template|operator|noexcept|co_yield|co_await|reflexpr|continue|co_await|co_yield|requires|volatile|register|restrict|explicit|volatile|noexcept|typename|default|_Pragma|mutable|include|concept|alignas|virtual|alignof|__asm__|defined|mutable|typedef|warning|private|and_eq|define|pragma|typeid|switch|bitand|return|ifndef|export|struct|sizeof|module|static|public|extern|inline|friend|delete|xor_eq|import|not_eq|class|compl|bitor|throw|or_eq|while|catch|break|union|const|const|endif|ifdef|undef|error|using|else|line|goto|else|elif|this|enum|case|new|asm|not|try|for|and|xor|or|if|do|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:atomic_cancel)|(?:__has_include)|(?:dynamic_cast)|(?:synchronized)|(?:thread_local)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:consteval)|(?:co_return)|(?:co_return)|(?:constexpr)|(?:protected)|(?:constexpr)|(?:namespace)|(?:noexcept)|(?:typename)|(?:decltype)|(?:template)|(?:operator)|(?:noexcept)|(?:co_yield)|(?:co_await)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:reflexpr)|(?:noexcept)|(?:requires)|(?:alignas)|(?:typedef)|(?:nullptr)|(?:alignof)|(?:mutable)|(?:concept)|(?:virtual)|(?:defined)|(?:__asm__)|(?:include)|(?:_Pragma)|(?:mutable)|(?:default)|(?:warning)|(?:private)|(?:module)|(?:return)|(?:not_eq)|(?:xor_eq)|(?:and_eq)|(?:ifndef)|(?:pragma)|(?:export)|(?:import)|(?:sizeof)|(?:static)|(?:delete)|(?:public)|(?:define)|(?:extern)|(?:inline)|(?:typeid)|(?:switch)|(?:friend)|(?:bitand)|(?:false)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:line)|(?:else)|(?:elif)|(?:true)|(?:NULL)|(?:case)|(?:goto)|(?:else)|(?:this)|(?:new)|(?:asm)|(?:not)|(?:and)|(?:xor)|(?:try)|(?:for)|(?:if)|(?:do)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -15406,61 +18894,80 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "patterns": [ { "include": "#inline_comment" } ] }, - "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { - "name": "comment.block.cpp" - }, - "9": { + "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "10": { + "6": { "name": "meta.qualified_type.cpp", "patterns": [ { - "match": "(?", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -15485,7 +19009,7 @@ } ] }, - "11": { + "7": { "patterns": [ { "include": "#attributes_context" @@ -15495,128 +19019,136 @@ } ] }, - "12": { + "8": { "patterns": [ { "include": "#inline_comment" } ] }, + "9": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, + "12": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "21": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, - "22": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "24": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "26": { - "name": "entity.name.scope-resolution.cpp" - }, - "27": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "29": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" - }, - "30": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "31": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "32": { - "name": "comment.block.cpp" - }, - "33": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "34": { - "name": "entity.name.type.cpp" - }, - "35": { - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_range" - } - ] - } + "17": {} } }, "undef": { - "match": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*undef\\b)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?undef\\b)((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "punctuation.definition.directive.cpp" }, - "7": { + "5": { "patterns": [ { "include": "#inline_comment" } ] }, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { + "6": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "11": { + "7": { "name": "entity.name.function.preprocessor.cpp" } }, "name": "meta.preprocessor.undef.cpp" }, "union_block": { - "name": "meta.block.union.cpp", - "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", + "begin": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?={)|(?:((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*+)?(?:((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(:(?!:)))?)", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { - "1": { + "0": { "name": "meta.head.union.cpp" }, - "3": { - "name": "storage.type.$3.cpp" + "1": { + "name": "storage.type.$1.cpp" }, - "4": { + "2": { "patterns": [ { "include": "#inline_comment" } ] }, - "5": { + "3": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "6": { + "4": { "name": "comment.block.cpp" }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, "7": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "8": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "9": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "10": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "9": { "name": "comment.block.cpp" }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "11": { + "patterns": [ + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))", + "captures": { + "1": { + "name": "storage.type.modifier.final.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?=:|{|$)", + "captures": { + "1": { + "name": "entity.name.type.union.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + }, + "6": { + "name": "storage.type.modifier.final.cpp" + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + }, + { + "match": "DLLEXPORT", + "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.other.preprocessor.macro.predefined.probably.$0.cpp" + } + ] + }, "12": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "include": "#inline_comment" } ] }, "13": { - "name": "entity.name.other.preprocessor.macro.predefined.DLLEXPORT.cpp" - }, - "14": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "15": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "16": { + "14": { "name": "comment.block.cpp" }, - "17": { + "15": { "patterns": [ { "match": "\\*\\/", @@ -15778,134 +19431,35 @@ } ] }, - "18": { + "16": { "patterns": [ { - "include": "#attributes_context" - }, - { - "include": "#number_literal" + "include": "#inline_comment" } ] }, + "17": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "18": { + "name": "comment.block.cpp" + }, "19": { "patterns": [ { - "include": "#inline_comment" + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" } ] }, "20": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "21": { - "name": "comment.block.cpp" - }, - "22": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "23": { - "name": "entity.name.type.$3.cpp" - }, - "24": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "25": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "26": { - "name": "comment.block.cpp" - }, - "27": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "28": { - "name": "storage.type.modifier.final.cpp" - }, - "29": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "30": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "31": { - "name": "comment.block.cpp" - }, - "32": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "33": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "34": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "35": { - "name": "comment.block.cpp" - }, - "36": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "37": { "name": "punctuation.separator.colon.inheritance.cpp" - }, - "38": { - "patterns": [ - { - "include": "#inheritance_context" - } - ] } }, - "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -15914,16 +19468,18 @@ "name": "punctuation.terminator.statement.cpp" } }, + "name": "meta.block.union.cpp", "patterns": [ { - "name": "meta.head.union.cpp", "begin": "\\G ?", - "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "end": "(?:\\{|<%|\\?\\?<|(?=;))", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.begin.bracket.curly.union.cpp" } }, + "name": "meta.head.union.cpp", "patterns": [ { "include": "#ever_present_context" @@ -15937,14 +19493,15 @@ ] }, { - "name": "meta.body.union.cpp", "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "(\\}|%>|\\?\\?>)", + "end": "\\}|%>|\\?\\?>", + "beginCaptures": {}, "endCaptures": { - "1": { + "0": { "name": "punctuation.section.block.end.bracket.curly.union.cpp" } }, + "name": "meta.body.union.cpp", "patterns": [ { "include": "#function_pointer" @@ -15964,9 +19521,11 @@ ] }, { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)", + "beginCaptures": {}, + "endCaptures": {}, "name": "meta.tail.union.cpp", - "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", - "end": "[\\s\\n]*(?=;)", "patterns": [ { "include": "$self" @@ -15976,7 +19535,7 @@ ] }, "union_declare": { - "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", + "match": "((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\S)(?![:{a-zA-Z])", "captures": { "1": { "name": "storage.type.union.declare.cpp" @@ -15989,34 +19548,43 @@ ] }, "3": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "4": { - "name": "comment.block.cpp" - }, - "5": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, - "6": { + "4": { "name": "entity.name.type.union.cpp" }, - "7": { + "5": { "patterns": [ { "match": "\\*", "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", + "match": "(?:\\&((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))){2,}\\&", "captures": { "1": { "patterns": [ @@ -16052,6 +19620,40 @@ } ] }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] + }, "8": { "patterns": [ { @@ -16060,105 +19662,107 @@ ] }, "9": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + "patterns": [ + { + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } + } + ] }, "10": { - "name": "comment.block.cpp" + "patterns": [ + { + "include": "#inline_comment" + } + ] }, "11": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] }, "12": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "13": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "14": { - "name": "comment.block.cpp" - }, - "15": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "16": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "17": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "18": { - "name": "comment.block.cpp" - }, - "19": { - "patterns": [ - { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" - } - ] - }, - "20": { "name": "variable.other.object.declare.cpp" }, - "21": { + "13": { "patterns": [ { "include": "#inline_comment" } ] }, - "22": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "23": { - "name": "comment.block.cpp" - }, - "24": { + "14": { "patterns": [ { - "match": "\\*\\/", - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - { - "match": "\\*", - "name": "comment.block.cpp" + "match": "(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "patterns": [ + { + "match": "\\*\\/", + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + { + "match": "\\*", + "name": "comment.block.cpp" + } + ] + } + } } ] } } }, "using_name": { - "match": "(using)\\s+(?!namespace\\b)", + "match": "(using)(?:\\s)+(?!namespace\\b)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -16166,8 +19770,8 @@ } }, "using_namespace": { - "name": "meta.using-namespace.cpp", - "begin": "(?(?:(?>[^<>]*)\\g<7>?)+)>)\\s*)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "less", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "char", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "allocator", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "const", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.modifier.specifier.const.cpp", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": "<", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.begin.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "char", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.delimiter.comma.template.argument.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp entity.name.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp entity.name.type.cpp", - "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp meta.qualified_type.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp meta.template.call.cpp meta.template.call.cpp punctuation.section.angle-brackets.end.template.call.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "type dnode", + "c": "std::tuple_element<0, std::pair, pugi::xml_node, std::less >, std::allocator, pugi::xml_node> > > > >::type dnode", "t": "source.cpp", "r": { "dark_plus": "default: #D4D4D4", @@ -869,29 +11,7 @@ } }, { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "_Rb_tree_iterator", + "c": "std::_Rb_tree_iterator, pugi::xml_node, std::less >, std::allocator, pugi::xml_node> > > > > > dnode_it = dnodes_.find(uid.position)", "t": "source.cpp", "r": { "dark_plus": "default: #D4D4D4", @@ -900,1028 +20,5 @@ "light_vs": "default: #000000", "hc_black": "default: #FFFFFF" } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "const", - "t": "source.cpp storage.modifier.specifier.const.cpp", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "long", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "pugi", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "map", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "char", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "less", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "char", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "allocator", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pair", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "const", - "t": "source.cpp storage.modifier.specifier.const.cpp", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "std", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "basic_string", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "char", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": ",", - "t": "source.cpp punctuation.separator.delimiter.comma.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "pugi", - "t": "source.cpp entity.name.scope-resolution.cpp", - "r": { - "dark_plus": "entity.name.scope-resolution: #4EC9B0", - "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.scope-resolution: #4EC9B0" - } - }, - { - "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "xml_node", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ">", - "t": "source.cpp keyword.operator.comparison.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " dnode_it ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "=", - "t": "source.cpp keyword.operator.assignment.cpp", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "dnodes_", - "t": "source.cpp variable.other.object.access.cpp", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" - } - }, - { - "c": ".", - "t": "source.cpp punctuation.separator.dot-access.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "find", - "t": "source.cpp entity.name.function.member.cpp", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "uid", - "t": "source.cpp variable.other.object.access.cpp", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" - } - }, - { - "c": ".", - "t": "source.cpp punctuation.separator.dot-access.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "position", - "t": "source.cpp variable.other.property.cpp", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" - } - }, - { - "c": ")", - "t": "source.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } } ] \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json index 324b231b..cb2fb192 100644 --- a/extensions/cpp/test/colorize-results/test_cc.json +++ b/extensions/cpp/test/colorize-results/test_cc.json @@ -122,7 +122,7 @@ }, { "c": "%d", - "t": "source.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -430,7 +430,7 @@ }, { "c": "%d", - "t": "source.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -474,7 +474,7 @@ }, { "c": "user_candidate", - "t": "source.cpp meta.bracket.square.access.cpp variable.other.object.cpp", + "t": "source.cpp meta.bracket.square.access variable.other.object", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -485,7 +485,7 @@ }, { "c": "[", - "t": "source.cpp meta.bracket.square.access.cpp punctuation.definition.begin.bracket.square.cpp", + "t": "source.cpp meta.bracket.square.access punctuation.definition.begin.bracket.square", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +496,7 @@ }, { "c": "i", - "t": "source.cpp meta.bracket.square.access.cpp", + "t": "source.cpp meta.bracket.square.access", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "]", - "t": "source.cpp meta.bracket.square.access.cpp punctuation.definition.end.bracket.square.cpp", + "t": "source.cpp meta.bracket.square.access punctuation.definition.end.bracket.square", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -727,7 +727,7 @@ }, { "c": "O", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp entity.name.type.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp entity.name.type.parameter.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -738,7 +738,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +749,7 @@ }, { "c": "obj", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1464,7 +1464,7 @@ }, { "c": "%s", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1486,7 +1486,7 @@ }, { "c": "%s", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1805,7 +1805,7 @@ }, { "c": "movw $0x38, %ax; ltr %ax", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly", "r": { "dark_plus": "meta.embedded.assembly: #CE9178", "light_plus": "meta.embedded.assembly: #A31515", @@ -1979,4 +1979,4 @@ "hc_black": "default: #FFFFFF" } } -] +] \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index 10f6041b..7752bbfa 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -287,7 +287,7 @@ }, { "c": "Rectangle", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp entity.name.type.class.cpp", + "t": "source.cpp meta.block.class.cpp entity.name.type.class.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -298,7 +298,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp", + "t": "source.cpp meta.block.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -485,7 +485,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -496,7 +496,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -793,7 +793,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -804,7 +804,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -815,7 +815,7 @@ }, { "c": "x", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -826,7 +826,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp punctuation.separator.delimiter.comma.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -837,7 +837,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +848,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -859,7 +859,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -870,7 +870,7 @@ }, { "c": "y", - "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp meta.parameter.cpp variable.parameter.cpp", + "t": "source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters meta.parameter.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1123,7 +1123,7 @@ }, { "c": "long", - "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1134,7 +1134,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.cpp", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload meta.parameter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1145,7 +1145,7 @@ }, { "c": "double", - "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload meta.parameter.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1519,7 +1519,7 @@ }, { "c": "movl %a %b", - "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly.cpp", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly", "r": { "dark_plus": "meta.embedded.assembly: #CE9178", "light_plus": "meta.embedded.assembly: #A31515", diff --git a/extensions/css-language-features/.vscode/launch.json b/extensions/css-language-features/.vscode/launch.json index 4f7166e6..8fccefff 100644 --- a/extensions/css-language-features/.vscode/launch.json +++ b/extensions/css-language-features/.vscode/launch.json @@ -1,14 +1,5 @@ { "version": "0.2.0", - "compounds": [ - { - "name": "Debug Extension and Language Server", - "configurations": [ - "Launch Extension", - "Attach Language Server" - ] - } - ], "configurations": [ { "name": "Launch Extension", @@ -41,19 +32,6 @@ ] }, { - "name": "Attach Language Server", - "type": "node", - "request": "attach", - "protocol": "inspector", - "port": 6044, - "sourceMaps": true, - "outFiles": [ - "${workspaceFolder}/server/out/**/*.js" - ], - "smartStep": true, - "restart": true - }, - { "name": "Server Unit Tests", "type": "node", "request": "launch", @@ -74,4 +52,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/extensions/css-language-features/client/src/node/cssClientMain.ts b/extensions/css-language-features/client/src/node/cssClientMain.ts index cc675b49..b88838d8 100644 --- a/extensions/css-language-features/client/src/node/cssClientMain.ts +++ b/extensions/css-language-features/client/src/node/cssClientMain.ts @@ -11,14 +11,13 @@ import { TextDecoder } from 'util'; // this method is called when vs code is activated export function activate(context: ExtensionContext) { - const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main || ''; const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/cssServerMain`; const serverModule = context.asAbsolutePath(serverMain); // The debug options for the server - const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (7000 + Math.round(Math.random() * 999))] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index f249a512..5a2cd56c 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -10,7 +10,7 @@ "main": "./out/node/cssServerMain", "browser": "./dist/browser/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.3.5", + "vscode-css-languageservice": "^4.4.0", "vscode-languageserver": "7.0.0-next.3", "vscode-uri": "^2.1.2" }, diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index 0b30955b..e62a8ecc 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -90,7 +90,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined, + completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-', ':'] } : undefined, hoverProvider: true, documentSymbolProvider: true, referencesProvider: true, diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 2d2ffcf8..af0eddbb 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -696,10 +696,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.5.tgz#92f8817057dee7c381df2289aad539c7b553548a" - integrity sha512-g9Pjxt9T32jhY0nTOo7WRFm0As27IfdaAxcFa8c7Rml1ZqBn3XXbkExjzxY7sBWYm7I1Tp4dK6UHXHoUQHGwig== +vscode-css-languageservice@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.4.0.tgz#a7c5edf3057e707601ca18fa3728784a298513b4" + integrity sha512-jWi+297PJUUWTHwlcrZz0zIuEXuHOBJIQMapXmEzbosWGv/gMnNSAMV4hTKnl5wzxvZKZzV6j+WFdrSlKQ5qnw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index 5ae907a9..b47641f2 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -9,9 +9,13 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -const TEXT_ALWAYS = localize('status.text.auto.attach.always', 'Auto Attach: Always'); -const TEXT_SMART = localize('status.text.auto.attach.smart', 'Auto Attach: Smart'); -const TEXT_WITH_FLAG = localize('status.text.auto.attach.withFlag', 'Auto Attach: With Flag'); +const TEXT_STATUSBAR_LABEL = { + [State.Disabled]: localize('status.text.auto.attach.disabled', 'Auto Attach: Disabled'), + [State.Always]: localize('status.text.auto.attach.always', 'Auto Attach: Always'), + [State.Smart]: localize('status.text.auto.attach.smart', 'Auto Attach: Smart'), + [State.OnlyWithFlag]: localize('status.text.auto.attach.withFlag', 'Auto Attach: With Flag'), +}; + const TEXT_STATE_LABEL = { [State.Disabled]: localize('debug.javascript.autoAttach.disabled.label', 'Disabled'), [State.Always]: localize('debug.javascript.autoAttach.always.label', 'Always'), @@ -41,6 +45,9 @@ const TEXT_STATE_DESCRIPTION = { }; const TEXT_TOGGLE_WORKSPACE = localize('scope.workspace', 'Toggle auto attach in this workspace'); const TEXT_TOGGLE_GLOBAL = localize('scope.global', 'Toggle auto attach on this machine'); +const TEXT_TEMP_DISABLE = localize('tempDisable.disable', 'Temporarily disable auto attach in this session'); +const TEXT_TEMP_ENABLE = localize('tempDisable.enable', 'Re-enable auto attach'); +const TEXT_TEMP_DISABLE_LABEL = localize('tempDisable.suffix', 'Auto Attach: Disabled'); const TOGGLE_COMMAND = 'extension.node-debug.toggleAutoAttach'; const STORAGE_IPC = 'jsDebugIpcState'; @@ -65,12 +72,13 @@ const enum State { let currentState: Promise<{ context: vscode.ExtensionContext; state: State | null }>; let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item let server: Promise | undefined; // auto attach server +let isTemporarilyDisabled = false; // whether the auto attach server is disabled temporarily, reset whenever the state changes export function activate(context: vscode.ExtensionContext): void { currentState = Promise.resolve({ context, state: null }); context.subscriptions.push( - vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting), + vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting.bind(null, context)), ); context.subscriptions.push( @@ -108,24 +116,36 @@ function getDefaultScope(info: ReturnType { +async function toggleAutoAttachSetting(context: vscode.ExtensionContext, scope?: vscode.ConfigurationTarget): Promise { const section = vscode.workspace.getConfiguration(SETTING_SECTION); scope = scope || getDefaultScope(section.inspect(SETTING_STATE)); const isGlobalScope = scope === vscode.ConfigurationTarget.Global; - const quickPick = vscode.window.createQuickPick(); + const quickPick = vscode.window.createQuickPick(); const current = readCurrentState(); - quickPick.items = [State.Always, State.Smart, State.OnlyWithFlag, State.Disabled].map(state => ({ + const items: PickItem[] = [State.Always, State.Smart, State.OnlyWithFlag, State.Disabled].map(state => ({ state, label: TEXT_STATE_LABEL[state], description: TEXT_STATE_DESCRIPTION[state], alwaysShow: true, })); - quickPick.activeItems = quickPick.items.filter(i => i.state === current); + if (current !== State.Disabled) { + items.unshift({ + setTempDisabled: !isTemporarilyDisabled, + label: isTemporarilyDisabled ? TEXT_TEMP_ENABLE : TEXT_TEMP_DISABLE, + alwaysShow: true, + }); + } + + quickPick.items = items; + quickPick.activeItems = isTemporarilyDisabled + ? [items[0]] + : quickPick.items.filter(i => 'state' in i && i.state === current); quickPick.title = isGlobalScope ? TEXT_TOGGLE_GLOBAL : TEXT_TOGGLE_WORKSPACE; quickPick.buttons = [ { @@ -136,7 +156,7 @@ async function toggleAutoAttachSetting(scope?: vscode.ConfigurationTarget): Prom quickPick.show(); - const result = await new Promise(resolve => { + let result = await new Promise(resolve => { quickPick.onDidAccept(() => resolve(quickPick.selectedItems[0])); quickPick.onDidHide(() => resolve(undefined)); quickPick.onDidTriggerButton(() => { @@ -155,11 +175,26 @@ async function toggleAutoAttachSetting(scope?: vscode.ConfigurationTarget): Prom } if ('scope' in result) { - return await toggleAutoAttachSetting(result.scope); + return await toggleAutoAttachSetting(context, result.scope); } if ('state' in result) { - section.update(SETTING_STATE, result.state, scope); + if (result.state !== current) { + section.update(SETTING_STATE, result.state, scope); + } else if (isTemporarilyDisabled) { + result = { setTempDisabled: false }; + } + } + + if ('setTempDisabled' in result) { + updateStatusBar(context, current, true); + isTemporarilyDisabled = result.setTempDisabled; + if (result.setTempDisabled) { + await destroyAttachServer(); + } else { + await createAttachServer(context); // unsets temp disabled var internally + } + updateStatusBar(context, current, false); } } @@ -168,26 +203,6 @@ function readCurrentState(): State { return section.get(SETTING_STATE) ?? State.Disabled; } -/** - * Makes sure the status bar exists and is visible. - */ -function ensureStatusBarExists(context: vscode.ExtensionContext) { - if (!statusItem) { - statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - statusItem.command = TOGGLE_COMMAND; - statusItem.tooltip = localize( - 'status.tooltip.auto.attach', - 'Automatically attach to node.js processes in debug mode', - ); - statusItem.show(); - context.subscriptions.push(statusItem); - } else { - statusItem.show(); - } - - return statusItem; -} - async function clearJsDebugAttachState(context: vscode.ExtensionContext) { await context.workspaceState.update(STORAGE_IPC, undefined); await vscode.commands.executeCommand('extension.js-debug.clearAutoAttachVariables'); @@ -275,28 +290,46 @@ interface CachedIpcState { const transitions: { [S in State]: (context: vscode.ExtensionContext) => Promise } = { async [State.Disabled](context) { await clearJsDebugAttachState(context); - statusItem?.hide(); }, async [State.OnlyWithFlag](context) { await createAttachServer(context); - const statusItem = ensureStatusBarExists(context); - statusItem.text = TEXT_WITH_FLAG; }, async [State.Smart](context) { await createAttachServer(context); - const statusItem = ensureStatusBarExists(context); - statusItem.text = TEXT_SMART; }, async [State.Always](context) { await createAttachServer(context); - const statusItem = ensureStatusBarExists(context); - statusItem.text = TEXT_ALWAYS; }, }; +/** + * Ensures the status bar text reflects the current state. + */ +function updateStatusBar(context: vscode.ExtensionContext, state: State, busy = false) { + if (state === State.Disabled && !busy) { + statusItem?.hide(); + return; + } + + if (!statusItem) { + statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); + statusItem.command = TOGGLE_COMMAND; + statusItem.tooltip = localize( + 'status.tooltip.auto.attach', + 'Automatically attach to node.js processes in debug mode', + ); + context.subscriptions.push(statusItem); + } + + let text = busy ? '$(loading) ' : ''; + text += isTemporarilyDisabled ? TEXT_TEMP_DISABLE_LABEL : TEXT_STATUSBAR_LABEL[state]; + statusItem.text = text; + statusItem.show(); +} + /** * Updates the auto attach feature based on the user or workspace setting */ @@ -306,7 +339,13 @@ function updateAutoAttach(newState: State) { return { context, state: oldState }; } + if (oldState !== null) { + updateStatusBar(context, oldState, true); + } + await transitions[newState](context); + isTemporarilyDisabled = false; + updateStatusBar(context, newState, false); return { context, state: newState }; }); } diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index abb9a683..11169906 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -39,8 +39,7 @@ "openExternally" ], "enumDescriptions": [ - "%debug.server.ready.action.openExternally.description%", - "%debug.server.ready.action.debugWithChrome.description%" + "%debug.server.ready.action.openExternally.description%" ], "markdownDescription": "%debug.server.ready.action.description%", "default": "openExternally" @@ -71,7 +70,6 @@ "debugWithChrome" ], "enumDescriptions": [ - "%debug.server.ready.action.openExternally.description%", "%debug.server.ready.action.debugWithChrome.description%" ], "markdownDescription": "%debug.server.ready.action.description%", @@ -93,6 +91,39 @@ "default": "${workspaceFolder}" } } + }, + { + "type": "object", + "additionalProperties": false, + "markdownDescription": "%debug.server.ready.serverReadyAction.description%", + "default": { + "action": "startDebugging", + "name": "" + }, + "required": ["name"], + "properties": { + "action": { + "type": "string", + "enum": [ + "startDebugging" + ], + "enumDescriptions": [ + "%debug.server.ready.action.startDebugging.description%" + ], + "markdownDescription": "%debug.server.ready.action.description%", + "default": "startDebugging" + }, + "pattern": { + "type": "string", + "markdownDescription": "%debug.server.ready.pattern.description%", + "default": "listening on port ([0-9]+)" + }, + "name": { + "type": "string", + "markdownDescription": "%debug.server.ready.debugConfigName.description%", + "default": "Launch Browser" + } + } } ] } diff --git a/extensions/debug-server-ready/package.nls.json b/extensions/debug-server-ready/package.nls.json index e9d2705c..c5212d7e 100644 --- a/extensions/debug-server-ready/package.nls.json +++ b/extensions/debug-server-ready/package.nls.json @@ -6,7 +6,9 @@ "debug.server.ready.action.description": "What to do with the URI when the server is ready.", "debug.server.ready.action.openExternally.description": "Open URI externally with the default application.", "debug.server.ready.action.debugWithChrome.description": "Start debugging with the 'Debugger for Chrome'.", + "debug.server.ready.action.startDebugging.description": "Run another launch configuration.", "debug.server.ready.pattern.description": "Server is ready if this pattern appears on the debug console. The first capture group must include a URI or a port number.", "debug.server.ready.uriFormat.description": "A format string used when constructing the URI from a port number. The first '%s' is substituted with the port number.", - "debug.server.ready.webRoot.description": "Value passed to the debug configuration for the 'Debugger for Chrome'." + "debug.server.ready.webRoot.description": "Value passed to the debug configuration for the 'Debugger for Chrome'.", + "debug.server.ready.debugConfigName.description": "Name of the launch configuration to run." } diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 9f8737f3..19fc6732 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -16,9 +16,10 @@ const WEB_ROOT = '${workspaceFolder}'; interface ServerReadyAction { pattern: string; - action?: 'openExternally' | 'debugWithChrome'; + action?: 'openExternally' | 'debugWithChrome' | 'startDebugging'; uriFormat?: string; webRoot?: string; + name?: string; } class ServerReadyDetector extends vscode.Disposable { @@ -155,6 +156,10 @@ class ServerReadyDetector extends vscode.Disposable { }); break; + case 'startDebugging': + vscode.debug.startDebugging(session.workspaceFolder, args.name || 'unspecified'); + break; + default: // not supported break; diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index fbb17884..12178fa8 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -342,6 +342,11 @@ "command": "editor.emmet.action.reflectCSSValue", "title": "%command.reflectCSSValue%", "category": "Emmet" + }, + { + "command": "workbench.action.showEmmetCommands", + "title": "%command.showEmmetCommands%", + "category": "" } ], "menus": { @@ -433,7 +438,7 @@ "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/html-matcher": "^0.3.3", - "@emmetio/math-expression": "^0.1.1", + "@emmetio/math-expression": "^1.0.4", "image-size": "^0.5.2", "vscode-emmet-helper": "~2.0.0", "vscode-html-languageservice": "^3.0.3" diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 3f3bf912..2a1add89 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -23,6 +23,7 @@ "command.decrementNumberByOneTenth": "Decrement by 0.1", "command.incrementNumberByTen": "Increment by 10", "command.decrementNumberByTen": "Decrement by 10", + "command.showEmmetCommands": "Show Emmet Commands", "emmetSyntaxProfiles": "Define profile for specified syntax or use your own profile with specific rules.", "emmetExclude": "An array of languages where Emmet abbreviations should not be expanded.", "emmetExtensionsPath": "Path to a folder containing Emmet profiles and snippets.", diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 16dc7b61..a37741b8 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -138,6 +138,7 @@ function doWrapping(individualLines: boolean, args: any) { newText = newText.replace(/\$\{[\d]*(:[^}]*)?\}/g, (match) => { // Replacing Placeholders return match.replace(/^\$\{[\d]*:/, '').replace('}', ''); }); + newText = newText.replace(/\\\$/g, '$'); // Remove backslashes before $ builder.replace(oldPreviewRange, newText); const expandedTextLines = newText.split('\n'); diff --git a/extensions/emmet/src/emmetCommon.ts b/extensions/emmet/src/emmetCommon.ts index b508b2a9..e768b03a 100644 --- a/extensions/emmet/src/emmetCommon.ts +++ b/extensions/emmet/src/emmetCommon.ts @@ -124,6 +124,10 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { return reflectCssValue(); })); + context.subscriptions.push(vscode.commands.registerCommand('workbench.action.showEmmetCommands', () => { + vscode.commands.executeCommand('workbench.action.quickOpen', '>Emmet: '); + })); + updateEmmetExtensionsPath(); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { diff --git a/extensions/emmet/src/evaluateMathExpression.ts b/extensions/emmet/src/evaluateMathExpression.ts index 8a7de6ca..588d4dce 100644 --- a/extensions/emmet/src/evaluateMathExpression.ts +++ b/extensions/emmet/src/evaluateMathExpression.ts @@ -6,24 +6,42 @@ /* Based on @sergeche's work in his emmet plugin */ import * as vscode from 'vscode'; -import evaluate from '@emmetio/math-expression'; +import evaluate, { extract } from '@emmetio/math-expression'; import { DocumentStreamReader } from './bufferStream'; -export function evaluateMathExpression() { +export function evaluateMathExpression(): Thenable { if (!vscode.window.activeTextEditor) { vscode.window.showInformationMessage('No editor is active'); - return; + return Promise.resolve(false); } const editor = vscode.window.activeTextEditor; const stream = new DocumentStreamReader(editor.document); - editor.edit(editBuilder => { + return editor.edit(editBuilder => { editor.selections.forEach(selection => { - const pos = selection.isReversed ? selection.anchor : selection.active; - stream.pos = pos; + // startpos always comes before endpos + const startpos = selection.isReversed ? selection.active : selection.anchor; + const endpos = selection.isReversed ? selection.anchor : selection.active; + const selectionText = stream.substring(startpos, endpos); try { - const result = String(evaluate(stream, true)); - editBuilder.replace(new vscode.Range(stream.pos, pos), result); + if (selectionText) { + // respect selections + const result = String(evaluate(selectionText)); + editBuilder.replace(new vscode.Range(startpos, endpos), result); + } else { + // no selection made, extract expression from line + const lineToSelectionEnd = stream.substring(new vscode.Position(selection.end.line, 0), endpos); + const extractedIndices = extract(lineToSelectionEnd); + if (!extractedIndices) { + throw new Error('Invalid extracted indices'); + } + const result = String(evaluate(lineToSelectionEnd.substr(extractedIndices[0], extractedIndices[1]))); + const rangeToReplace = new vscode.Range( + new vscode.Position(selection.end.line, extractedIndices[0]), + new vscode.Position(selection.end.line, extractedIndices[1]) + ); + editBuilder.replace(rangeToReplace, result); + } } catch (err) { vscode.window.showErrorMessage('Could not evaluate expression'); // Ignore error since most likely it’s because of non-math expression @@ -31,5 +49,4 @@ export function evaluateMathExpression() { } }); }); - } diff --git a/extensions/emmet/src/test/evaluateMathExpression.test.ts b/extensions/emmet/src/test/evaluateMathExpression.test.ts new file mode 100644 index 00000000..553d0417 --- /dev/null +++ b/extensions/emmet/src/test/evaluateMathExpression.test.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as assert from 'assert'; +import { Position, Selection } from 'vscode'; +import { withRandomFileEditor, closeAllEditors } from './testUtils'; +import { evaluateMathExpression } from '../evaluateMathExpression'; + +suite('Tests for Evaluate Math Expression', () => { + teardown(closeAllEditors); + + function testEvaluateMathExpression(fileContents: string, selection: [number, number] | number, expectedFileContents: string): Thenable { + return withRandomFileEditor(fileContents, 'html', async (editor, _doc) => { + const selectionToUse = typeof selection === 'number' ? + new Selection(new Position(0, selection), new Position(0, selection)) : + new Selection(new Position(0, selection[0]), new Position(0, selection[1])); + editor.selection = selectionToUse; + + await evaluateMathExpression(); + + assert.strictEqual(editor.document.getText(), expectedFileContents); + return Promise.resolve(); + }); + } + + test('Selected sanity check', () => { + return testEvaluateMathExpression('1 + 2', [0, 5], '3'); + }); + + test('Selected with surrounding text', () => { + return testEvaluateMathExpression('test1 + 2test', [4, 9], 'test3test'); + }); + + test('Selected with number not part of selection', () => { + return testEvaluateMathExpression('test3 1+2', [6, 9], 'test3 3'); + }); + + test('Non-selected sanity check', () => { + return testEvaluateMathExpression('1 + 2', 5, '3'); + }); + + test('Non-selected midway', () => { + return testEvaluateMathExpression('1 + 2', 1, '1 + 2'); + }); + + test('Non-selected with surrounding text', () => { + return testEvaluateMathExpression('test1 + 3test', 9, 'test4test'); + }); +}); diff --git a/extensions/emmet/src/typings/emmetio__math-expression.d.ts b/extensions/emmet/src/typings/emmetio__math-expression.d.ts deleted file mode 100644 index 4c3d862b..00000000 --- a/extensions/emmet/src/typings/emmetio__math-expression.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -declare module '@emmetio/math-expression' { - import { BufferStream } from 'EmmetNode'; - - function index(stream: BufferStream, backward: boolean): number; - - export default index; -} - diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index bb19fbcd..d956865f 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -31,13 +31,12 @@ "@emmetio/stream-reader" "^2.0.0" "@emmetio/stream-reader-utils" "^0.1.0" -"@emmetio/math-expression@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-0.1.1.tgz#1ff2c7f05800f64c57ca89038ee18bce9f5776dc" - integrity sha1-H/LH8FgA9kxXyokDjuGLzp9Xdtw= +"@emmetio/math-expression@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-1.0.4.tgz#cb657ed944f82b3728f863bf5ece1b1ff3ae7497" + integrity sha512-1m7y8/VeXCAfgFoPGTerbqCIadApcIINujd3TaM/LRLPPKiod8aT1PPmh542spnsUSsSnZJjbuF7xiO4WFA42g== dependencies: - "@emmetio/stream-reader" "^2.0.1" - "@emmetio/stream-reader-utils" "^0.1.0" + "@emmetio/scanner" "^1.0.0" "@emmetio/scanner@^1.0.0": version "1.0.0" @@ -49,15 +48,15 @@ resolved "https://registry.yarnpkg.com/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz#244cb02c77ec2e74f78a9bd318218abc9c500a61" integrity sha1-JEywLHfsLnT3ipvTGCGKvJxQCmE= -"@emmetio/stream-reader@^2.0.0", "@emmetio/stream-reader@^2.0.1", "@emmetio/stream-reader@^2.2.0": +"@emmetio/stream-reader@^2.0.0", "@emmetio/stream-reader@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz#46cffea119a0a003312a21c2d9b5628cb5fcd442" integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= "@types/node@^12.11.7": - version "12.19.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.2.tgz#9565ed5c72ba96038fc3add643edd5e7820598e7" - integrity sha512-SRH6QM0IMOBBFmDiJ75vlhcbUEYEquvSuhsVW9ijG20JvdFTfOrB1p6ddZxz5y/JNnbf+9HoHhjhOVSX2hsJyA== + version "12.19.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.4.tgz#cdfbb62e26c7435ed9aab9c941393cc3598e9b46" + integrity sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w== ajv@^6.12.3: version "6.12.6" @@ -178,9 +177,9 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" - integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== balanced-match@^1.0.0: version "1.0.0" @@ -392,12 +391,12 @@ debug@^2.2.0: dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== dependencies: - ms "^2.1.1" + ms "2.1.2" decamelize@^1.1.2: version "1.2.0" @@ -994,9 +993,9 @@ is-buffer@^1.1.5, is-buffer@~1.1.6: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-core-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d" - integrity sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946" + integrity sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA== dependencies: has "^1.0.3" @@ -1410,7 +1409,7 @@ lodash.values@~2.4.1: dependencies: lodash.keys "~2.4.1" -lodash@^4.16.4: +lodash@^4.17.15: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -1572,12 +1571,12 @@ mocha-junit-reporter@^1.17.0: xml "^1.0.0" mocha-multi-reporters@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" - integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= + version "1.5.1" + resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz#c73486bed5519e1d59c9ce39ac7a9792600e5676" + integrity sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg== dependencies: - debug "^3.1.0" - lodash "^4.16.4" + debug "^4.1.1" + lodash "^4.17.15" mocha@^2.3.3: version "2.5.3" @@ -1605,7 +1604,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -2413,14 +2412,15 @@ vinyl@~2.0.1: replace-ext "^1.0.0" vscode-emmet-helper@~2.0.0: - version "2.0.8" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.8.tgz#7c3cf8027d1a75d29625e029d516da7bc56c1fdb" - integrity sha512-Wyf+b5pua+13eZSHCpmob1x915x/od4z6lIia9T2N4v7+CUYNxDisBu3/ShIM3qg3YiYvTCOm+Yx/CLd2khcVw== + version "2.0.9" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.9.tgz#16244c087cba4e379116f268384bb644649db6ad" + integrity sha512-S6RjnR9gUicl8LsYnQAMNqqOxolud9gcj+NpPyEnxfxp1YIBuC9oetj6l6N9VMZBWu6tL77wmf+/EJsRx1PDPA== dependencies: emmet "^2.1.5" jsonc-parser "^2.3.0" vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.15.1" + vscode-nls "^5.0.0" vscode-uri "^2.1.2" vscode-html-languageservice@^3.0.3: diff --git a/extensions/git/package.json b/extensions/git/package.json index c9430e7c..bea5f96a 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -181,6 +181,12 @@ "category": "Git", "icon": "$(discard)" }, + { + "command": "git.rename", + "title": "%command.rename%", + "category": "Git", + "icon": "$(discard)" + }, { "command": "git.commit", "title": "%command.commit%", @@ -278,6 +284,11 @@ "title": "%command.checkout%", "category": "Git" }, + { + "command": "git.checkoutDetached", + "title": "%command.checkoutDetached%", + "category": "Git" + }, { "command": "git.branch", "title": "%command.branch%", @@ -368,6 +379,11 @@ "title": "%command.pushToForce%", "category": "Git" }, + { + "command": "git.pushTags", + "title": "%command.pushTags%", + "category": "Git" + }, { "command": "git.pushWithTags", "title": "%command.pushFollowTags%", @@ -378,6 +394,11 @@ "title": "%command.pushFollowTagsForce%", "category": "Git" }, + { + "command": "git.cherryPick", + "title": "%command.cherryPick%", + "category": "Git" + }, { "command": "git.addRemote", "title": "%command.addRemote%", @@ -605,6 +626,10 @@ "command": "git.cleanAllUntracked", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.rename", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file" + }, { "command": "git.commit", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -705,6 +730,10 @@ "command": "git.renameBranch", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.cherryPick", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.pull", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -769,6 +798,10 @@ "command": "git.pushWithTagsForce", "when": "config.git.enabled && !git.missing && config.git.allowForcePush && gitOpenRepositoryCount != 0" }, + { + "command": "git.pushTags", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.addRemote", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -1279,17 +1312,17 @@ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" } ], "scm/change/title": [ @@ -1657,21 +1690,27 @@ "scope": "resource" }, "git.checkoutType": { - "type": "string", - "enum": [ - "all", - "local", - "tags", - "remote" - ], - "enumDescriptions": [ - "%config.checkoutType.all%", - "%config.checkoutType.local%", - "%config.checkoutType.tags%", - "%config.checkoutType.remote%" - ], + "type": "array", + "items": { + "type": "string", + "enum": [ + "local", + "tags", + "remote" + ], + "enumDescriptions": [ + "%config.checkoutType.local%", + "%config.checkoutType.tags%", + "%config.checkoutType.remote%" + ] + }, + "uniqueItems": true, "markdownDescription": "%config.checkoutType%", - "default": "all" + "default": [ + "local", + "remote", + "tags" + ] }, "git.ignoreLegacyWarning": { "type": "boolean", @@ -1751,6 +1790,28 @@ "description": "%config.enableStatusBarSync%", "scope": "resource" }, + "git.followTagsWhenSync": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.followTagsWhenSync%" + }, + "git.promptToSaveFilesBeforeStash": { + "type": "string", + "enum": [ + "always", + "staged", + "never" + ], + "enumDescriptions": [ + "%config.promptToSaveFilesBeforeStash.always%", + "%config.promptToSaveFilesBeforeStash.staged%", + "%config.promptToSaveFilesBeforeStash.never%" + ], + "scope": "resource", + "default": "always", + "description": "%config.promptToSaveFilesBeforeStash%" + }, "git.promptToSaveFilesBeforeCommit": { "type": "string", "enum": [ @@ -1783,6 +1844,23 @@ "scope": "resource", "default": "none" }, + "git.openAfterClone": { + "type": "string", + "enum": [ + "always", + "alwaysNewWindow", + "whenNoFolderOpen", + "prompt" + ], + "enumDescriptions": [ + "%config.openAfterClone.always%", + "%config.openAfterClone.alwaysNewWindow%", + "%config.openAfterClone.whenNoFolderOpen%", + "%config.openAfterClone.prompt%" + ], + "default": "prompt", + "description": "%config.openAfterClone%" + }, "git.showInlineOpenFileAction": { "type": "boolean", "default": true, @@ -1840,6 +1918,12 @@ "default": false, "description": "%config.alwaysSignOff%" }, + "git.ignoreSubmodules": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.ignoreSubmodules%" + }, "git.ignoredRepositories": { "type": "array", "items": { @@ -1876,6 +1960,12 @@ "default": false, "description": "%config.fetchOnPull%" }, + "git.pruneOnFetch": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.pruneOnFetch%" + }, "git.pullTags": { "type": "boolean", "scope": "resource", @@ -1962,6 +2052,12 @@ "default": true, "description": "%config.terminalAuthentication%" }, + "git.useCommitInputAsStashMessage": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.useCommitInputAsStashMessage%" + }, "git.githubAuthentication": { "deprecationMessage": "This setting is now deprecated, please use `github.gitAuthentication` instead." }, @@ -2166,31 +2262,36 @@ { "view": "scm", "contents": "%view.workbench.scm.empty%", - "when": "config.git.enabled && git.state == initialized && workbenchState == empty", + "when": "config.git.enabled && workbenchState == empty", + "enablement": "git.state == initialized", "group": "2_open@1" }, { "view": "scm", "contents": "%view.workbench.scm.folder%", - "when": "config.git.enabled && git.state == initialized && workbenchState == folder", + "when": "config.git.enabled && workbenchState == folder", + "enablement": "git.state == initialized", "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.workspace%", - "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0", + "when": "config.git.enabled && workbenchState == workspace && workspaceFolderCount != 0", + "enablement": "git.state == initialized", "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.emptyWorkspace%", - "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount == 0", + "when": "config.git.enabled && workbenchState == workspace && workspaceFolderCount == 0", + "enablement": "git.state == initialized", "group": "2_open@1" }, { "view": "explorer", "contents": "%view.workbench.cloneRepository%", - "when": "config.git.enabled && git.state == initialized", + "when": "config.git.enabled", + "enablement": "git.state == initialized", "group": "5_scm@1" } ] diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index e2a46033..6158a7e0 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -23,6 +23,7 @@ "command.unstage": "Unstage Changes", "command.unstageAll": "Unstage All Changes", "command.unstageSelectedRanges": "Unstage Selected Ranges", + "command.rename": "Rename", "command.clean": "Discard Changes", "command.cleanAll": "Discard All Changes", "command.cleanAllTracked": "Discard All Tracked Changes", @@ -46,10 +47,12 @@ "command.restoreCommitTemplate": "Restore Commit Template", "command.undoCommit": "Undo Last Commit", "command.checkout": "Checkout to...", + "command.checkoutDetached": "Checkout to (Detached)...", "command.branch": "Create Branch...", "command.branchFrom": "Create Branch From...", "command.deleteBranch": "Delete Branch...", "command.renameBranch": "Rename Branch...", + "command.cherryPick": "Cherry Pick...", "command.merge": "Merge Branch...", "command.rebase": "Rebase Branch...", "command.createTag": "Create Tag", @@ -66,6 +69,7 @@ "command.pushToForce": "Push to... (Force)", "command.pushFollowTags": "Push (Follow Tags)", "command.pushFollowTagsForce": "Push (Follow Tags, Force)", + "command.pushTags": "Push Tags", "command.addRemote": "Add Remote...", "command.removeRemote": "Remove Remote", "command.sync": "Sync", @@ -100,11 +104,10 @@ "config.countBadge.all": "Count all changes.", "config.countBadge.tracked": "Count only tracked changes.", "config.countBadge.off": "Turn off counter.", - "config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`.", - "config.checkoutType.all": "Show all references.", - "config.checkoutType.local": "Show only local branches.", - "config.checkoutType.tags": "Show only tags.", - "config.checkoutType.remote": "Show only remote branches.", + "config.checkoutType": "Controls what type of git refs are listed when running `Checkout to...`.", + "config.checkoutType.local": "Local branches", + "config.checkoutType.tags": "Tags", + "config.checkoutType.remote": "Remote branches", "config.branchValidationRegex": "A regular expression to validate new branch names.", "config.branchWhitespaceChar": "The character to replace whitespace in new branch names.", "config.ignoreLegacyWarning": "Ignores the legacy Git warning.", @@ -121,6 +124,11 @@ "config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.", "config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.", "config.enableStatusBarSync": "Controls whether the Git Sync command appears in the status bar.", + "config.followTagsWhenSync": "Follow push all tags when running the sync command.", + "config.promptToSaveFilesBeforeStash": "Controls whether Git should check for unsaved files before stashing changes.", + "config.promptToSaveFilesBeforeStash.always": "Check for any unsaved files.", + "config.promptToSaveFilesBeforeStash.staged": "Check only for unsaved staged files.", + "config.promptToSaveFilesBeforeStash.never": "Disable this check.", "config.promptToSaveFilesBeforeCommit": "Controls whether Git should check for unsaved files before committing.", "config.promptToSaveFilesBeforeCommit.always": "Check for any unsaved files.", "config.promptToSaveFilesBeforeCommit.staged": "Check only for unsaved staged files.", @@ -129,6 +137,11 @@ "config.postCommitCommand.none": "Don't run any command after a commit.", "config.postCommitCommand.push": "Run 'Git Push' after a successful commit.", "config.postCommitCommand.sync": "Run 'Git Sync' after a successful commit.", + "config.openAfterClone": "Controls whether to open a repository automatically after cloning.", + "config.openAfterClone.always": "Always open in current window.", + "config.openAfterClone.alwaysNewWindow": "Always open in a new window.", + "config.openAfterClone.whenNoFolderOpen": "Only open in current window when no folder is opened.", + "config.openAfterClone.prompt": "Always prompt for action.", "config.showInlineOpenFileAction": "Controls whether to show an inline Open File action in the Git changes view.", "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", "config.inputValidation": "Controls when to show commit message input validation.", @@ -138,6 +151,7 @@ "config.detectSubmodulesLimit": "Controls the limit of git submodules detected.", "config.alwaysShowStagedChangesResourceGroup": "Always show the Staged Changes resource group.", "config.alwaysSignOff": "Controls the signoff flag for all commits.", + "config.ignoreSubmodules": "Ignore modifications to submodules in the file tree.", "config.ignoredRepositories": "List of git repositories to ignore.", "config.scanRepositories": "List of paths to search for git repositories in.", "config.showProgress": "Controls whether git actions should show progress.", @@ -145,6 +159,7 @@ "config.confirmEmptyCommits": "Always confirm the creation of empty commits for the 'Git: Commit Empty' command.", "config.fetchOnPull": "When enabled, fetch all branches when pulling. Otherwise, fetch just the current one.", "config.pullTags": "Fetch all tags when pulling.", + "config.pruneOnFetch": "Prune when fetching.", "config.autoStash": "Stash any changes before pulling and restore them after successful pull.", "config.allowForcePush": "Controls whether force push (with or without lease) is enabled.", "config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.", @@ -164,6 +179,8 @@ "config.timeline.date": "Controls which date to use for items in the Timeline view", "config.timeline.date.committed": "Use the committed date", "config.timeline.date.authored": "Use the authored date", + "config.useCommitInputAsStashMessage": "Controls whether to use the message from the commit input box as the default stash message.", + "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", "submenu.commit.signoff": "Sign Off", @@ -188,5 +205,5 @@ "view.workbench.scm.folder": "The folder currently open doesn't have a git repository. You can initialize a repository which will enable source control features powered by git.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories. You can initialize a repository on a folder which will enable source control features powered by git.\n[Initialize Repository](command:git.init)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Add Folder to Workspace](command:workbench.action.addRootFolder)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", - "view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone)" + "view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone 'Clone a repository once the git extension has activated')" } diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 8bcdba44..0a309c6f 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -357,7 +357,7 @@ export function registerAPICommands(extension: GitExtensionImpl): Disposable { return; } - return pickRemoteSource(extension.model, opts); + return pickRemoteSource(extension.model, opts as any); })); return Disposable.from(...disposables); diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index f63b0b12..b9a09632 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -212,6 +212,7 @@ export interface RemoteSourceProvider { readonly icon?: string; // codicon name readonly supportsQuery?: boolean; getRemoteSources(query?: string): ProviderResult; + getBranches?(url: string): ProviderResult; publishRepository?(repository: Repository): Promise; } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 83fe906c..2c24d909 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git'; @@ -31,14 +30,14 @@ class CheckoutItem implements QuickPickItem { constructor(protected ref: Ref) { } - async run(repository: Repository): Promise { + async run(repository: Repository, opts?: { detached?: boolean }): Promise { const ref = this.ref.name; if (!ref) { return; } - await repository.checkout(ref); + await repository.checkout(ref, opts); } } @@ -55,7 +54,7 @@ class CheckoutRemoteHeadItem extends CheckoutItem { return localize('remote branch at', "Remote branch at {0}", this.shortCommit); } - async run(repository: Repository): Promise { + async run(repository: Repository, opts?: { detached?: boolean }): Promise { if (!this.ref.name) { return; } @@ -63,9 +62,9 @@ class CheckoutRemoteHeadItem extends CheckoutItem { const branches = await repository.findTrackingBranches(this.ref.name); if (branches.length > 0) { - await repository.checkout(branches[0].name!); + await repository.checkout(branches[0].name!, opts); } else { - await repository.checkoutTracking(this.ref.name); + await repository.checkoutTracking(this.ref.name, opts); } } } @@ -114,31 +113,21 @@ class RebaseItem implements QuickPickItem { } class CreateBranchItem implements QuickPickItem { - - constructor(private cc: CommandCenter) { } - get label(): string { return '$(plus) ' + localize('create branch', 'Create new branch...'); } get description(): string { return ''; } - get alwaysShow(): boolean { return true; } - - async run(repository: Repository): Promise { - await this.cc.branch(repository); - } } class CreateBranchFromItem implements QuickPickItem { - - constructor(private cc: CommandCenter) { } - get label(): string { return '$(plus) ' + localize('create branch from', 'Create new branch from...'); } get description(): string { return ''; } - get alwaysShow(): boolean { return true; } +} - async run(repository: Repository): Promise { - await this.cc.branch(repository); - } +class CheckoutDetachedItem implements QuickPickItem { + get label(): string { return '$(debug-disconnect) ' + localize('checkout detached', 'Checkout detached...'); } + get description(): string { return ''; } + get alwaysShow(): boolean { return true; } } class HEADItem implements QuickPickItem { @@ -217,18 +206,53 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ function createCheckoutItems(repository: Repository): CheckoutItem[] { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeTags = checkoutType === 'all' || checkoutType === 'tags'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + const checkoutTypeConfig = config.get('checkoutType'); + let checkoutTypes: string[]; - const heads = repository.refs.filter(ref => ref.type === RefType.Head) - .map(ref => new CheckoutItem(ref)); - const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) - .map(ref => new CheckoutTagItem(ref)); - const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) - .map(ref => new CheckoutRemoteHeadItem(ref)); + if (checkoutTypeConfig === 'all' || !checkoutTypeConfig || checkoutTypeConfig.length === 0) { + checkoutTypes = ['local', 'remote', 'tags']; + } else if (typeof checkoutTypeConfig === 'string') { + checkoutTypes = [checkoutTypeConfig]; + } else { + checkoutTypes = checkoutTypeConfig; + } - return [...heads, ...tags, ...remoteHeads]; + const processors = checkoutTypes.map(getCheckoutProcessor) + .filter(p => !!p) as CheckoutProcessor[]; + + for (const ref of repository.refs) { + for (const processor of processors) { + processor.onRef(ref); + } + } + + return processors.reduce((r, p) => r.concat(...p.items), []); +} + +class CheckoutProcessor { + + private refs: Ref[] = []; + get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(r)); } + constructor(private type: RefType, private ctor: { new(ref: Ref): CheckoutItem }) { } + + onRef(ref: Ref): void { + if (ref.type === this.type) { + this.refs.push(ref); + } + } +} + +function getCheckoutProcessor(type: string): CheckoutProcessor | undefined { + switch (type) { + case 'local': + return new CheckoutProcessor(RefType.Head, CheckoutItem); + case 'remote': + return new CheckoutProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem); + case 'tags': + return new CheckoutProcessor(RefType.Tag, CheckoutTagItem); + } + + return undefined; } function sanitizeRemoteName(name: string) { @@ -246,6 +270,7 @@ enum PushType { Push, PushTo, PushFollowTags, + PushTags } interface PushOptions { @@ -254,9 +279,27 @@ interface PushOptions { silent?: boolean; } +class CommandErrorOutputTextDocumentContentProvider implements TextDocumentContentProvider { + + private items = new Map(); + + set(uri: Uri, contents: string): void { + this.items.set(uri.path, contents); + } + + delete(uri: Uri): void { + this.items.delete(uri.path); + } + + provideTextDocumentContent(uri: Uri): string | undefined { + return this.items.get(uri.path); + } +} + export class CommandCenter { private disposables: Disposable[]; + private commandErrors = new CommandErrorOutputTextDocumentContentProvider(); constructor( private git: Git, @@ -273,6 +316,8 @@ export class CommandCenter { return commands.registerCommand(commandId, command); } }); + + this.disposables.push(workspace.registerTextDocumentContentProvider('git-output', this.commandErrors)); } @command('git.setLogLevel') @@ -311,165 +356,14 @@ export class CommandCenter { } @command('git.openResource') - async openResource(resource: Resource, preserveFocus: boolean): Promise { + async openResource(resource: Resource): Promise { const repository = this.model.getRepository(resource.resourceUri); if (!repository) { return; } - const config = workspace.getConfiguration('git', Uri.file(repository.root)); - const openDiffOnClick = config.get('openDiffOnClick'); - - if (openDiffOnClick) { - await this._openResource(resource, undefined, preserveFocus, false); - } else { - await this.openFile(resource); - } - } - - private async _openResource(resource: Resource, preview?: boolean, preserveFocus?: boolean, preserveSelection?: boolean): Promise { - let stat: Stats | undefined; - - try { - stat = await new Promise((c, e) => lstat(resource.resourceUri.fsPath, (err, stat) => err ? e(err) : c(stat))); - } catch (err) { - // noop - } - - let left: Uri | undefined; - let right: Uri | undefined; - - if (stat && stat.isDirectory()) { - const repository = this.model.getRepositoryForSubmodule(resource.resourceUri); - - if (repository) { - right = toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: repository.root }); - } - } else { - if (resource.type !== Status.DELETED_BY_THEM) { - left = this.getLeftResource(resource); - } - - right = this.getRightResource(resource); - } - - const title = this.getTitle(resource); - - if (!right) { - // TODO - console.error('oh no'); - return; - } - - const opts: TextDocumentShowOptions = { - preserveFocus, - preview, - viewColumn: ViewColumn.Active - }; - - const activeTextEditor = window.activeTextEditor; - - // Check if active text editor has same path as other editor. we cannot compare via - // URI.toString() here because the schemas can be different. Instead we just go by path. - if (preserveSelection && activeTextEditor && activeTextEditor.document.uri.path === right.path) { - opts.selection = activeTextEditor.selection; - } - - if (!left) { - await commands.executeCommand('vscode.open', right, opts, title); - } else { - await commands.executeCommand('vscode.diff', left, right, title, opts); - } - } - - private getLeftResource(resource: Resource): Uri | undefined { - switch (resource.type) { - case Status.INDEX_MODIFIED: - case Status.INDEX_RENAMED: - case Status.INDEX_ADDED: - return toGitUri(resource.original, 'HEAD'); - - case Status.MODIFIED: - case Status.UNTRACKED: - return toGitUri(resource.resourceUri, '~'); - - case Status.DELETED_BY_THEM: - return toGitUri(resource.resourceUri, ''); - } - return undefined; - } - - private getRightResource(resource: Resource): Uri | undefined { - switch (resource.type) { - case Status.INDEX_MODIFIED: - case Status.INDEX_ADDED: - case Status.INDEX_COPIED: - case Status.INDEX_RENAMED: - return toGitUri(resource.resourceUri, ''); - - case Status.INDEX_DELETED: - case Status.DELETED: - return toGitUri(resource.resourceUri, 'HEAD'); - - case Status.DELETED_BY_US: - return toGitUri(resource.resourceUri, '~3'); - - case Status.DELETED_BY_THEM: - return toGitUri(resource.resourceUri, '~2'); - - case Status.MODIFIED: - case Status.UNTRACKED: - case Status.IGNORED: - case Status.INTENT_TO_ADD: - const repository = this.model.getRepository(resource.resourceUri); - - if (!repository) { - return; - } - - const uriString = resource.resourceUri.toString(); - const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); - - if (indexStatus && indexStatus.renameResourceUri) { - return indexStatus.renameResourceUri; - } - - return resource.resourceUri; - - case Status.BOTH_ADDED: - case Status.BOTH_MODIFIED: - return resource.resourceUri; - } - return undefined; - } - - private getTitle(resource: Resource): string { - const basename = path.basename(resource.resourceUri.fsPath); - - switch (resource.type) { - case Status.INDEX_MODIFIED: - case Status.INDEX_RENAMED: - case Status.INDEX_ADDED: - return localize('git.title.index', '{0} (Index)', basename); - - case Status.MODIFIED: - case Status.BOTH_ADDED: - case Status.BOTH_MODIFIED: - return localize('git.title.workingTree', '{0} (Working Tree)', basename); - - case Status.DELETED_BY_US: - return localize('git.title.theirs', '{0} (Theirs)', basename); - - case Status.DELETED_BY_THEM: - return localize('git.title.ours', '{0} (Ours)', basename); - - case Status.UNTRACKED: - return localize('git.title.untracked', '{0} (Untracked)', basename); - - default: - return ''; - } + await resource.open(); } async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise { @@ -531,35 +425,54 @@ export class CommandCenter { (progress, token) => this.git.clone(url!, { parentPath: parentPath!, progress, recursive: options.recursive }, token) ); - let message = localize('proposeopen', "Would you like to open the cloned repository?"); - const open = localize('openrepo', "Open"); - const openNewWindow = localize('openreponew', "Open in New Window"); - const choices = [open, openNewWindow]; + const config = workspace.getConfiguration('git'); + const openAfterClone = config.get<'always' | 'alwaysNewWindow' | 'whenNoFolderOpen' | 'prompt'>('openAfterClone'); - const addToWorkspace = localize('add', "Add to Workspace"); - if (workspace.workspaceFolders) { - message = localize('proposeopen2', "Would you like to open the cloned repository, or add it to the current workspace?"); - choices.push(addToWorkspace); + enum PostCloneAction { Open, OpenNewWindow, AddToWorkspace } + let action: PostCloneAction | undefined = undefined; + + if (openAfterClone === 'always') { + action = PostCloneAction.Open; + } else if (openAfterClone === 'alwaysNewWindow') { + action = PostCloneAction.OpenNewWindow; + } else if (openAfterClone === 'whenNoFolderOpen' && !workspace.workspaceFolders) { + action = PostCloneAction.Open; } - const result = await window.showInformationMessage(message, ...choices); + if (action === undefined) { + let message = localize('proposeopen', "Would you like to open the cloned repository?"); + const open = localize('openrepo', "Open"); + const openNewWindow = localize('openreponew', "Open in New Window"); + const choices = [open, openNewWindow]; + + const addToWorkspace = localize('add', "Add to Workspace"); + if (workspace.workspaceFolders) { + message = localize('proposeopen2', "Would you like to open the cloned repository, or add it to the current workspace?"); + choices.push(addToWorkspace); + } + + const result = await window.showInformationMessage(message, ...choices); + + action = result === open ? PostCloneAction.Open + : result === openNewWindow ? PostCloneAction.OpenNewWindow + : result === addToWorkspace ? PostCloneAction.AddToWorkspace : undefined; + } - const openFolder = result === open; /* __GDPR__ "clone" : { "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ - this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: openFolder ? 1 : 0 }); + this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: action === PostCloneAction.Open || action === PostCloneAction.OpenNewWindow ? 1 : 0 }); const uri = Uri.file(repositoryPath); - if (openFolder) { + if (action === PostCloneAction.Open) { commands.executeCommand('vscode.openFolder', uri, { forceReuseWindow: true }); - } else if (result === addToWorkspace) { + } else if (action === PostCloneAction.AddToWorkspace) { workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); - } else if (result === openNewWindow) { + } else if (action === PostCloneAction.OpenNewWindow) { commands.executeCommand('vscode.openFolder', uri, { forceNewWindow: true }); } } catch (err) { @@ -761,7 +674,10 @@ export class CommandCenter { try { document = await workspace.openTextDocument(uri); } catch (error) { - await commands.executeCommand('vscode.open', uri, opts); + await commands.executeCommand('vscode.open', uri, { + ...opts, + override: arg instanceof Resource && arg.type === Status.BOTH_MODIFIED ? false : undefined + }); continue; } @@ -801,7 +717,7 @@ export class CommandCenter { return; } - const HEAD = this.getLeftResource(resource); + const HEAD = resource.leftUri; const basename = path.basename(resource.resourceUri.fsPath); const title = `${basename} (HEAD)`; @@ -819,10 +735,6 @@ export class CommandCenter { @command('git.openChange') async openChange(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise { - const preserveFocus = arg instanceof Resource; - const preview = !(arg instanceof Resource); - - const preserveSelection = arg instanceof Uri || !arg; let resources: Resource[] | undefined = undefined; if (arg instanceof Uri) { @@ -849,10 +761,33 @@ export class CommandCenter { } for (const resource of resources) { - await this._openResource(resource, preview, preserveFocus, preserveSelection); + await resource.openChange(); } } + @command('git.rename', { repository: true }) + async rename(repository: Repository, fromUri: Uri | undefined): Promise { + fromUri = fromUri ?? window.activeTextEditor?.document.uri; + + if (!fromUri) { + return; + } + + const from = path.relative(repository.root, fromUri.path); + let to = await window.showInputBox({ + value: from, + valueSelection: [from.length - path.basename(from).length, from.length] + }); + + to = to?.trim(); + + if (!to) { + return; + } + + await repository.move(from, to); + } + @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { this.outputChannel.appendLine(`git.stage ${resourceStates.length}`); @@ -1018,6 +953,10 @@ export class CommandCenter { @command('git.stageChange') async stageChange(uri: Uri, changes: LineChange[], index: number): Promise { + if (!uri) { + return; + } + const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0]; if (!textEditor) { @@ -1068,6 +1007,10 @@ export class CommandCenter { @command('git.revertChange') async revertChange(uri: Uri, changes: LineChange[], index: number): Promise { + if (!uri) { + return; + } + const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0]; if (!textEditor) { @@ -1397,7 +1340,7 @@ export class CommandCenter { ? localize('unsaved files single', "The following file has unsaved changes which won't be included in the commit if you proceed: {0}.\n\nWould you like to save it before committing?", path.basename(documents[0].uri.fsPath)) : localize('unsaved files', "There are {0} unsaved files.\n\nWould you like to save them before committing?", documents.length); const saveAndCommit = localize('save and commit', "Save All & Commit"); - const commit = localize('commit', "Commit Anyway"); + const commit = localize('commit', "Commit Staged Changes"); const pick = await window.showWarningMessage(message, { modal: true }, saveAndCommit, commit); if (pick === saveAndCommit) { @@ -1409,8 +1352,14 @@ export class CommandCenter { } } + if (!opts) { + opts = { all: noStagedChanges }; + } else if (!opts.all && noStagedChanges && !opts.empty) { + opts = { ...opts, all: true }; + } + // no changes, and the user has not configured to commit all in this case - if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit) { + if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.empty) { const suggestSmartCommit = config.get('suggestSmartCommit') === true; if (!suggestSmartCommit) { @@ -1434,13 +1383,7 @@ export class CommandCenter { } } - if (!opts) { - opts = { all: noStagedChanges }; - } else if (!opts.all && noStagedChanges) { - opts = { ...opts, all: true }; - } - - // enable signing of commits if configurated + // enable signing of commits if configured opts.signCommit = enableCommitSigning; if (config.get('alwaysSignOff')) { @@ -1458,10 +1401,18 @@ export class CommandCenter { // no staged changes and no tracked unstaged changes || (noStagedChanges && smartCommitChanges === 'tracked' && repository.workingTreeGroup.resourceStates.every(r => r.type === Status.UNTRACKED)) ) + // amend allows changing only the commit message + && !opts.amend && !opts.empty ) { - window.showInformationMessage(localize('no changes', "There are no changes to commit.")); - return false; + const commitAnyway = localize('commit anyway', "Create Empty Commit"); + const answer = await window.showInformationMessage(localize('no changes', "There are no changes to commit."), commitAnyway); + + if (answer !== commitAnyway) { + return false; + } + + opts.empty = true; } if (opts.noVerify) { @@ -1484,9 +1435,9 @@ export class CommandCenter { } } - const message = await getCommitMessage(); + let message = await getCommitMessage(); - if (!message) { + if (!message && !opts.amend) { return false; } @@ -1523,7 +1474,7 @@ export class CommandCenter { let value: string | undefined = undefined; if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) { - value = (await repository.getCommit(repository.HEAD.commit)).message; + return undefined; } const branchName = repository.headShortName; @@ -1690,20 +1641,38 @@ export class CommandCenter { } @command('git.checkout', { repository: true }) - async checkout(repository: Repository, treeish: string): Promise { - if (typeof treeish === 'string') { - await repository.checkout(treeish); + async checkout(repository: Repository, treeish?: string): Promise { + return this._checkout(repository, { treeish }); + } + + @command('git.checkoutDetached', { repository: true }) + async checkoutDetached(repository: Repository, treeish?: string): Promise { + return this._checkout(repository, { detached: true, treeish }); + } + + private async _checkout(repository: Repository, opts?: { detached?: boolean, treeish?: string }): Promise { + if (typeof opts?.treeish === 'string') { + await repository.checkout(opts?.treeish, opts); return true; } - const createBranch = new CreateBranchItem(this); - const createBranchFrom = new CreateBranchFromItem(this); - const picks = [createBranch, createBranchFrom, ...createCheckoutItems(repository)]; - const placeHolder = localize('select a ref to checkout', 'Select a ref to checkout'); + const createBranch = new CreateBranchItem(); + const createBranchFrom = new CreateBranchFromItem(); + const checkoutDetached = new CheckoutDetachedItem(); + const picks: QuickPickItem[] = []; + + if (!opts?.detached) { + picks.push(createBranch, createBranchFrom, checkoutDetached); + } + + picks.push(...createCheckoutItems(repository)); const quickpick = window.createQuickPick(); quickpick.items = picks; - quickpick.placeholder = placeHolder; + quickpick.placeholder = opts?.detached + ? localize('select a ref to checkout detached', 'Select a ref to checkout in detached mode') + : localize('select a ref to checkout', 'Select a ref to checkout'); + quickpick.show(); const choice = await new Promise(c => quickpick.onDidAccept(() => c(quickpick.activeItems[0]))); @@ -1717,8 +1686,31 @@ export class CommandCenter { await this._branch(repository, quickpick.value); } else if (choice === createBranchFrom) { await this._branch(repository, quickpick.value, true); + } else if (choice === checkoutDetached) { + return this._checkout(repository, { detached: true }); } else { - await (choice as CheckoutItem).run(repository); + const item = choice as CheckoutItem; + + try { + await item.run(repository, opts); + } catch (err) { + if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) { + throw err; + } + + const force = localize('force', "Force Checkout"); + const stash = localize('stashcheckout', "Stash & Checkout"); + const choice = await window.showWarningMessage(localize('local changes', "Your local changes would be overwritten by checkout."), { modal: true }, force, stash); + + if (choice === force) { + await this.cleanAll(repository); + await item.run(repository, opts); + } else if (choice === stash) { + await this.stash(repository); + await item.run(repository, opts); + await this.stashPopLatest(repository); + } + } } return true; @@ -1849,8 +1841,8 @@ export class CommandCenter { @command('git.merge', { repository: true }) async merge(repository: Repository): Promise { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + const checkoutType = config.get('checkoutType'); + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote' || checkoutType?.includes('remote'); const heads = repository.refs.filter(ref => ref.type === RefType.Head) .filter(ref => ref.name || ref.commit) @@ -1874,8 +1866,8 @@ export class CommandCenter { @command('git.rebase', { repository: true }) async rebase(repository: Repository): Promise { const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + const checkoutType = config.get('checkoutType'); + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote' || checkoutType?.includes('remote'); const heads = repository.refs.filter(ref => ref.type === RefType.Head) .filter(ref => ref.name !== repository.HEAD?.name) @@ -2068,7 +2060,7 @@ export class CommandCenter { forcePushMode = config.get('useForcePushWithLease') === true ? ForcePushMode.ForceWithLease : ForcePushMode.Force; if (config.get('confirmForcePush')) { - const message = localize('confirm force push', "You are about to force push your changes, this can be destructive and could inadvertedly overwrite changes made by others.\n\nAre you sure to continue?"); + const message = localize('confirm force push', "You are about to force push your changes, this can be destructive and could inadvertently overwrite changes made by others.\n\nAre you sure to continue?"); const yes = localize('ok', "OK"); const neverAgain = localize('never ask again', "OK, Don't Ask Again"); const pick = await window.showWarningMessage(message, { modal: true }, yes, neverAgain); @@ -2086,6 +2078,10 @@ export class CommandCenter { return; } + if (pushOptions.pushType === PushType.PushTags) { + await repository.pushTags(undefined, forcePushMode); + } + if (!repository.HEAD || !repository.HEAD.name) { if (!pushOptions.silent) { window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote.")); @@ -2157,6 +2153,21 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushFollowTags, forcePush: true }); } + @command('git.cherryPick', { repository: true }) + async cherryPick(repository: Repository): Promise { + const hash = await window.showInputBox({ + placeHolder: localize('commit hash', "Commit Hash"), + prompt: localize('provide commit hash', "Please provide the commit hash"), + ignoreFocusOut: true + }); + + if (!hash) { + return; + } + + await repository.cherryPick(hash); + } + @command('git.pushTo', { repository: true }) async pushTo(repository: Repository): Promise { await this._push(repository, { pushType: PushType.PushTo }); @@ -2167,6 +2178,11 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushTo, forcePush: true }); } + @command('git.pushTags', { repository: true }) + async pushTags(repository: Repository): Promise { + await this._push(repository, { pushType: PushType.PushTags }); + } + @command('git.addRemote', { repository: true }) async addRemote(repository: Repository): Promise { const url = await pickRemoteSource(this.model, { @@ -2200,6 +2216,7 @@ export class CommandCenter { } await repository.addRemote(name, url); + await repository.fetch(name); return name; } @@ -2413,7 +2430,45 @@ export class CommandCenter { return; } - const message = await this.getStashMessage(); + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + const promptToSaveFilesBeforeStashing = config.get<'always' | 'staged' | 'never'>('promptToSaveFilesBeforeStash'); + + if (promptToSaveFilesBeforeStashing !== 'never') { + let documents = workspace.textDocuments + .filter(d => !d.isUntitled && d.isDirty && isDescendant(repository.root, d.uri.fsPath)); + + if (promptToSaveFilesBeforeStashing === 'staged' || repository.indexGroup.resourceStates.length > 0) { + documents = documents + .filter(d => repository.indexGroup.resourceStates.some(s => pathEquals(s.resourceUri.fsPath, d.uri.fsPath))); + } + + if (documents.length > 0) { + const message = documents.length === 1 + ? localize('unsaved stash files single', "The following file has unsaved changes which won't be included in the stash if you proceed: {0}.\n\nWould you like to save it before stashing?", path.basename(documents[0].uri.fsPath)) + : localize('unsaved stash files', "There are {0} unsaved files.\n\nWould you like to save them before stashing?", documents.length); + const saveAndStash = localize('save and stash', "Save All & Stash"); + const stash = localize('stash', "Stash Anyway"); + const pick = await window.showWarningMessage(message, { modal: true }, saveAndStash, stash); + + if (pick === saveAndStash) { + await Promise.all(documents.map(d => d.save())); + } else if (pick !== stash) { + return; // do not stash on cancel + } + } + } + + let message: string | undefined; + + if (config.get('useCommitInputAsStashMessage') && (!repository.sourceControl.commitTemplate || repository.inputBox.value !== repository.sourceControl.commitTemplate)) { + message = repository.inputBox.value; + } + + message = await window.showInputBox({ + value: message, + prompt: localize('provide stash message', "Optionally provide a stash message"), + placeHolder: localize('stash message', "Stash message") + }); if (typeof message === 'undefined') { return; @@ -2422,13 +2477,6 @@ export class CommandCenter { await repository.createStash(message, includeUntracked); } - private async getStashMessage(): Promise { - return await window.showInputBox({ - prompt: localize('provide stash message', "Optionally provide a stash message"), - placeHolder: localize('stash message', "Stash message") - }); - } - @command('git.stash', { repository: true }) stash(repository: Repository): Promise { return this._stash(repository); @@ -2496,6 +2544,16 @@ export class CommandCenter { return; } + // request confirmation for the operation + const yes = localize('yes', "Yes"); + const result = await window.showWarningMessage( + localize('sure drop', "Are you sure you want to drop the stash: {0}?", stash.description), + yes + ); + if (result !== yes) { + return; + } + await repository.dropStash(stash.index); } @@ -2614,6 +2672,31 @@ export class CommandCenter { const outputChannel = this.outputChannel as OutputChannel; choices.set(openOutputChannelChoice, () => outputChannel.show()); + const showCommandOutputChoice = localize('show command output', "Show Command Output"); + if (err.stderr) { + choices.set(showCommandOutputChoice, async () => { + const timestamp = new Date().getTime(); + const uri = Uri.parse(`git-output:/git-error-${timestamp}`); + + let command = 'git'; + + if (err.gitArgs) { + command = `${command} ${err.gitArgs.join(' ')}`; + } else if (err.gitCommand) { + command = `${command} ${err.gitCommand}`; + } + + this.commandErrors.set(uri, `> ${command}\n${err.stderr}`); + + try { + const doc = await workspace.openTextDocument(uri); + await window.showTextDocument(doc); + } finally { + this.commandErrors.delete(uri); + } + }); + } + switch (err.gitErrorCode) { case GitErrorCodes.DirtyWorkTree: message = localize('clean repo', "Please clean your repository working tree before checkout."); diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index a4fbf0ad..63c02605 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -15,18 +15,18 @@ class GitIgnoreDecorationProvider implements FileDecorationProvider { private static Decoration: FileDecoration = { color: new ThemeColor('gitDecoration.ignoredResourceForeground') }; - readonly onDidChange: Event; + readonly onDidChangeFileDecorations: Event; private queue = new Map>; }>(); private disposables: Disposable[] = []; constructor(private model: Model) { - this.onDidChange = fireEvent(anyEvent( + this.onDidChangeFileDecorations = fireEvent(anyEvent( filterEvent(workspace.onDidSaveTextDocument, e => /\.gitignore$|\.git\/info\/exclude$/.test(e.uri.path)), model.onDidOpenRepository, model.onDidCloseRepository )); - this.disposables.push(window.registerDecorationProvider(this)); + this.disposables.push(window.registerFileDecorationProvider(this)); } async provideFileDecoration(uri: Uri): Promise { @@ -93,14 +93,14 @@ class GitDecorationProvider implements FileDecorationProvider { }; private readonly _onDidChangeDecorations = new EventEmitter(); - readonly onDidChange: Event = this._onDidChangeDecorations.event; + readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; private disposables: Disposable[] = []; private decorations = new Map(); constructor(private repository: Repository) { this.disposables.push( - window.registerDecorationProvider(this), + window.registerFileDecorationProvider(this), repository.onDidRunGitStatus(this.onDidRunGitStatus, this) ); } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 1a411995..af536fe6 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -260,6 +260,7 @@ export interface IGitErrorData { exitCode?: number; gitErrorCode?: string; gitCommand?: string; + gitArgs?: string[]; } export class GitError { @@ -271,6 +272,7 @@ export class GitError { exitCode?: number; gitErrorCode?: string; gitCommand?: string; + gitArgs?: string[]; constructor(data: IGitErrorData) { if (data.error) { @@ -287,6 +289,7 @@ export class GitError { this.exitCode = data.exitCode; this.gitErrorCode = data.gitErrorCode; this.gitCommand = data.gitCommand; + this.gitArgs = data.gitArgs; } toString(): string { @@ -535,7 +538,8 @@ export class Git { stderr: result.stderr, exitCode: result.exitCode, gitErrorCode: getGitErrorCode(result.stderr), - gitCommand: args[0] + gitCommand: args[0], + gitArgs: args })); } @@ -1295,13 +1299,17 @@ export class Repository { await this.run(['update-index', add, '--cacheinfo', mode, hash, path]); } - async checkout(treeish: string, paths: string[], opts: { track?: boolean } = Object.create(null)): Promise { + async checkout(treeish: string, paths: string[], opts: { track?: boolean, detached?: boolean } = Object.create(null)): Promise { const args = ['checkout', '-q']; if (opts.track) { args.push('--track'); } + if (opts.detached) { + args.push('--detach'); + } + if (treeish) { args.push(treeish); } @@ -1317,23 +1325,30 @@ export class Repository { } catch (err) { if (/Please,? commit your changes or stash them/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.DirtyWorkTree; + err.gitTreeish = treeish; } throw err; } } - async commit(message: string, opts: CommitOptions = Object.create(null)): Promise { - const args = ['commit', '--quiet', '--allow-empty-message', '--file', '-']; + async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise { + const args = ['commit', '--quiet', '--allow-empty-message']; if (opts.all) { args.push('--all'); } - if (opts.amend) { + if (opts.amend && message) { args.push('--amend'); } + if (opts.amend && !message) { + args.push('--amend', '--no-edit'); + } else { + args.push('--file', '-'); + } + if (opts.signoff) { args.push('--signoff'); } @@ -1350,8 +1365,11 @@ export class Repository { args.push('--no-verify'); } + // Stops git from guessing at user/email + args.splice(0, 0, '-c', 'user.useConfigOnly=true'); + try { - await this.run(args, { input: message || '' }); + await this.run(args, !opts.amend || message ? { input: message || '' } : {}); } catch (commitErr) { await this.handleCommitError(commitErr); } @@ -1414,6 +1432,11 @@ export class Repository { await this.run(args); } + async move(from: string, to: string): Promise { + const args = ['mv', from, to]; + await this.run(args); + } + async setBranchUpstream(name: string, upstream: string): Promise { const args = ['branch', '--set-upstream-to', upstream, name]; await this.run(args); @@ -1536,9 +1559,11 @@ export class Repository { await this.run(args); } - async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean } = {}): Promise { + async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean, readonly cancellationToken?: CancellationToken } = {}): Promise { const args = ['fetch']; - const spawnOptions: SpawnOptions = {}; + const spawnOptions: SpawnOptions = { + cancellationToken: options.cancellationToken, + }; if (options.remote) { args.push(options.remote); @@ -1635,7 +1660,7 @@ export class Repository { } } - async push(remote?: string, name?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise { + async push(remote?: string, name?: string, setUpstream: boolean = false, followTags = false, forcePushMode?: ForcePushMode, tags = false): Promise { const args = ['push']; if (forcePushMode === ForcePushMode.ForceWithLease) { @@ -1648,10 +1673,14 @@ export class Repository { args.push('-u'); } - if (tags) { + if (followTags) { args.push('--follow-tags'); } + if (tags) { + args.push('--tags'); + } + if (remote) { args.push(remote); } @@ -1677,6 +1706,11 @@ export class Repository { } } + async cherryPick(commitHash: string): Promise { + const args = ['cherry-pick', commitHash]; + await this.run(args); + } + async blame(path: string): Promise { try { const args = ['blame', sanitizePath(path)]; @@ -1761,11 +1795,17 @@ export class Repository { } } - getStatus(limit = 5000): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> { + getStatus(opts?: { limit?: number, ignoreSubmodules?: boolean }): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> { return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => { const parser = new GitStatusParser(); const env = { GIT_OPTIONAL_LOCKS: '0' }; - const child = this.stream(['status', '-z', '-u'], { env }); + const args = ['status', '-z', '-u']; + + if (opts?.ignoreSubmodules) { + args.push('--ignore-submodules'); + } + + const child = this.stream(args, { env }); const onExit = (exitCode: number) => { if (exitCode !== 0) { @@ -1775,13 +1815,15 @@ export class Repository { stderr, exitCode, gitErrorCode: getGitErrorCode(stderr), - gitCommand: 'status' + gitCommand: 'status', + gitArgs: args })); } c({ status: parser.status, didHitLimit: false }); }; + const limit = opts?.limit ?? 5000; const onStdoutData = (raw: string) => { parser.update(raw); @@ -1845,7 +1887,7 @@ export class Repository { args.push('--sort', `-${opts.sort}`); } - args.push('--format', '%(refname) %(objectname)'); + args.push('--format', '%(refname) %(objectname) %(*objectname)'); if (opts?.pattern) { args.push(opts.pattern); @@ -1860,12 +1902,12 @@ export class Repository { const fn = (line: string): Ref | null => { let match: RegExpExecArray | null; - if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { + if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { return { name: match[1], commit: match[2], type: RefType.Head }; - } else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { + } else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { return { name: `${match[1]}/${match[2]}`, commit: match[3], type: RefType.RemoteHead, remote: match[1] }; - } else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40})$/.exec(line)) { - return { name: match[1], commit: match[2], type: RefType.Tag }; + } else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { + return { name: match[1], commit: match[3] ?? match[2], type: RefType.Tag }; } return null; diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index b736f606..cbe437d6 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -59,7 +59,8 @@ class RemoteSourceProviderQuickPick { this.quickpick.items = remoteSources.map(remoteSource => ({ label: remoteSource.name, description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), - remoteSource + remoteSource, + alwaysShow: true })); } } catch (err) { @@ -80,12 +81,30 @@ class RemoteSourceProviderQuickPick { export interface PickRemoteSourceOptions { readonly providerLabel?: (provider: RemoteSourceProvider) => string; readonly urlLabel?: string; + readonly providerName?: string; + readonly branch?: boolean; // then result is PickRemoteSourceResult } -export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { +export interface PickRemoteSourceResult { + readonly url: string; + readonly branch?: string; +} + +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch?: false | undefined }): Promise; +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch: true }): Promise; +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); quickpick.ignoreFocusOut = true; + if (options.providerName) { + const provider = model.getRemoteProviders() + .filter(provider => provider.name === options.providerName)[0]; + + if (provider) { + return await pickProviderSource(provider, options); + } + } + const providers = model.getRemoteProviders() .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); @@ -116,18 +135,48 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp if (result.url) { return result.url; } else if (result.provider) { - const quickpick = new RemoteSourceProviderQuickPick(result.provider); - const remote = await quickpick.pick(); - - if (remote) { - if (typeof remote.url === 'string') { - return remote.url; - } else if (remote.url.length > 0) { - return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); - } - } + return await pickProviderSource(result.provider, options); } } return undefined; } + +async function pickProviderSource(provider: RemoteSourceProvider, options: PickRemoteSourceOptions = {}): Promise { + const quickpick = new RemoteSourceProviderQuickPick(provider); + const remote = await quickpick.pick(); + + let url: string | undefined; + + if (remote) { + if (typeof remote.url === 'string') { + url = remote.url; + } else if (remote.url.length > 0) { + url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); + } + } + + if (!url || !options.branch) { + return url; + } + + if (!provider.getBranches) { + return { url }; + } + + const branches = await provider.getBranches(url); + + if (!branches) { + return { url }; + } + + const branch = await window.showQuickPick(branches, { + placeHolder: localize('branch name', "Branch name") + }); + + if (!branch) { + return { url }; + } + + return { url, branch }; +} diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 6b4fd018..87f24395 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -75,13 +75,21 @@ export class Resource implements SourceControlResourceState { return this._resourceUri; } - @memoize + get leftUri(): Uri | undefined { + return this.resources[0]; + } + + get rightUri(): Uri { + return this.resources[1]; + } + get command(): Command { - return { - command: 'git.openResource', - title: localize('open', "Open"), - arguments: [this] - }; + return this._commandResolver.resolveDefaultCommand(this); + } + + @memoize + private get resources(): [Uri | undefined, Uri] { + return this._commandResolver.getResources(this); } get resourceGroupType(): ResourceGroupType { return this._resourceGroupType; } @@ -262,12 +270,28 @@ export class Resource implements SourceControlResourceState { } constructor( + private _commandResolver: ResourceCommandResolver, private _resourceGroupType: ResourceGroupType, private _resourceUri: Uri, private _type: Status, private _useIcons: boolean, - private _renameResourceUri?: Uri + private _renameResourceUri?: Uri, ) { } + + async open(): Promise { + const command = this.command; + await commands.executeCommand(command.command, ...(command.arguments || [])); + } + + async openFile(): Promise { + const command = this._commandResolver.resolveFileCommand(this); + await commands.executeCommand(command.command, ...(command.arguments || [])); + } + + async openChange(): Promise { + const command = this._commandResolver.resolveChangeCommand(this); + await commands.executeCommand(command.command, ...(command.arguments || [])); + } } export const enum Operation { @@ -292,6 +316,7 @@ export const enum Operation { Fetch = 'Fetch', Pull = 'Pull', Push = 'Push', + CherryPick = 'CherryPick', Sync = 'Sync', Show = 'Show', Stage = 'Stage', @@ -315,6 +340,8 @@ export const enum Operation { Blame = 'Blame', Log = 'Log', LogFile = 'LogFile', + + Move = 'Move' } function isReadOnly(operation: Operation): boolean { @@ -550,6 +577,142 @@ class DotGitWatcher implements IFileWatcher { } } +class ResourceCommandResolver { + + constructor(private repository: Repository) { } + + resolveDefaultCommand(resource: Resource): Command { + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const openDiffOnClick = config.get('openDiffOnClick', true); + return openDiffOnClick ? this.resolveChangeCommand(resource) : this.resolveFileCommand(resource); + } + + resolveFileCommand(resource: Resource): Command { + return { + command: 'vscode.open', + title: localize('open', "Open"), + arguments: [resource.resourceUri] + }; + } + + resolveChangeCommand(resource: Resource): Command { + const title = this.getTitle(resource); + + if (!resource.leftUri) { + return { + command: 'vscode.open', + title: localize('open', "Open"), + arguments: [resource.rightUri, { override: resource.type === Status.BOTH_MODIFIED ? false : undefined }, title] + }; + } else { + return { + command: 'vscode.diff', + title: localize('open', "Open"), + arguments: [resource.leftUri, resource.rightUri, title] + }; + } + } + + getResources(resource: Resource): [Uri | undefined, Uri] { + for (const submodule of this.repository.submodules) { + if (path.join(this.repository.root, submodule.path) === resource.resourceUri.fsPath) { + return [undefined, toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: this.repository.root })]; + } + } + + return [this.getLeftResource(resource), this.getRightResource(resource)]; + } + + private getLeftResource(resource: Resource): Uri | undefined { + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_RENAMED: + case Status.INDEX_ADDED: + return toGitUri(resource.original, 'HEAD'); + + case Status.MODIFIED: + case Status.UNTRACKED: + return toGitUri(resource.resourceUri, '~'); + + case Status.DELETED_BY_US: + case Status.DELETED_BY_THEM: + return toGitUri(resource.resourceUri, '~1'); + } + return undefined; + } + + private getRightResource(resource: Resource): Uri { + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_ADDED: + case Status.INDEX_COPIED: + case Status.INDEX_RENAMED: + return toGitUri(resource.resourceUri, ''); + + case Status.INDEX_DELETED: + case Status.DELETED: + return toGitUri(resource.resourceUri, 'HEAD'); + + case Status.DELETED_BY_US: + return toGitUri(resource.resourceUri, '~3'); + + case Status.DELETED_BY_THEM: + return toGitUri(resource.resourceUri, '~2'); + + case Status.MODIFIED: + case Status.UNTRACKED: + case Status.IGNORED: + case Status.INTENT_TO_ADD: + const uriString = resource.resourceUri.toString(); + const [indexStatus] = this.repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); + + if (indexStatus && indexStatus.renameResourceUri) { + return indexStatus.renameResourceUri; + } + + return resource.resourceUri; + + case Status.BOTH_ADDED: + case Status.BOTH_MODIFIED: + return resource.resourceUri; + } + + throw new Error('Should never happen'); + } + + private getTitle(resource: Resource): string { + const basename = path.basename(resource.resourceUri.fsPath); + + switch (resource.type) { + case Status.INDEX_MODIFIED: + case Status.INDEX_RENAMED: + case Status.INDEX_ADDED: + return localize('git.title.index', '{0} (Index)', basename); + + case Status.MODIFIED: + case Status.BOTH_ADDED: + case Status.BOTH_MODIFIED: + return localize('git.title.workingTree', '{0} (Working Tree)', basename); + + case Status.INDEX_DELETED: + case Status.DELETED: + return localize('git.title.deleted', '{0} (Deleted)', basename); + + case Status.DELETED_BY_US: + return localize('git.title.theirs', '{0} (Theirs)', basename); + + case Status.DELETED_BY_THEM: + return localize('git.title.ours', '{0} (Ours)', basename); + + case Status.UNTRACKED: + return localize('git.title.untracked', '{0} (Untracked)', basename); + + default: + return ''; + } + } +} + export class Repository implements Disposable { private _onDidChangeRepository = new EventEmitter(); @@ -680,6 +843,7 @@ export class Repository implements Disposable { private isRepositoryHuge = false; private didWarnAboutLimit = false; + private resourceCommandResolver = new ResourceCommandResolver(this); private disposables: Disposable[] = []; constructor( @@ -746,11 +910,12 @@ export class Repository implements Disposable { onConfigListener(updateIndexGroupVisibility, this, this.disposables); updateIndexGroupVisibility(); - const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchSortOrder', root)); - onConfigListenerForBranchSortOrder(this.updateModelState, this, this.disposables); - - const onConfigListenerForUntracked = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.untrackedChanges', root)); - onConfigListenerForUntracked(this.updateModelState, this, this.disposables); + filterEvent(workspace.onDidChangeConfiguration, e => + e.affectsConfiguration('git.branchSortOrder', root) + || e.affectsConfiguration('git.untrackedChanges', root) + || e.affectsConfiguration('git.ignoreSubmodules', root) + || e.affectsConfiguration('git.openDiffOnClick', root) + )(this.updateModelState, this, this.disposables); const updateInputBoxVisibility = () => { const config = workspace.getConfiguration('git', root); @@ -864,6 +1029,12 @@ export class Repository implements Disposable { return; } + const path = uri.path; + + if (this.mergeGroup.resourceStates.some(r => r.resourceUri.path === path)) { + return undefined; + } + return toGitUri(uri, '', { replaceFileExtension: true }); } @@ -976,7 +1147,7 @@ export class Repository implements Disposable { await this.run(Operation.RevertFiles, () => this.repository.revert('HEAD', resources.map(r => r.fsPath))); } - async commit(message: string, opts: CommitOptions = Object.create(null)): Promise { + async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise { if (this.rebaseCommit) { await this.run(Operation.RebaseContinue, async () => { if (opts.all) { @@ -1053,6 +1224,14 @@ export class Repository implements Disposable { await this.run(Operation.RenameBranch, () => this.repository.renameBranch(name)); } + async cherryPick(commitHash: string): Promise { + await this.run(Operation.CherryPick, () => this.repository.cherryPick(commitHash)); + } + + async move(from: string, to: string): Promise { + await this.run(Operation.Move, () => this.repository.move(from, to)); + } + async getBranch(name: string): Promise { return await this.run(Operation.GetBranch, () => this.repository.getBranch(name)); } @@ -1081,12 +1260,12 @@ export class Repository implements Disposable { await this.run(Operation.DeleteTag, () => this.repository.deleteTag(name)); } - async checkout(treeish: string): Promise { - await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [])); + async checkout(treeish: string, opts?: { detached?: boolean }): Promise { + await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [], opts)); } - async checkoutTracking(treeish: string): Promise { - await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { track: true })); + async checkoutTracking(treeish: string, opts: { detached?: boolean } = {}): Promise { + await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { ...opts, track: true })); } async findTrackingBranches(upstreamRef: string): Promise { @@ -1119,21 +1298,31 @@ export class Repository implements Disposable { @throttle async fetchDefault(options: { silent?: boolean } = {}): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch(options)); + await this._fetch({ silent: options.silent }); } @throttle async fetchPrune(): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ prune: true })); + await this._fetch({ prune: true }); } @throttle async fetchAll(): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ all: true })); + await this._fetch({ all: true }); } async fetch(remote?: string, ref?: string, depth?: number): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ remote, ref, depth })); + await this._fetch({ remote, ref, depth }); + } + + private async _fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean } = {}): Promise { + if (!options.prune) { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const prune = config.get('pruneOnFetch'); + options.prune = prune; + } + + await this.run(Operation.Fetch, async () => this.repository.fetch(options)); } @throttle @@ -1169,11 +1358,12 @@ export class Repository implements Disposable { const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); + // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.repository.pull(rebase, undefined, undefined, { unshallow, tags }); - } else { - await this.repository.pull(rebase, remote, branch, { unshallow, tags }); + await this.repository.fetch({ all: true }); } + + await this.repository.pull(rebase, remote, branch, { unshallow, tags }); }); }); } @@ -1199,6 +1389,10 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this._push(remote, undefined, false, true, forcePushMode)); } + async pushTags(remote?: string, forcePushMode?: ForcePushMode): Promise { + await this.run(Operation.Push, () => this._push(remote, undefined, false, false, forcePushMode, true)); + } + async blame(path: string): Promise { return await this.run(Operation.Blame, () => this.repository.blame(path)); } @@ -1229,11 +1423,18 @@ export class Repository implements Disposable { const config = workspace.getConfiguration('git', Uri.file(this.root)); const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); + const followTags = config.get('followTagsWhenSync'); const supportCancellation = config.get('supportCancellation'); - const fn = fetchOnPull - ? async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, undefined, undefined, { tags, cancellationToken }) - : async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken }); + const fn = async (cancellationToken?: CancellationToken) => { + // When fetchOnPull is enabled, fetch all branches when pulling + if (fetchOnPull) { + await this.repository.fetch({ all: true, cancellationToken }); + } + + await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken }); + }; + if (supportCancellation) { const opts: ProgressOptions = { @@ -1256,7 +1457,7 @@ export class Repository implements Disposable { const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); if (shouldPush) { - await this._push(remoteName, pushBranch); + await this._push(remoteName, pushBranch, false, followTags); } }); }); @@ -1418,9 +1619,9 @@ export class Repository implements Disposable { return ignored; } - private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise { + private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, followTags = false, forcePushMode?: ForcePushMode, tags = false): Promise { try { - await this.repository.push(remote, refspec, setUpstream, tags, forcePushMode); + await this.repository.push(remote, refspec, setUpstream, followTags, forcePushMode, tags); } catch (err) { if (!remote || !refspec) { throw err; @@ -1518,9 +1719,12 @@ export class Repository implements Disposable { @throttle private async updateModelState(): Promise { - const { status, didHitLimit } = await this.repository.getStatus(); - const config = workspace.getConfiguration('git'); const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const ignoreSubmodules = scopedConfig.get('ignoreSubmodules'); + + const { status, didHitLimit } = await this.repository.getStatus({ ignoreSubmodules }); + + const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLimitWarning') === true; const useIcons = !config.get('decorations.enabled', true); this.isRepositoryHuge = didHitLimit; @@ -1595,36 +1799,36 @@ export class Repository implements Disposable { switch (raw.x + raw.y) { case '??': switch (untrackedChanges) { - case 'mixed': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.UNTRACKED, useIcons)); - case 'separate': return untracked.push(new Resource(ResourceGroupType.Untracked, uri, Status.UNTRACKED, useIcons)); + case 'mixed': return workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.UNTRACKED, useIcons)); + case 'separate': return untracked.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Untracked, uri, Status.UNTRACKED, useIcons)); default: return undefined; } case '!!': switch (untrackedChanges) { - case 'mixed': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.IGNORED, useIcons)); - case 'separate': return untracked.push(new Resource(ResourceGroupType.Untracked, uri, Status.IGNORED, useIcons)); + case 'mixed': return workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.IGNORED, useIcons)); + case 'separate': return untracked.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Untracked, uri, Status.IGNORED, useIcons)); default: return undefined; } - case 'DD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_DELETED, useIcons)); - case 'AU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_US, useIcons)); - case 'UD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_THEM, useIcons)); - case 'UA': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_THEM, useIcons)); - case 'DU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_US, useIcons)); - case 'AA': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_ADDED, useIcons)); - case 'UU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_MODIFIED, useIcons)); + case 'DD': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.BOTH_DELETED, useIcons)); + case 'AU': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.ADDED_BY_US, useIcons)); + case 'UD': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.DELETED_BY_THEM, useIcons)); + case 'UA': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.ADDED_BY_THEM, useIcons)); + case 'DU': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.DELETED_BY_US, useIcons)); + case 'AA': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.BOTH_ADDED, useIcons)); + case 'UU': return merge.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Merge, uri, Status.BOTH_MODIFIED, useIcons)); } switch (raw.x) { - case 'M': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); break; - case 'A': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_ADDED, useIcons)); break; - case 'D': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_DELETED, useIcons)); break; - case 'R': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_RENAMED, useIcons, renameUri)); break; - case 'C': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_COPIED, useIcons, renameUri)); break; + case 'M': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); break; + case 'A': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_ADDED, useIcons)); break; + case 'D': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_DELETED, useIcons)); break; + case 'R': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_RENAMED, useIcons, renameUri)); break; + case 'C': index.push(new Resource(this.resourceCommandResolver, ResourceGroupType.Index, uri, Status.INDEX_COPIED, useIcons, renameUri)); break; } switch (raw.y) { - case 'M': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; - case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; - case 'A': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; + case 'M': workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; + case 'D': workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; + case 'A': workingTree.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; } return undefined; diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index a315963a..6e240836 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -200,7 +200,7 @@ export class GitTimelineProvider implements TimelineProvider { if (working) { const date = new Date(); - const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommited Changes'), date.getTime(), 'working', 'git:file:working'); + const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommitted Changes'), date.getTime(), 'working', 'git:file:working'); // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new (ThemeIcon as any)('git-commit'); item.description = ''; diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index 98b715f8..b49942b5 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -40,6 +40,15 @@ export async function activate(context: vscode.ExtensionContext) { onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); return session; } catch (e) { + // If login was cancelled, do not notify user. + if (e.message === 'Cancelled') { + /* __GDPR__ + "loginCancelled" : { } + */ + telemetryReporter.sendTelemetryEvent('loginCancelled'); + throw e; + } + /* __GDPR__ "loginFailed" : { } */ diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index f28ac97c..d1cf45b7 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -180,9 +180,12 @@ export class GitHubAuthenticationProvider { } public async logout(id: string) { + Logger.info(`Logging out of ${id}`); const sessionIndex = this._sessions.findIndex(session => session.id === id); if (sessionIndex > -1) { this._sessions.splice(sessionIndex, 1); + } else { + Logger.error('Session not found'); } await this.storeSessions(); diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 55c0e4ef..b50285c9 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -23,7 +23,7 @@ class UriEventHandler extends vscode.EventEmitter implements vscode. export const uriHandler = new UriEventHandler; -const onDidManuallyProvideToken = new vscode.EventEmitter(); +const onDidManuallyProvideToken = new vscode.EventEmitter(); @@ -91,7 +91,7 @@ export class GitHubServer { return Promise.race([ existingPromise, - promiseFromEvent(onDidManuallyProvideToken.event) + promiseFromEvent(onDidManuallyProvideToken.event, (token: string | undefined): string => { if (!token) { throw new Error('Cancelled'); } return token; }) ]).finally(() => { this._pendingStates.delete(scopes); this._codeExchangePromises.delete(scopes); @@ -147,7 +147,11 @@ export class GitHubServer { public async manuallyProvideToken() { const uriOrToken = await vscode.window.showInputBox({ prompt: 'Token', ignoreFocusOut: true }); - if (!uriOrToken) { return; } + if (!uriOrToken) { + onDidManuallyProvideToken.fire(undefined); + return; + } + try { const uri = vscode.Uri.parse(uriOrToken); if (!uri.scheme || uri.scheme === 'file') { throw new Error; } diff --git a/extensions/github/src/publish.ts b/extensions/github/src/publish.ts index 0abb606b..cf8f306c 100644 --- a/extensions/github/src/publish.ts +++ b/extensions/github/src/publish.ts @@ -9,6 +9,7 @@ import { API as GitAPI, Repository } from './typings/git'; import { getOctokit } from './auth'; import { TextEncoder } from 'util'; import { basename } from 'path'; +import { Octokit } from '@octokit/rest'; const localize = nls.loadMessageBundle(); @@ -57,9 +58,18 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) quickpick.show(); quickpick.busy = true; - const octokit = await getOctokit(); - const user = await octokit.users.getAuthenticated({}); - const owner = user.data.login; + let owner: string; + let octokit: Octokit; + try { + octokit = await getOctokit(); + const user = await octokit.users.getAuthenticated({}); + owner = user.data.login; + } catch (e) { + // User has cancelled sign in + quickpick.dispose(); + return; + } + quickpick.busy = false; let repo: string | undefined; @@ -139,7 +149,7 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) new Promise(c => quickpick.onDidHide(() => c(undefined))) ]); - if (!result) { + if (!result || result.length === 0) { return; } @@ -192,9 +202,9 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) } const openInGitHub = 'Open In GitHub'; - const action = await vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub); - - if (action === openInGitHub) { - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url)); - } + vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub).then(action => { + if (action === openInGitHub) { + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url)); + } + }); } diff --git a/extensions/github/src/pushErrorHandler.ts b/extensions/github/src/pushErrorHandler.ts index a08a8d45..0b2f3b9d 100644 --- a/extensions/github/src/pushErrorHandler.ts +++ b/extensions/github/src/pushErrorHandler.ts @@ -40,8 +40,14 @@ async function handlePushError(repository: Repository, remote: Remote, refspec: // Issue: what if there's already another `origin` repo? await repository.addRemote('origin', ghRepository.clone_url); - await repository.fetch('origin', remoteName); - await repository.setBranchUpstream(localName, `origin/${remoteName}`); + + try { + await repository.fetch('origin', remoteName); + await repository.setBranchUpstream(localName, `origin/${remoteName}`); + } catch { + // noop + } + await repository.push('origin', localName, true); return [octokit, ghRepository]; diff --git a/extensions/github/src/remoteSourceProvider.ts b/extensions/github/src/remoteSourceProvider.ts index 9d0ab4c5..17b15300 100644 --- a/extensions/github/src/remoteSourceProvider.ts +++ b/extensions/github/src/remoteSourceProvider.ts @@ -8,6 +8,12 @@ import { getOctokit } from './auth'; import { Octokit } from '@octokit/rest'; import { publishRepository } from './publish'; +function parse(url: string): { owner: string, repo: string } | undefined { + const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(url) + || /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(url); + return (match && { owner: match[1], repo: match[2] }) ?? undefined; +} + function asRemoteSource(raw: any): RemoteSource { return { name: `$(github) ${raw.full_name}`, @@ -28,17 +34,30 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { async getRemoteSources(query?: string): Promise { const octokit = await getOctokit(); - const [fromUser, fromQuery] = await Promise.all([ + + if (query) { + const repository = parse(query); + + if (repository) { + const raw = await octokit.repos.get(repository); + return [asRemoteSource(raw.data)]; + } + } + + const all = await Promise.all([ this.getUserRemoteSources(octokit, query), this.getQueryRemoteSources(octokit, query) ]); - const userRepos = new Set(fromUser.map(r => r.name)); + const map = new Map(); - return [ - ...fromUser, - ...fromQuery.filter(r => !userRepos.has(r.name)) - ]; + for (const group of all) { + for (const remoteSource of group) { + map.set(remoteSource.name, remoteSource); + } + } + + return [...map.values()]; } private async getUserRemoteSources(octokit: Octokit, query?: string): Promise { @@ -61,6 +80,35 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { return raw.data.items.map(asRemoteSource); } + async getBranches(url: string): Promise { + const repository = parse(url); + + if (!repository) { + return []; + } + + const octokit = await getOctokit(); + + const branches: string[] = []; + let page = 1; + + while (true) { + let res = await octokit.repos.listBranches({ ...repository, per_page: 100, page }); + + if (res.data.length === 0) { + break; + } + + branches.push(...res.data.map(b => b.name)); + page++; + } + + const repo = await octokit.repos.get(repository); + const defaultBranch = repo.data.default_branch; + + return branches.sort((a, b) => a === defaultBranch ? -1 : b === defaultBranch ? 1 : 0); + } + publishRepository(repository: Repository): Promise { return publishRepository(this.gitAPI, repository); } diff --git a/extensions/github/src/typings/git.d.ts b/extensions/github/src/typings/git.d.ts index 54f21b96..b9a09632 100644 --- a/extensions/github/src/typings/git.d.ts +++ b/extensions/github/src/typings/git.d.ts @@ -130,6 +130,7 @@ export interface CommitOptions { signoff?: boolean; signCommit?: boolean; empty?: boolean; + noVerify?: boolean; } export interface BranchQuery { @@ -211,6 +212,7 @@ export interface RemoteSourceProvider { readonly icon?: string; // codicon name readonly supportsQuery?: boolean; getRemoteSources(query?: string): ProviderResult; + getBranches?(url: string): ProviderResult; publishRepository?(repository: Repository): Promise; } diff --git a/extensions/html-language-features/client/src/htmlClient.ts b/extensions/html-language-features/client/src/htmlClient.ts index 6917b569..494b935a 100644 --- a/extensions/html-language-features/client/src/htmlClient.ts +++ b/extensions/html-language-features/client/src/htmlClient.ts @@ -27,8 +27,8 @@ namespace CustomDataChangedNotification { namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } -namespace OnTypeRenameRequest { - export const type: RequestType = new RequestType('html/onTypeRename'); +namespace LinkedEditingRequest { + export const type: RequestType = new RequestType('html/linkedEditing'); } // experimental: semantic tokens @@ -44,7 +44,7 @@ namespace SemanticTokenLegendRequest { } namespace SettingIds { - export const renameOnType = 'editor.renameOnType'; + export const linkedRename = 'editor.linkedRename'; export const formatEnable = 'html.format.enable'; } @@ -169,10 +169,10 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua } }); - disposable = languages.registerOnTypeRenameProvider(documentSelector, { - async provideOnTypeRenameRanges(document, position) { + disposable = languages.registerLinkedEditingRangeProvider(documentSelector, { + async provideLinkedEditingRanges(document, position) { const param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); - return client.sendRequest(OnTypeRenameRequest.type, param).then(response => { + return client.sendRequest(LinkedEditingRequest.type, param).then(response => { if (response) { return { ranges: response.map(r => client.protocol2CodeConverter.asRange(r)) @@ -301,7 +301,7 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua const promptForTypeOnRenameKey = 'html.promptForTypeOnRename'; const promptForTypeOnRename = extensions.getExtension('formulahendry.auto-rename-tag') !== undefined && (context.globalState.get(promptForTypeOnRenameKey) !== false) && - !workspace.getConfiguration('editor', { languageId: 'html' }).get('renameOnType'); + !workspace.getConfiguration('editor', { languageId: 'html' }).get('linkedRename'); if (promptForTypeOnRename) { const activeEditorListener = window.onDidChangeActiveTextEditor(async e => { @@ -309,9 +309,9 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua context.globalState.update(promptForTypeOnRenameKey, false); activeEditorListener.dispose(); const configure = localize('configureButton', 'Configure'); - const res = await window.showInformationMessage(localize('renameOnTypeQuestion', 'VS Code now has built-in support for auto-renaming tags. Do you want to enable it?'), configure); + const res = await window.showInformationMessage(localize('linkedRenameQuestion', 'VS Code now has built-in support for auto-renaming tags. Do you want to enable it?'), configure); if (res === configure) { - commands.executeCommand('workbench.action.openSettings', SettingIds.renameOnType); + commands.executeCommand('workbench.action.openSettings', SettingIds.linkedRename); } } }); diff --git a/extensions/html-language-features/client/src/node/htmlClientMain.ts b/extensions/html-language-features/client/src/node/htmlClientMain.ts index c58515b3..097bda1f 100644 --- a/extensions/html-language-features/client/src/node/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/node/htmlClientMain.ts @@ -24,7 +24,7 @@ export function activate(context: ExtensionContext) { const serverModule = context.asAbsolutePath(serverMain); // The debug options for the server - const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (8000 + Math.round(Math.random() * 999))] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 204ae49b..c2c211cc 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -138,6 +138,31 @@ ], "description": "%html.format.wrapAttributes.desc%" }, + "html.format.wrapAttributesIndentSize": { + "type": [ + "number", + "null" + ], + "scope": "resource", + "default": null, + "description": "%html.format.wrapAttributesIndentSize.desc%" + }, + "html.format.templating": { + "type": [ + "boolean" + ], + "scope": "resource", + "default": false, + "description": "%html.format.templating.desc%" + }, + "html.format.unformattedContentDelimiter": { + "type": [ + "string" + ], + "scope": "resource", + "default": "", + "markdownDescription": "%html.format.unformattedContentDelimiter.desc%" + }, "html.suggest.html5": { "type": "boolean", "scope": "resource", @@ -162,6 +187,18 @@ "default": true, "description": "%html.autoClosingTags%" }, + "html.hover.documentation": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%html.hover.documentation%" + }, + "html.hover.references": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%html.hover.references%" + }, "html.mirrorCursorOnMatchingTag": { "type": "boolean", "scope": "resource", @@ -204,7 +241,7 @@ "dependencies": { "vscode-extension-telemetry": "0.1.1", "vscode-languageclient": "7.0.0-next.5.1", - "vscode-nls": "^4.1.2" + "vscode-nls": "^5.0.0" }, "devDependencies": { "@types/node": "^12.11.7" diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index ff581502..49c63209 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -20,11 +20,16 @@ "html.format.wrapAttributes.alignedmultiple": "Wrap when line length is exceeded, align attributes vertically.", "html.format.wrapAttributes.preserve": "Preserve wrapping of attributes", "html.format.wrapAttributes.preservealigned": "Preserve wrapping of attributes but align.", + "html.format.templating.desc": "Honor django, erb, handlebars and php templating language tags.", + "html.format.unformattedContentDelimiter.desc": "Keep text content together between this string.", + "html.format.wrapAttributesIndentSize.desc": "Alignment size when using 'force aligned' and 'aligned multiple' in `#html.format.wrapAttributes#` or `null` to use the default indent size.", "html.suggest.html5.desc": "Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.", "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", "html.validate.styles": "Controls whether the built-in HTML language support validates embedded styles.", "html.autoClosingTags": "Enable/disable autoclosing of HTML tags.", "html.mirrorCursorOnMatchingTag": "Enable/disable mirroring cursor on matching HTML tag.", - "html.mirrorCursorOnMatchingTagDeprecationMessage": "Deprecated in favor of `editor.renameOnType`" + "html.mirrorCursorOnMatchingTagDeprecationMessage": "Deprecated in favor of `editor.linkedEditing`", + "html.hover.documentation": "Show tag and attribute documentation in hover.", + "html.hover.references": "Show references to MDN in hover." } diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 62bf4176..65ce7cc7 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/node/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.3.5", - "vscode-html-languageservice": "^3.1.4", + "vscode-css-languageservice": "^4.4.0", + "vscode-html-languageservice": "^3.2.0", "vscode-languageserver": "7.0.0-next.3", "vscode-nls": "^5.0.0", "vscode-uri": "^2.1.2" diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 1ddfef6c..7b76a857 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -33,8 +33,8 @@ namespace CustomDataChangedNotification { namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } -namespace OnTypeRenameRequest { - export const type: RequestType = new RequestType('html/onTypeRename'); +namespace LinkedEditingRequest { + export const type: RequestType = new RequestType('html/linkedEditing'); } // experimental: semantic tokens @@ -284,7 +284,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) if (!mode || !mode.doComplete) { return { isIncomplete: true, items: [] }; } - const doComplete = mode.doComplete!; + const doComplete = mode.doComplete; if (mode.getId() !== 'html') { /* __GDPR__ @@ -321,8 +321,10 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const document = documents.get(textDocumentPosition.textDocument.uri); if (document) { const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); - if (mode && mode.doHover) { - return mode.doHover(document, textDocumentPosition.position); + const doHover = mode?.doHover; + if (doHover) { + const settings = await getDocumentSettings(document, () => doHover.length > 2); + return doHover(document, textDocumentPosition.position, settings); } } return null; @@ -508,15 +510,15 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, null, `Error while computing rename for ${params.textDocument.uri}`, token); }); - connection.onRequest(OnTypeRenameRequest.type, (params, token) => { + connection.onRequest(LinkedEditingRequest.type, (params, token) => { return runSafe(async () => { const document = documents.get(params.textDocument.uri); if (document) { const pos = params.position; if (pos.character > 0) { const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); - if (mode && mode.doOnTypeRename) { - return mode.doOnTypeRename(document, pos); + if (mode && mode.doLinkedEditing) { + return mode.doLinkedEditing(document, pos); } } } diff --git a/extensions/html-language-features/server/src/modes/cssMode.ts b/extensions/html-language-features/server/src/modes/cssMode.ts index 9b3ddd9b..f7c5455b 100644 --- a/extensions/html-language-features/server/src/modes/cssMode.ts +++ b/extensions/html-language-features/server/src/modes/cssMode.ts @@ -5,7 +5,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { Stylesheet, LanguageService as CSSLanguageService } from 'vscode-css-languageservice'; -import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext } from './languageModes'; +import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext, Settings } from './languageModes'; import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport'; export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode { @@ -25,9 +25,9 @@ export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegio const stylesheet = cssStylesheets.get(embedded); return cssLanguageService.doComplete2(embedded, position, stylesheet, documentContext) || CompletionList.create(); }, - async doHover(document: TextDocument, position: Position) { + async doHover(document: TextDocument, position: Position, settings?: Settings) { let embedded = embeddedCSSDocuments.get(document); - return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded)); + return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded), settings?.html?.hover); }, async findDocumentHighlight(document: TextDocument, position: Position) { let embedded = embeddedCSSDocuments.get(document); diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 96462f8e..a8a17ca8 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -8,7 +8,7 @@ import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration, SelectionRange, TextDocument, Position, Range, FoldingRange, - LanguageMode, Workspace + LanguageMode, Workspace, Settings } from './languageModes'; export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: Workspace): LanguageMode { @@ -31,8 +31,8 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: let completionList = htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext, options); return completionList; }, - async doHover(document: TextDocument, position: Position) { - return htmlLanguageService.doHover(document, position, htmlDocuments.get(document)); + async doHover(document: TextDocument, position: Position, settings?: Settings) { + return htmlLanguageService.doHover(document, position, htmlDocuments.get(document), settings?.html?.hover); }, async findDocumentHighlight(document: TextDocument, position: Position) { return htmlLanguageService.findDocumentHighlights(document, position, htmlDocuments.get(document)); @@ -80,9 +80,9 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: const htmlDocument = htmlDocuments.get(document); return htmlLanguageService.findMatchingTagPosition(document, position, htmlDocument); }, - async doOnTypeRename(document: TextDocument, position: Position) { + async doLinkedEditing(document: TextDocument, position: Position) { const htmlDocument = htmlDocuments.get(document); - return htmlLanguageService.findOnTypeRenameRanges(document, position, htmlDocument); + return htmlLanguageService.findLinkedEditingRanges(document, position, htmlDocument); }, dispose() { htmlDocuments.dispose(); diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 086b8a90..1fc33fae 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -6,7 +6,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, - Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, + Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind, SelectionRange, LanguageMode, Settings, SemanticTokenData, Workspace, DocumentContext } from './languageModes'; @@ -142,10 +142,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache Promise; doComplete?: (document: TextDocument, position: Position, documentContext: DocumentContext, settings?: Settings) => Promise; doResolve?: (document: TextDocument, item: CompletionItem) => Promise; - doHover?: (document: TextDocument, position: Position) => Promise; + doHover?: (document: TextDocument, position: Position, settings?: Settings) => Promise; doSignatureHelp?: (document: TextDocument, position: Position) => Promise; doRename?: (document: TextDocument, position: Position, newName: string) => Promise; - doOnTypeRename?: (document: TextDocument, position: Position) => Promise; + doLinkedEditing?: (document: TextDocument, position: Position) => Promise; findDocumentHighlight?: (document: TextDocument, position: Position) => Promise; findDocumentSymbols?: (document: TextDocument) => Promise; findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => Promise; diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts index 6d815748..548ce7d8 100644 --- a/extensions/html-language-features/server/src/test/semanticTokens.test.ts +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -87,8 +87,8 @@ suite('HTML Semantic Tokens', () => { ]; await assertTokens(input, [ t(3, 11, 3, 'function.declaration'), t(3, 15, 2, 'parameter.declaration'), - t(4, 11, 3, 'function'), t(4, 15, 4, 'interface'), t(4, 20, 3, 'member'), t(4, 24, 2, 'parameter'), - t(6, 6, 6, 'variable'), t(6, 13, 8, 'property'), t(6, 24, 5, 'member'), t(6, 35, 7, 'member'), t(6, 43, 1, 'parameter.declaration'), t(6, 48, 3, 'function'), t(6, 52, 1, 'parameter') + t(4, 11, 3, 'function'), t(4, 15, 4, 'interface'), t(4, 20, 3, 'method'), t(4, 24, 2, 'parameter'), + t(6, 6, 6, 'variable'), t(6, 13, 8, 'property'), t(6, 24, 5, 'method'), t(6, 35, 7, 'method'), t(6, 43, 1, 'parameter.declaration'), t(6, 48, 3, 'function'), t(6, 52, 1, 'parameter') ]); }); @@ -115,9 +115,9 @@ suite('HTML Semantic Tokens', () => { t(3, 8, 1, 'class.declaration'), t(4, 11, 1, 'property.declaration.static'), t(5, 4, 1, 'property.declaration'), - t(6, 10, 1, 'member.declaration.async'), t(6, 23, 1, 'class'), t(6, 25, 1, 'property.static'), t(6, 40, 1, 'member.async'), + t(6, 10, 1, 'method.declaration.async'), t(6, 23, 1, 'class'), t(6, 25, 1, 'property.static'), t(6, 40, 1, 'method.async'), t(7, 8, 1, 'property.declaration'), t(7, 26, 1, 'property'), - t(8, 11, 1, 'member.declaration.static'), t(8, 28, 1, 'class'), t(8, 32, 1, 'property'), + t(8, 11, 1, 'method.declaration.static'), t(8, 28, 1, 'class'), t(8, 32, 1, 'property'), ]); }); @@ -157,7 +157,7 @@ suite('HTML Semantic Tokens', () => { t(3, 8, 1, 'variable.declaration.readonly'), t(4, 8, 1, 'class.declaration'), t(4, 28, 1, 'property.declaration.static.readonly'), t(4, 42, 3, 'property.declaration.static'), t(4, 47, 3, 'interface'), t(5, 13, 1, 'enum.declaration'), t(5, 17, 1, 'property.declaration.readonly'), t(5, 24, 1, 'property.declaration.readonly'), t(5, 28, 1, 'property.readonly'), - t(6, 2, 7, 'variable'), t(6, 10, 3, 'member'), t(6, 14, 1, 'variable.readonly'), t(6, 18, 1, 'class'), t(6, 20, 1, 'property.static.readonly'), t(6, 24, 1, 'class'), t(6, 26, 3, 'property.static'), t(6, 30, 6, 'property.readonly'), + t(6, 2, 7, 'variable'), t(6, 10, 3, 'method'), t(6, 14, 1, 'variable.readonly'), t(6, 18, 1, 'class'), t(6, 20, 1, 'property.static.readonly'), t(6, 24, 1, 'class'), t(6, 26, 3, 'property.static'), t(6, 30, 6, 'property.readonly'), ]); }); @@ -197,7 +197,7 @@ suite('HTML Semantic Tokens', () => { ]; await assertTokens(input, [ t(3, 11, 1, 'function.declaration'), t(3, 13, 1, 'typeParameter.declaration'), t(3, 16, 2, 'parameter.declaration'), t(3, 20, 1, 'typeParameter'), t(3, 24, 1, 'typeParameter'), t(3, 39, 2, 'parameter'), - t(6, 2, 6, 'variable'), t(6, 9, 5, 'member') + t(6, 2, 6, 'variable'), t(6, 9, 5, 'method') ]); }); @@ -215,7 +215,7 @@ suite('HTML Semantic Tokens', () => { /*9*/'', ]; await assertTokens(input, [ - t(3, 2, 6, 'variable'), t(3, 9, 5, 'member') + t(3, 2, 6, 'variable'), t(3, 9, 5, 'method') ], [Range.create(Position.create(2, 0), Position.create(4, 0))]); await assertTokens(input, [ diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 2bb5bba8..6d17972d 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -880,20 +880,20 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.5.tgz#92f8817057dee7c381df2289aad539c7b553548a" - integrity sha512-g9Pjxt9T32jhY0nTOo7WRFm0As27IfdaAxcFa8c7Rml1ZqBn3XXbkExjzxY7sBWYm7I1Tp4dK6UHXHoUQHGwig== +vscode-css-languageservice@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.4.0.tgz#a7c5edf3057e707601ca18fa3728784a298513b4" + integrity sha512-jWi+297PJUUWTHwlcrZz0zIuEXuHOBJIQMapXmEzbosWGv/gMnNSAMV4hTKnl5wzxvZKZzV6j+WFdrSlKQ5qnw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" vscode-nls "^5.0.0" vscode-uri "^2.1.2" -vscode-html-languageservice@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.4.tgz#0316dff77ee38dc176f40560cbf55e4f64f4f433" - integrity sha512-3M+bm+hNvwQcScVe5/ok9BXvctOiGJ4nlOkkFf+WKSDrYNkarZ/RByKOa1/iylbvZxJUPzbeziembWPe/dMvhw== +vscode-html-languageservice@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.2.0.tgz#e92269a04097d87bd23431e3a4e491a27b5447b9" + integrity sha512-aLWIoWkvb5HYTVE0kI9/u3P0ZAJGrYOSAAE6L0wqB9radKRtbJNrF9+BjSUFyCgBdNBE/GFExo35LoknQDJrfw== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 0d4b9507..7d639c7b 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -71,10 +71,10 @@ vscode-languageserver-types@3.16.0-next.2: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-nls@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" - integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== zone.js@0.7.6: version "0.7.6" diff --git a/extensions/javascript/snippets/javascript.code-snippets b/extensions/javascript/snippets/javascript.code-snippets index b005c80c..a48a64c2 100644 --- a/extensions/javascript/snippets/javascript.code-snippets +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -35,7 +35,7 @@ "prefix": "forin", "body": [ "for (const ${1:key} in ${2:object}) {", - "\tif (${2:object}.hasOwnProperty(${1:key})) {", + "\tif (Object.hasOwnProperty.call(${2:object}, ${1:key})) {", "\t\tconst ${3:element} = ${2:object}[${1:key}];", "\t\t$0", "\t}", diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 9a991cde..c729c8ff 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/59b62cbc624e3a01368e5432d85af0c99a27d49d", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -128,8 +128,18 @@ "match": "(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" @@ -487,7 +497,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js variable.other.constant.js entity.name.function.js" @@ -871,7 +881,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -1111,7 +1121,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js entity.name.function.js" @@ -1251,6 +1261,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1434,7 +1447,7 @@ }, { "name": "meta.arrow.js", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -2469,7 +2482,7 @@ }, { "name": "string.regexp.js", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.js" @@ -2628,7 +2641,7 @@ }, { "name": "meta.object.member.js", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js" @@ -2719,7 +2732,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -2752,7 +2765,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -2788,7 +2801,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.js" @@ -2976,7 +2989,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -3060,7 +3073,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -3628,7 +3641,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3840,7 +3853,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js meta.return.type.arrow.js keyword.operator.type.annotation.js" @@ -4159,7 +4172,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.js" @@ -4677,7 +4690,7 @@ }, { "name": "string.regexp.js", - "begin": "((?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx entity.name.function.js.jsx" @@ -487,7 +497,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js.jsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx variable.other.constant.js.jsx entity.name.function.js.jsx" @@ -871,7 +881,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -1111,7 +1121,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js.jsx entity.name.function.js.jsx" @@ -1251,6 +1261,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1434,7 +1447,7 @@ }, { "name": "meta.arrow.js.jsx", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -2469,7 +2482,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.js.jsx" @@ -2628,7 +2641,7 @@ }, { "name": "meta.object.member.js.jsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js.jsx" @@ -2719,7 +2732,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -2752,7 +2765,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -2788,7 +2801,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.js.jsx" @@ -2976,7 +2989,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -3060,7 +3073,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -3628,7 +3641,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3840,7 +3853,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx keyword.operator.type.annotation.js.jsx" @@ -4159,7 +4172,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -4677,7 +4690,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "((? { - window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`); + client.onNotification(ResultLimitReachedNotification.type, async message => { + const shouldPrompt = context.globalState.get(StorageIds.maxItemsExceededInformation) !== false; + if (shouldPrompt) { + const ok = localize('ok', "Ok"); + const openSettings = localize('goToSetting', 'Open Settings'); + const neverAgain = localize('yes never again', "Don't Show Again"); + const pick = await window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`, ok, openSettings, neverAgain); + if (pick === neverAgain) { + await context.globalState.update(StorageIds.maxItemsExceededInformation, false); + } else if (pick === openSettings) { + await commands.executeCommand('workbench.action.openSettings', SettingIds.maxItemsComputed); + } + } }); function updateFormatterRegistration() { @@ -315,6 +330,8 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua range: client.code2ProtocolConverter.asRange(range), options: client.code2ProtocolConverter.asFormattingOptions(options) }; + params.options.insertFinalNewline = workspace.getConfiguration('files', document).get('insertFinalNewline'); + return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( client.protocol2CodeConverter.asTextEdits, (error) => { diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts index 6e9aed7f..eba26913 100644 --- a/extensions/json-language-features/client/src/node/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -25,7 +25,7 @@ export function activate(context: ExtensionContext) { const serverModule = context.asAbsolutePath(serverMain); // The debug options for the server - const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (6000 + Math.round(Math.random() * 999))] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index db003f05..a8e3231a 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -127,10 +127,10 @@ ] }, "dependencies": { - "request-light": "^0.3.0", + "request-light": "^0.4.0", "vscode-extension-telemetry": "0.1.1", "vscode-languageclient": "7.0.0-next.5.1", - "vscode-nls": "^4.1.2" + "vscode-nls": "^5.0.0" }, "devDependencies": { "@types/node": "^12.11.7" diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 59729a0e..9315f7f2 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -13,5 +13,6 @@ "json.schemaResolutionErrorMessage": "Unable to resolve schema.", "json.clickToRetry": "Click to retry.", "json.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).", + "json.maxItemsExceededInformation.desc": "Show notification when exceeding the maximum number of outline symbols and folding regions.", "json.enableSchemaDownload.desc": "When enabled, JSON schemas can be fetched from http and https locations." } diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index cb0dded1..86df3228 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,9 +12,9 @@ }, "main": "./out/node/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.2.1", - "request-light": "^0.3.0", - "vscode-json-languageservice": "^3.9.1", + "jsonc-parser": "^3.0.0", + "request-light": "^0.4.0", + "vscode-json-languageservice": "^3.11.0", "vscode-languageserver": "7.0.0-next.3", "vscode-uri": "^2.1.2" }, diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 26191bc4..e7d2526f 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -141,7 +141,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, - definitionProvider: true + documentLinkProvider: {} }; return { capabilities }; @@ -481,15 +481,15 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); }); - connection.onDefinition((params, token) => { + connection.onDocumentLinks((params, token) => { return runSafeAsync(async () => { const document = documents.get(params.textDocument.uri); if (document) { const jsonDocument = getJSONDocument(document); - return languageService.findDefinition(document, params.position, jsonDocument); + return languageService.findLinks(document, jsonDocument); } return []; - }, [], `Error while computing definitions for ${params.textDocument.uri}`, token); + }, [], `Error while computing links for ${params.textDocument.uri}`, token); }); // Listen on the connection diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index c438b03d..ec82f87b 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -61,36 +61,31 @@ https-proxy-agent@^2.2.4: agent-base "^4.3.0" debug "^3.1.0" -jsonc-parser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" - integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== - -jsonc-parser@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" - integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== +jsonc-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -request-light@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.3.0.tgz#04daa783e7f0a70392328dda4b546f3e27845f2d" - integrity sha512-xlVlZVT0ZvCT+c3zm3SjeFCzchoQxsUUmx5fkal0I6RIDJK+lmb1UYyKJ7WM4dTfnzHP4ElWwAf8Dli8c0/tVA== +request-light@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.4.0.tgz#c6b91ef00b18cb0de75d2127e55b3a2c9f7f90f9" + integrity sha512-fimzjIVw506FBZLspTAXHdpvgvQebyjpNyLRd0e6drPPRq7gcrROeGWRyF81wLqFg5ijPgnOQbmfck5wdTqpSA== dependencies: http-proxy-agent "^2.1.0" https-proxy-agent "^2.2.4" - vscode-nls "^4.1.1" + vscode-nls "^4.1.2" -vscode-json-languageservice@^3.9.1: - version "3.9.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.9.1.tgz#f72b581f8cd2bd9b47445ccf8b0ddcde6aba7483" - integrity sha512-oJkknkdCVitQ5XPSRa0weHjUxt8eSCptaL+MBQQlRsa6Nb8XnEY0S5wYnLUFHzEvKzwt01/LKk8LdOixWEXkNA== +vscode-json-languageservice@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.11.0.tgz#ad574b36c4346bd7830f1d34b5a5213d3af8d232" + integrity sha512-QxI+qV97uD7HHOCjh3MrM1TfbdwmTXrMckri5Tus1/FQiG3baDZb2C9Y0y8QThs7PwHYBIQXcAc59ZveCRZKPA== dependencies: - jsonc-parser "^2.3.1" + jsonc-parser "^3.0.0" vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "3.16.0-next.2" vscode-nls "^5.0.0" @@ -126,10 +121,10 @@ vscode-languageserver@7.0.0-next.3: dependencies: vscode-languageserver-protocol "3.16.0-next.4" -vscode-nls@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== +vscode-nls@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== vscode-nls@^5.0.0: version "5.0.0" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 29972b7d..a1e87bdb 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -94,14 +94,14 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -request-light@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.3.0.tgz#04daa783e7f0a70392328dda4b546f3e27845f2d" - integrity sha512-xlVlZVT0ZvCT+c3zm3SjeFCzchoQxsUUmx5fkal0I6RIDJK+lmb1UYyKJ7WM4dTfnzHP4ElWwAf8Dli8c0/tVA== +request-light@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.4.0.tgz#c6b91ef00b18cb0de75d2127e55b3a2c9f7f90f9" + integrity sha512-fimzjIVw506FBZLspTAXHdpvgvQebyjpNyLRd0e6drPPRq7gcrROeGWRyF81wLqFg5ijPgnOQbmfck5wdTqpSA== dependencies: http-proxy-agent "^2.1.0" https-proxy-agent "^2.2.4" - vscode-nls "^4.1.1" + vscode-nls "^4.1.2" semver@^5.3.0: version "5.5.0" @@ -146,16 +146,16 @@ vscode-languageserver-types@3.16.0-next.2: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-nls@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== - vscode-nls@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + zone.js@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/extensions/json/cgmanifest.json b/extensions/json/cgmanifest.json index 53db2000..1af8426e 100644 --- a/extensions/json/cgmanifest.json +++ b/extensions/json/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/json/package.json b/extensions/json/package.json index 73902a8e..1b7331b2 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -33,8 +33,7 @@ ], "filenames": [ "composer.lock", - ".watchmanconfig", - ".ember-cli" + ".watchmanconfig" ], "mimetypes": [ "application/json", @@ -57,6 +56,9 @@ ".hintrc", ".babelrc" ], + "filenames": [ + ".ember-cli" + ], "configuration": "./language-configuration.json" } ], diff --git a/extensions/json/syntaxes/JSON.tmLanguage.json b/extensions/json/syntaxes/JSON.tmLanguage.json index 9454f0ed..b53febdc 100644 --- a/extensions/json/syntaxes/JSON.tmLanguage.json +++ b/extensions/json/syntaxes/JSON.tmLanguage.json @@ -210,4 +210,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/json/syntaxes/JSONC.tmLanguage.json b/extensions/json/syntaxes/JSONC.tmLanguage.json index bf65fce9..31828ba6 100644 --- a/extensions/json/syntaxes/JSONC.tmLanguage.json +++ b/extensions/json/syntaxes/JSONC.tmLanguage.json @@ -210,4 +210,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/make/language-configuration.json b/extensions/make/language-configuration.json index 80289039..82645b84 100644 --- a/extensions/make/language-configuration.json +++ b/extensions/make/language-configuration.json @@ -3,8 +3,47 @@ "lineComment": "#" }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] + ], + "autoClosingPairs": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + } ] -} \ No newline at end of file +} diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 71df78ef..5606dcc7 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -26,7 +26,19 @@ ], "license": "TextMate Bundle License", "version": "0.0.0" + }, + { + "component": { + "type": "git", + "git": { + "name": "microsoft/vscode-markdown-tm-grammar", + "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", + "commitHash": "11cf764606cb2cde54badb5d0e5a0758a8871c4b" + } + }, + "license": "MIT", + "version": "0.0.0" } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/markdown-basics/snippets/markdown.code-snippets b/extensions/markdown-basics/snippets/markdown.code-snippets index b07f9841..f0753507 100644 --- a/extensions/markdown-basics/snippets/markdown.code-snippets +++ b/extensions/markdown-basics/snippets/markdown.code-snippets @@ -64,6 +64,11 @@ "body": ["1. ${1:first}", "2. ${2:second}", "3. ${3:third}", "$0"], "description": "Insert ordered list" }, + "Insert definition list": { + "prefix": "definition list", + "body": ["${1:term}", ": ${2:definition}", "$0"], + "description": "Insert definition list" + }, "Insert horizontal rule": { "prefix": "horizontal rule", "body": "----------\n", @@ -83,5 +88,5 @@ "prefix": "strikethrough", "body": "~~${1:${TM_SELECTED_TEXT}}~~", "description": "Insert strikethrough" - } + }, } diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index e8feaa75..13ee6f36 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4be9cb335581f3559166c319607dac9100103083", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/f051a36bd9713dd722cbe1bdde9c8240d12f00b4", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2190,9 +2190,18 @@ }, "13": { "name": "punctuation.definition.string.end.markdown" + }, + "14": { + "name": "string.other.link.description.title.markdown" + }, + "15": { + "name": "punctuation.definition.string.begin.markdown" + }, + "16": { + "name": "punctuation.definition.string.end.markdown" } }, - "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotesâ€Ļ\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", + "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parensâ€Ļ\n | ((\").+?(\")) # or in double quotesâ€Ļ\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", "name": "meta.link.reference.def.markdown" }, "list_paragraph": { @@ -2453,10 +2462,19 @@ "name": "punctuation.definition.string.markdown" }, "15": { + "name": "string.other.link.description.title.markdown" + }, + "16": { + "name": "punctuation.definition.string.markdown" + }, + "17": { + "name": "punctuation.definition.string.markdown" + }, + "18": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parensâ€Ļ\n | ((\").+?(\")) # or in quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parensâ€Ļ\n | ((\").+?(\")) # or in double quotesâ€Ļ\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.image.inline.markdown" }, "image-ref": { @@ -2616,10 +2634,19 @@ "name": "punctuation.definition.string.end.markdown" }, "16": { + "name": "string.other.link.description.title.markdown" + }, + "17": { + "name": "punctuation.definition.string.begin.markdown" + }, + "18": { + "name": "punctuation.definition.string.end.markdown" + }, + "19": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n ((?>[^\\s()]+)|\\(\\g*\\))*)(>?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parensâ€Ļ\n | ((\").+?(\")) # or in quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n ((?>[^\\s()]+)|\\(\\g*\\))*)(>?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parensâ€Ļ\n | ((\").+?(\")) # or in double quotesâ€Ļ\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.link.inline.markdown" }, "link-ref": { diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index f581cd00..edaa6f92 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -154,15 +154,13 @@ table { border-collapse: collapse; } -table > thead > tr > th { +th { text-align: left; border-bottom: 1px solid; } -table > thead > tr > th, -table > thead > tr > td, -table > tbody > tr > th, -table > tbody > tr > td { +th, +td { padding: 5px 10px; } @@ -217,22 +215,22 @@ pre code { border-color: rgb(0, 0, 0); } -.vscode-light table > thead > tr > th { +.vscode-light th { border-color: rgba(0, 0, 0, 0.69); } -.vscode-dark table > thead > tr > th { +.vscode-dark th { border-color: rgba(255, 255, 255, 0.69); } .vscode-light h1, .vscode-light hr, -.vscode-light table > tbody > tr + tr > td { +.vscode-light td { border-color: rgba(0, 0, 0, 0.18); } .vscode-dark h1, .vscode-dark hr, -.vscode-dark table > tbody > tr + tr > td { +.vscode-dark td { border-color: rgba(255, 255, 255, 0.18); } diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 34d6902f..72b952e8 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -324,7 +324,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "highlight.js": "9.15.10", + "highlight.js": "10.1.2", "markdown-it": "^10.0.0", "markdown-it-front-matter": "^0.2.1", "vscode-extension-telemetry": "0.1.1", diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index c17dc17c..79288f2d 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -12,10 +12,18 @@ import { TableOfContentsProvider } from '../tableOfContentsProvider'; import { isMarkdownFile } from '../util/file'; +type UriComponents = { + readonly scheme?: string; + readonly path: string; + readonly fragment?: string; + readonly authority?: string; + readonly query?: string; +}; + export interface OpenDocumentLinkArgs { - readonly path: {}; + readonly parts: UriComponents; readonly fragment: string; - readonly fromResource: {}; + readonly fromResource: UriComponents; } enum OpenMarkdownLinks { @@ -32,7 +40,7 @@ export class OpenDocumentLinkCommand implements Command { path: vscode.Uri, fragment: string, ): vscode.Uri { - const toJson = (uri: vscode.Uri) => { + const toJson = (uri: vscode.Uri): UriComponents => { return { scheme: uri.scheme, authority: uri.authority, @@ -42,7 +50,7 @@ export class OpenDocumentLinkCommand implements Command { }; }; return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ - path: toJson(path), + parts: toJson(path), fragment, fromResource: toJson(fromResource), }))}`); @@ -56,36 +64,58 @@ export class OpenDocumentLinkCommand implements Command { return OpenDocumentLinkCommand.execute(this.engine, args); } - public static async execute(engine: MarkdownEngine, args: OpenDocumentLinkArgs) { + public static async execute(engine: MarkdownEngine, args: OpenDocumentLinkArgs): Promise { const fromResource = vscode.Uri.parse('').with(args.fromResource); - const targetResource = vscode.Uri.parse('').with(args.path); + + const targetResource = reviveUri(args.parts); + const column = this.getViewColumn(fromResource); - try { - return await this.tryOpen(engine, targetResource, args, column); - } catch { - if (extname(targetResource.path) === '') { - return this.tryOpen(engine, targetResource.with({ path: targetResource.path + '.md' }), args, column); - } - await vscode.commands.executeCommand('vscode.open', targetResource, column); - return undefined; + + const didOpen = await this.tryOpen(engine, targetResource, args, column); + if (didOpen) { + return; + } + + if (extname(targetResource.path) === '') { + await this.tryOpen(engine, targetResource.with({ path: targetResource.path + '.md' }), args, column); + return; } } - private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { - if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { - if (vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { - return this.tryRevealLine(engine, vscode.window.activeTextEditor, args.fragment); + private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn): Promise { + const tryUpdateForActiveFile = async (): Promise => { + if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { + if (vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { + await this.tryRevealLine(engine, vscode.window.activeTextEditor, args.fragment); + return true; + } } + return false; + }; + + if (await tryUpdateForActiveFile()) { + return true; + } + + let stat: vscode.FileStat; + try { + stat = await vscode.workspace.fs.stat(resource); + } catch { + return false; } - const stat = await vscode.workspace.fs.stat(resource); if (stat.type === vscode.FileType.Directory) { - return vscode.commands.executeCommand('revealInExplorer', resource); + await vscode.commands.executeCommand('revealInExplorer', resource); + return true; } - return vscode.workspace.openTextDocument(resource) - .then(document => vscode.window.showTextDocument(document, column)) - .then(editor => this.tryRevealLine(engine, editor, args.fragment)); + try { + await vscode.commands.executeCommand('vscode.open', resource, column); + } catch { + return false; + } + + return tryUpdateForActiveFile(); } private static getViewColumn(resource: vscode.Uri): vscode.ViewColumn { @@ -101,7 +131,7 @@ export class OpenDocumentLinkCommand implements Command { } private static async tryRevealLine(engine: MarkdownEngine, editor: vscode.TextEditor, fragment?: string) { - if (editor && fragment) { + if (fragment) { const toc = new TableOfContentsProvider(engine, editor.document); const entry = await toc.lookup(fragment); if (entry) { @@ -122,6 +152,12 @@ export class OpenDocumentLinkCommand implements Command { } } +function reviveUri(parts: any) { + if (parts.scheme === 'file') { + return vscode.Uri.file(parts.path); + } + return vscode.Uri.parse('').with(parts); +} export async function resolveLinkToMarkdownFile(path: string): Promise { try { diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 6d85e613..becb1687 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -65,19 +65,6 @@ function getWorkspaceFolder(document: vscode.TextDocument) { || vscode.workspace.workspaceFolders?.[0]?.uri; } -function matchAll( - pattern: RegExp, - text: string -): Array { - const out: RegExpMatchArray[] = []; - pattern.lastIndex = 0; - let match: RegExpMatchArray | null; - while ((match = pattern.exec(text))) { - out.push(match); - } - return out; -} - function extractDocumentLink( document: vscode.TextDocument, pre: number, @@ -103,7 +90,7 @@ function extractDocumentLink( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)(\S+)/gm; @@ -124,7 +111,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { document: vscode.TextDocument, ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; - for (const match of matchAll(this.linkPattern, text)) { + for (const match of text.matchAll(this.linkPattern)) { const matchImage = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); if (matchImage) { results.push(matchImage); @@ -144,7 +131,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { const results: vscode.DocumentLink[] = []; const definitions = this.getDefinitions(text, document); - for (const match of matchAll(this.referenceLinkPattern, text)) { + for (const match of text.matchAll(this.referenceLinkPattern)) { let linkStart: vscode.Position; let linkEnd: vscode.Position; let reference = match[3]; @@ -190,7 +177,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { private getDefinitions(text: string, document: vscode.TextDocument) { const out = new Map(); - for (const match of matchAll(this.definitionPattern, text)) { + for (const match of text.matchAll(this.definitionPattern)) { const pre = match[1]; const reference = match[2]; const link = match[3].trim(); diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 553c1a32..62c245c9 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -7,7 +7,6 @@ import { Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; import { TableOfContentsProvider } from '../tableOfContentsProvider'; -import { flatten } from '../util/arrays'; const rangeLimit = 5000; @@ -27,7 +26,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi this.getHeaderFoldingRanges(document), this.getBlockFoldingRanges(document) ]); - return flatten(foldables).slice(0, rangeLimit); + return foldables.flat().slice(0, rangeLimit); } private async getRegions(document: vscode.TextDocument): Promise { diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index f2fb8003..9244546c 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -408,7 +408,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } } - OpenDocumentLinkCommand.execute(this.engine, { path: hrefPath, fragment, fromResource: this.resource.toJSON() }); + OpenDocumentLinkCommand.execute(this.engine, { parts: { path: hrefPath }, fragment, fromResource: this.resource.toJSON() }); } //#region WebviewResourceProvider diff --git a/extensions/markdown-language-features/src/features/smartSelect.ts b/extensions/markdown-language-features/src/features/smartSelect.ts index 0be50d0c..12b2bdd7 100644 --- a/extensions/markdown-language-features/src/features/smartSelect.ts +++ b/extensions/markdown-language-features/src/features/smartSelect.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; @@ -15,7 +14,7 @@ export default class MarkdownSmartSelect implements vscode.SelectionRangeProvide ) { } public async provideSelectionRanges(document: vscode.TextDocument, positions: vscode.Position[], _token: vscode.CancellationToken): Promise { - let promises = await Promise.all(positions.map((position) => { + const promises = await Promise.all(positions.map((position) => { return this.provideSelectionRange(document, position, _token); })); return promises.filter(item => item !== undefined) as vscode.SelectionRange[]; @@ -24,54 +23,206 @@ export default class MarkdownSmartSelect implements vscode.SelectionRangeProvide private async provideSelectionRange(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise { const headerRange = await this.getHeaderSelectionRange(document, position); const blockRange = await this.getBlockSelectionRange(document, position, headerRange); - return blockRange ? blockRange : headerRange ? headerRange : undefined; + const inlineRange = await this.getInlineSelectionRange(document, position, blockRange); + return inlineRange || blockRange || headerRange; + } + private async getInlineSelectionRange(document: vscode.TextDocument, position: vscode.Position, blockRange?: vscode.SelectionRange): Promise { + return createInlineRange(document, position, blockRange); } private async getBlockSelectionRange(document: vscode.TextDocument, position: vscode.Position, headerRange?: vscode.SelectionRange): Promise { const tokens = await this.engine.parse(document); - let blockTokens = getTokensForPosition(tokens, position); + const blockTokens = getBlockTokensForPosition(tokens, position, headerRange); if (blockTokens.length === 0) { return undefined; } - let parentRange = headerRange ? headerRange : createBlockRange(document, position.line, blockTokens.shift()); - let currentRange: vscode.SelectionRange | undefined; + let currentRange: vscode.SelectionRange | undefined = headerRange ? headerRange : createBlockRange(blockTokens.shift()!, document, position.line); - for (const token of blockTokens) { - currentRange = createBlockRange(document, position.line, token, parentRange); - if (currentRange) { - parentRange = currentRange; - } - } - if (currentRange) { - return currentRange; - } else { - return parentRange; - } - } - - private async getHeaderSelectionRange(document: vscode.TextDocument, position: vscode.Position): Promise { - const tocProvider = new TableOfContentsProvider(this.engine, document); - const toc = await tocProvider.getToc(); - - let headerInfo = getHeadersForPosition(toc, position); - - let headers = headerInfo.headers; - - let parentRange: vscode.SelectionRange | undefined; - let currentRange: vscode.SelectionRange | undefined; - - for (let i = 0; i < headers.length; i++) { - currentRange = createHeaderRange(i === headers.length - 1, headerInfo.headerOnThisLine, headers[i], parentRange, getFirstChildHeader(document, headers[i], toc)); - if (currentRange && currentRange.parent) { - parentRange = currentRange; - } + for (let i = 0; i < blockTokens.length; i++) { + currentRange = createBlockRange(blockTokens[i], document, position.line, currentRange); } return currentRange; } + + private async getHeaderSelectionRange(document: vscode.TextDocument, position: vscode.Position): Promise { + + const tocProvider = new TableOfContentsProvider(this.engine, document); + const toc = await tocProvider.getToc(); + + const headerInfo = getHeadersForPosition(toc, position); + + const headers = headerInfo.headers; + + let currentRange: vscode.SelectionRange | undefined; + + for (let i = 0; i < headers.length; i++) { + currentRange = createHeaderRange(headers[i], i === headers.length - 1, headerInfo.headerOnThisLine, currentRange, getFirstChildHeader(document, headers[i], toc)); + } + return currentRange; + } +} + +function getHeadersForPosition(toc: TocEntry[], position: vscode.Position): { headers: TocEntry[], headerOnThisLine: boolean } { + const enclosingHeaders = toc.filter(header => header.location.range.start.line <= position.line && header.location.range.end.line >= position.line); + const sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line)); + const onThisLine = toc.find(header => header.line === position.line) !== undefined; + return { + headers: sortedHeaders, + headerOnThisLine: onThisLine + }; +} + +function createHeaderRange(header: TocEntry, isClosestHeaderToPosition: boolean, onHeaderLine: boolean, parent?: vscode.SelectionRange, startOfChildRange?: vscode.Position): vscode.SelectionRange | undefined { + const range = header.location.range; + const contentRange = new vscode.Range(range.start.translate(1), range.end); + if (onHeaderLine && isClosestHeaderToPosition && startOfChildRange) { + // selection was made on this header line, so select header and its content until the start of its first child + // then all of its content + return new vscode.SelectionRange(range.with(undefined, startOfChildRange), new vscode.SelectionRange(range, parent)); + } else if (onHeaderLine && isClosestHeaderToPosition) { + // selection was made on this header line and no children so expand to all of its content + return new vscode.SelectionRange(range, parent); + } else if (isClosestHeaderToPosition && startOfChildRange) { + // selection was made within content and has child so select content + // of this header then all content then header + return new vscode.SelectionRange(contentRange.with(undefined, startOfChildRange), new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(range, parent)))); + } else { + // not on this header line so select content then header + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(range, parent)); + } +} + +function getBlockTokensForPosition(tokens: Token[], position: vscode.Position, parent?: vscode.SelectionRange): Token[] { + const enclosingTokens = tokens.filter(token => token.map && (token.map[0] <= position.line && token.map[1] > position.line) && (!parent || (token.map[0] >= parent.range.start.line && token.map[1] <= parent.range.end.line + 1)) && isBlockElement(token)); + if (enclosingTokens.length === 0) { + return []; + } + const sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0])); + return sortedTokens; +} + +function createBlockRange(block: Token, document: vscode.TextDocument, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + if (block.type === 'fence') { + return createFencedRange(block, cursorLine, document, parent); + } else { + let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0]; + let endLine = startLine === block.map[1] ? block.map[1] : block.map[1] - 1; + if (block.type === 'paragraph_open' && block.map[1] - block.map[0] === 2) { + startLine = endLine = cursorLine; + } else if (isList(block) && document.lineAt(endLine).isEmptyOrWhitespace) { + endLine = endLine - 1; + } + const range = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text?.length ?? 0); + if (parent?.range.contains(range) && !parent.range.isEqual(range)) { + return new vscode.SelectionRange(range, parent); + } else if (parent?.range.isEqual(range)) { + return parent; + } else { + return new vscode.SelectionRange(range); + } + } +} + +function createInlineRange(document: vscode.TextDocument, cursorPosition: vscode.Position, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const lineText = document.lineAt(cursorPosition.line).text; + const boldSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, parent); + const italicSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, parent); + let comboSelection: vscode.SelectionRange | undefined; + if (boldSelection && italicSelection && !boldSelection.range.isEqual(italicSelection.range)) { + if (boldSelection.range.contains(italicSelection.range)) { + comboSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, boldSelection); + } else if (italicSelection.range.contains(boldSelection.range)) { + comboSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, italicSelection); + } + } + const linkSelection = createLinkRange(lineText, cursorPosition.character, cursorPosition.line, comboSelection || boldSelection || italicSelection || parent); + const inlineCodeBlockSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, false, linkSelection || parent); + return inlineCodeBlockSelection || linkSelection || comboSelection || boldSelection || italicSelection; +} + +function createFencedRange(token: Token, cursorLine: number, document: vscode.TextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange { + const startLine = token.map[0]; + const endLine = token.map[1] - 1; + const onFenceLine = cursorLine === startLine || cursorLine === endLine; + const fenceRange = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text.length); + const contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(startLine + 1, 0, endLine - 1, document.lineAt(endLine - 1).text.length) : undefined; + if (contentRange) { + return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); + } else { + if (parent?.range.isEqual(fenceRange)) { + return parent; + } else { + return new vscode.SelectionRange(fenceRange, parent); + } + } +} + +function createBoldRange(lineText: string, cursorChar: number, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const regex = /(?:^|(?<=\s))(?:\*\*\s*([^*]+)(?:\*\s*([^*]+)\s*?\*)*([^*]+)\s*?\*\*)/g; + const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); + if (matches.length > 0) { + // should only be one match, so select first and index 0 contains the entire match + const bold = matches[0][0]; + const startIndex = lineText.indexOf(bold); + const cursorOnStars = cursorChar === startIndex || cursorChar === startIndex + 1 || cursorChar === startIndex + bold.length || cursorChar === startIndex + bold.length - 1; + const contentAndStars = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + bold.length), parent); + const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 2, cursorLine, startIndex + bold.length - 2), contentAndStars); + return cursorOnStars ? contentAndStars : content; + } + return undefined; +} + +function createOtherInlineRange(lineText: string, cursorChar: number, cursorLine: number, isItalic: boolean, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const regex = isItalic ? /(?:^|(?<=\s))(?:\*\s*([^*]+)(?:\*\*\s*([^*]+)\s*?\*\*)*([^*]+)\s*?\*)/g : /\`[^\`]*\`/g; + const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); + if (matches.length > 0) { + // should only be one match, so select first and index 0 contains the entire match + const match = matches[0][0]; + const startIndex = lineText.indexOf(match); + const cursorOnType = cursorChar === startIndex || cursorChar === startIndex + match.length; + const contentAndType = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + match.length), parent); + const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 1, cursorLine, startIndex + match.length - 1), contentAndType); + return cursorOnType ? contentAndType : content; + } + return undefined; +} + +function createLinkRange(lineText: string, cursorChar: number, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { + const regex = /(\[[^\(\)]*\])(\([^\[\]]*\))/g; + const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length > cursorChar); + + if (matches.length > 0) { + // should only be one match, so select first and index 0 contains the entire match, so match = [text](url) + const link = matches[0][0]; + const linkRange = new vscode.SelectionRange(new vscode.Range(cursorLine, lineText.indexOf(link), cursorLine, lineText.indexOf(link) + link.length), parent); + + const linkText = matches[0][1]; + const url = matches[0][2]; + + // determine if cursor is within [text] or (url) in order to know which should be selected + const nearestType = cursorChar >= lineText.indexOf(linkText) && cursorChar < lineText.indexOf(linkText) + linkText.length ? linkText : url; + + const indexOfType = lineText.indexOf(nearestType); + // determine if cursor is on a bracket or paren and if so, return the [content] or (content), skipping over the content range + const cursorOnType = cursorChar === indexOfType || cursorChar === indexOfType + nearestType.length; + + const contentAndNearestType = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType, cursorLine, indexOfType + nearestType.length), linkRange); + const content = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType + 1, cursorLine, indexOfType + nearestType.length - 1), contentAndNearestType); + return cursorOnType ? contentAndNearestType : content; + } + return undefined; +} + +function isList(token: Token): boolean { + return token.type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(token.type) : false; +} + +function isBlockElement(token: Token): boolean { + return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type); } function getFirstChildHeader(document: vscode.TextDocument, header?: TocEntry, toc?: TocEntry[]): vscode.Position | undefined { @@ -80,159 +231,9 @@ function getFirstChildHeader(document: vscode.TextDocument, header?: TocEntry, t let children = toc.filter(t => header.location.range.contains(t.location.range) && t.location.range.start.line > header.location.range.start.line).sort((t1, t2) => t1.line - t2.line); if (children.length > 0) { childRange = children[0].location.range.start; - let lineText = document.lineAt(childRange.line - 1).text; + const lineText = document.lineAt(childRange.line - 1).text; return childRange ? childRange.translate(-1, lineText.length) : undefined; } } return undefined; } - -function getTokensForPosition(tokens: Token[], position: vscode.Position): Token[] { - let enclosingTokens = tokens.filter(token => token.map && (token.map[0] <= position.line && token.map[1] > position.line) && isBlockElement(token)); - - if (enclosingTokens.length === 0) { - return []; - } - - let sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0])); - return sortedTokens; -} - -function getHeadersForPosition(toc: TocEntry[], position: vscode.Position): { headers: TocEntry[], headerOnThisLine: boolean } { - let enclosingHeaders = toc.filter(header => header.location.range.start.line <= position.line && header.location.range.end.line >= position.line); - let sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line)); - let onThisLine = toc.find(header => header.line === position.line) !== undefined; - return { - headers: sortedHeaders, - headerOnThisLine: onThisLine - }; -} - -function isBlockElement(token: Token): boolean { - return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type); -} - -function createHeaderRange(isClosestHeaderToPosition: boolean, onHeaderLine: boolean, header?: TocEntry, parent?: vscode.SelectionRange, childStart?: vscode.Position): vscode.SelectionRange | undefined { - if (header) { - let contentRange = new vscode.Range(header.location.range.start.translate(1), header.location.range.end); - let headerPlusContentRange = header.location.range; - let partialContentRange = childStart && isClosestHeaderToPosition ? contentRange.with(undefined, childStart) : undefined; - if (onHeaderLine && isClosestHeaderToPosition && childStart) { - return new vscode.SelectionRange(header.location.range.with(undefined, childStart), new vscode.SelectionRange(header.location.range, parent)); - } else if (onHeaderLine && isClosestHeaderToPosition) { - return new vscode.SelectionRange(header.location.range, parent); - } else if (parent && parent.range.contains(headerPlusContentRange)) { - if (partialContentRange) { - return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange, parent)))); - } else { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange, parent)); - } - } else if (partialContentRange) { - return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange)))); - } else { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange)); - } - } else { - return undefined; - } -} - -function createBlockRange(document: vscode.TextDocument, cursorLine: number, block?: Token, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { - if (block) { - if (block.type === 'fence') { - return createFencedRange(block, cursorLine, document, parent); - } else { - let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0]; - let endLine = startLine !== block.map[1] && isList(block.type) ? block.map[1] - 1 : block.map[1]; - let startPos = new vscode.Position(startLine, 0); - let endPos = new vscode.Position(endLine, getEndCharacter(document, startLine, endLine)); - let range = new vscode.Range(startPos, endPos); - if (parent && parent.range.contains(range) && !parent.range.isEqual(range)) { - return new vscode.SelectionRange(range, parent); - } else if (parent) { - if (rangeLinesEqual(range, parent.range)) { - return range.end.character > parent.range.end.character ? new vscode.SelectionRange(range) : parent; - } else if (parent.range.end.line + 1 === range.end.line) { - let adjustedRange = new vscode.Range(range.start, range.end.translate(-1, parent.range.end.character)); - if (adjustedRange.isEqual(parent.range)) { - return parent; - } else { - return new vscode.SelectionRange(adjustedRange, parent); - } - } else if (parent.range.end.line === range.end.line) { - let adjustedRange = new vscode.Range(parent.range.start, range.end.translate(undefined, parent.range.end.character)); - if (adjustedRange.isEqual(parent.range)) { - return parent; - } else { - return new vscode.SelectionRange(adjustedRange, parent.parent); - } - } else { - return parent; - } - } else { - return new vscode.SelectionRange(range); - } - } - } else { - return undefined; - } -} - -function createFencedRange(token: Token, cursorLine: number, document: vscode.TextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange { - const startLine = token.map[0]; - const endLine = token.map[1] - 1; - let onFenceLine = cursorLine === startLine || cursorLine === endLine; - let fenceRange = new vscode.Range(new vscode.Position(startLine, 0), new vscode.Position(endLine, document.lineAt(endLine).text.length)); - let contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(new vscode.Position(startLine + 1, 0), new vscode.Position(endLine - 1, getEndCharacter(document, startLine + 1, endLine))) : undefined; - if (parent && contentRange) { - if (parent.range.contains(fenceRange) && !parent.range.isEqual(fenceRange)) { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); - } else if (parent.range.isEqual(fenceRange)) { - return new vscode.SelectionRange(contentRange, parent); - } else if (rangeLinesEqual(fenceRange, parent.range)) { - let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range; - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(revisedRange, getRealParent(parent, revisedRange))); - } else if (parent.range.end.line === fenceRange.end.line) { - parent.range.end.translate(undefined, fenceRange.end.character); - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); - } - } else if (contentRange) { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange)); - } else if (parent) { - if (parent.range.contains(fenceRange) && !parent.range.isEqual(fenceRange)) { - return new vscode.SelectionRange(fenceRange, parent); - } else if (parent.range.isEqual(fenceRange)) { - return parent; - } else if (rangeLinesEqual(fenceRange, parent.range)) { - let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range; - return new vscode.SelectionRange(revisedRange, parent.parent); - } else if (parent.range.end.line === fenceRange.end.line) { - parent.range.end.translate(undefined, fenceRange.end.character); - return new vscode.SelectionRange(fenceRange, parent); - } - } - return new vscode.SelectionRange(fenceRange, parent); -} - -function isList(type: string): boolean { - return type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(type) : false; -} - -function getEndCharacter(document: vscode.TextDocument, startLine: number, endLine: number): number { - let startLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0; - let endLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0; - let endChar = Math.max(startLength, endLength); - return startLine !== endLine ? 0 : endChar; -} - -function getRealParent(parent: vscode.SelectionRange, range: vscode.Range) { - let currentParent: vscode.SelectionRange | undefined = parent; - while (currentParent && !currentParent.range.contains(range)) { - currentParent = currentParent.parent; - } - return currentParent; -} - -function rangeLinesEqual(range: vscode.Range, parent: vscode.Range) { - return range.start.line === parent.start.line && range.end.line === parent.end.line; -} diff --git a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts index f97bb021..91f9dfcd 100644 --- a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts @@ -9,7 +9,6 @@ import { isMarkdownFile } from '../util/file'; import { Lazy, lazy } from '../util/lazy'; import MDDocumentSymbolProvider from './documentSymbolProvider'; import { SkinnyTextDocument, SkinnyTextLine } from '../tableOfContentsProvider'; -import { flatten } from '../util/arrays'; export interface WorkspaceMarkdownDocumentProvider { getAllMarkdownDocuments(): Thenable>; @@ -136,7 +135,7 @@ export default class MarkdownWorkspaceSymbolProvider extends Disposable implemen } const allSymbolsSets = await Promise.all(Array.from(this._symbolCache.values()).map(x => x.value)); - const allSymbols = flatten(allSymbolsSets); + const allSymbols = allSymbolsSets.flat(); return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1); } diff --git a/extensions/markdown-language-features/src/test/inMemoryDocument.ts b/extensions/markdown-language-features/src/test/inMemoryDocument.ts index 052216f9..8f5f6fe7 100644 --- a/extensions/markdown-language-features/src/test/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/test/inMemoryDocument.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; - +import * as os from 'os'; export class InMemoryDocument implements vscode.TextDocument { private readonly _lines: string[]; @@ -13,7 +13,7 @@ export class InMemoryDocument implements vscode.TextDocument { private readonly _contents: string, public readonly version = 1, ) { - this._lines = this._contents.split(/\n/g); + this._lines = this._contents.split(/\r\n|\n/g); } @@ -21,7 +21,7 @@ export class InMemoryDocument implements vscode.TextDocument { languageId: string = ''; isDirty: boolean = false; isClosed: boolean = false; - eol: vscode.EndOfLine = vscode.EndOfLine.LF; + eol: vscode.EndOfLine = os.platform() === 'win32' ? vscode.EndOfLine.CRLF : vscode.EndOfLine.LF; notebook: undefined; get fileName(): string { @@ -47,9 +47,9 @@ export class InMemoryDocument implements vscode.TextDocument { } positionAt(offset: number): vscode.Position { const before = this._contents.slice(0, offset); - const newLines = before.match(/\n/g); + const newLines = before.match(/\r\n|\n/g); const line = newLines ? newLines.length : 0; - const preCharacters = before.match(/(\n|^).*$/g); + const preCharacters = before.match(/(\r\n|\n|^).*$/g); return new vscode.Position(line, preCharacters ? preCharacters[0].length : 0); } getText(_range?: vscode.Range | undefined): string { diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts index 787df59a..6e77d56c 100644 --- a/extensions/markdown-language-features/src/test/smartSelect.test.ts +++ b/extensions/markdown-language-features/src/test/smartSelect.test.ts @@ -14,10 +14,10 @@ const CURSOR = '$$CURSOR$$'; const testFileName = vscode.Uri.file('test.md'); -suite.only('markdown.SmartSelect', () => { +suite('markdown.SmartSelect', () => { test('Smart select single word', async () => { const ranges = await getSelectionRangesForDocument(`Hel${CURSOR}lo`); - assertNestedRangesEqual(ranges![0], [0, 1]); + assertNestedLineNumbersEqual(ranges![0], [0, 0]); }); test('Smart select multi-line paragraph', async () => { const ranges = await getSelectionRangesForDocument( @@ -26,12 +26,12 @@ suite.only('markdown.SmartSelect', () => { `For example, the[node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter]`, `(https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).` )); - assertNestedRangesEqual(ranges![0], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select paragraph', async () => { const ranges = await getSelectionRangesForDocument(`Many of the core components and extensions to ${CURSOR}VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).`); - assertNestedRangesEqual(ranges![0], [0, 1]); + assertNestedLineNumbersEqual(ranges![0], [0, 0]); }); test('Smart select html block', async () => { const ranges = await getSelectionRangesForDocument( @@ -40,7 +40,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}VS Code in action`, `

`)); - assertNestedRangesEqual(ranges![0], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select header on header line', async () => { const ranges = await getSelectionRangesForDocument( @@ -48,7 +48,7 @@ suite.only('markdown.SmartSelect', () => { `# Header${CURSOR}`, `Hello`)); - assertNestedRangesEqual(ranges![0], [0, 1]); + assertNestedLineNumbersEqual(ranges![0], [0, 1]); }); test('Smart select single word w grandparent header on text line', async () => { @@ -59,7 +59,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}Hello` )); - assertNestedRangesEqual(ranges![0], [2, 2], [1, 2]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [1, 2]); }); test('Smart select html block w parent header', async () => { const ranges = await getSelectionRangesForDocument( @@ -69,7 +69,7 @@ suite.only('markdown.SmartSelect', () => { `VS Code in action`, `

`)); - assertNestedRangesEqual(ranges![0], [1, 3], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 3], [0, 3]); }); test('Smart select fenced code block', async () => { const ranges = await getSelectionRangesForDocument( @@ -78,7 +78,7 @@ suite.only('markdown.SmartSelect', () => { `a${CURSOR}`, `~~~`)); - assertNestedRangesEqual(ranges![0], [0, 2]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select list', async () => { const ranges = await getSelectionRangesForDocument( @@ -87,8 +87,7 @@ suite.only('markdown.SmartSelect', () => { `- ${CURSOR}item 2`, `- item 3`, `- item 4`)); - - assertNestedRangesEqual(ranges![0], [1, 1], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [0, 3]); }); test('Smart select list with fenced code block', async () => { const ranges = await getSelectionRangesForDocument( @@ -100,7 +99,7 @@ suite.only('markdown.SmartSelect', () => { `- item 3`, `- item 4`)); - assertNestedRangesEqual(ranges![0], [1, 3], [0, 5]); + assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 5]); }); test('Smart select multi cursor', async () => { const ranges = await getSelectionRangesForDocument( @@ -112,8 +111,8 @@ suite.only('markdown.SmartSelect', () => { `- ${CURSOR}item 3`, `- item 4`)); - assertNestedRangesEqual(ranges![0], [0, 0], [0, 5]); - assertNestedRangesEqual(ranges![1], [4, 4], [0, 5]); + assertNestedLineNumbersEqual(ranges![0], [0, 0], [0, 5]); + assertNestedLineNumbersEqual(ranges![1], [4, 4], [0, 5]); }); test('Smart select nested block quotes', async () => { const ranges = await getSelectionRangesForDocument( @@ -122,7 +121,7 @@ suite.only('markdown.SmartSelect', () => { `> item 2`, `>> ${CURSOR}item 3`, `>> item 4`)); - assertNestedRangesEqual(ranges![0], [2, 4], [0, 4]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [0, 3]); }); test('Smart select multi nested block quotes', async () => { const ranges = await getSelectionRangesForDocument( @@ -131,8 +130,7 @@ suite.only('markdown.SmartSelect', () => { `>> item 2`, `>>> ${CURSOR}item 3`, `>>>> item 4`)); - - assertNestedRangesEqual(ranges![0], [2, 3], [2, 4], [1, 4], [0, 4]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [1, 3], [0, 3]); }); test('Smart select subheader content', async () => { const ranges = await getSelectionRangesForDocument( @@ -143,7 +141,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]); }); test('Smart select subheader line', async () => { const ranges = await getSelectionRangesForDocument( @@ -154,7 +152,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [2, 3], [1, 3], [0, 3]); }); test('Smart select blank line', async () => { const ranges = await getSelectionRangesForDocument( @@ -165,7 +163,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [1, 3], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 3]); }); test('Smart select line between paragraphs', async () => { const ranges = await getSelectionRangesForDocument( @@ -174,7 +172,7 @@ suite.only('markdown.SmartSelect', () => { `${CURSOR}`, `paragraph 2`)); - assertNestedRangesEqual(ranges![0], [0, 3]); + assertNestedLineNumbersEqual(ranges![0], [0, 2]); }); test('Smart select empty document', async () => { const ranges = await getSelectionRangesForDocument(``, [new vscode.Position(0, 0)]); @@ -196,7 +194,7 @@ suite.only('markdown.SmartSelect', () => { `more content`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [4, 6], [3, 9], [3, 10], [2, 10], [1, 10], [0, 10]); + assertNestedLineNumbersEqual(ranges![0], [4, 6], [3, 9], [3, 10], [2, 10], [1, 10], [0, 10]); }); test('Smart select list with one element without selecting child subheader', async () => { const ranges = await getSelectionRangesForDocument( @@ -209,8 +207,7 @@ suite.only('markdown.SmartSelect', () => { ``, `content 2`, `# main header 2`)); - - assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [1, 6], [0, 6]); + assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [1, 3], [1, 6], [0, 6]); }); test('Smart select content under header then subheaders and their content', async () => { const ranges = await getSelectionRangesForDocument( @@ -224,7 +221,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [0, 3], [0, 6]); + assertNestedLineNumbersEqual(ranges![0], [0, 3], [0, 6]); }); test('Smart select last blockquote element under header then subheaders and their content', async () => { const ranges = await getSelectionRangesForDocument( @@ -242,7 +239,7 @@ suite.only('markdown.SmartSelect', () => { `content 2`, `# main header 2`)); - assertNestedRangesEqual(ranges![0], [4, 6], [2, 6], [1, 7], [1, 10], [0, 10]); + assertNestedLineNumbersEqual(ranges![0], [5, 5], [4, 5], [2, 5], [1, 7], [1, 10], [0, 10]); }); test('Smart select content of subheader then subheader then content of main header then main header', async () => { const ranges = await getSelectionRangesForDocument( @@ -266,7 +263,7 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `content 2`)); - assertNestedRangesEqual(ranges![0], [11, 12], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [11, 11], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]); }); test('Smart select last line content of subheader then subheader then content of main header then main header', async () => { const ranges = await getSelectionRangesForDocument( @@ -288,9 +285,9 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`, `- content 2`, - `${CURSOR}content 2`)); + `- ${CURSOR}content 2`)); - assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); }); test('Smart select last line content after content of subheader then subheader then content of main header then main header', async () => { const ranges = await getSelectionRangesForDocument( @@ -312,9 +309,9 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`, `- content 2`, - `content 2${CURSOR}`)); + `- content 2${CURSOR}`)); - assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); }); test('Smart select fenced code block then list then rest of content', async () => { const ranges = await getSelectionRangesForDocument( @@ -338,7 +335,7 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`)); - assertNestedRangesEqual(ranges![0], [9, 11], [8, 12], [7, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [9, 11], [8, 12], [8, 12], [7, 17], [1, 17], [0, 17]); }); test('Smart select fenced code block then list then rest of content on fenced line', async () => { const ranges = await getSelectionRangesForDocument( @@ -362,15 +359,289 @@ suite.only('markdown.SmartSelect', () => { `- content 2`, `- content 2`)); - assertNestedRangesEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]); + assertNestedLineNumbersEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]); + }); + test('Smart select without multiple ranges', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + ``, + `- ${CURSOR}paragraph`, + `- content`)); + + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [1, 4], [0, 4]); + }); + test('Smart select on second level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level 2`, + ` * level 1`, + ` * level ${CURSOR}1`, + `* level 0`)); + + assertNestedLineNumbersEqual(ranges![0], [5, 5], [1, 5], [0, 5], [0, 6]); + }); + test('Smart select on third level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + ` * level 1`, + ` * level 1`, + `* level 0`)); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [2, 4], [1, 6], [0, 6], [0, 7]); + }); + test('Smart select level 2 then level 1', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + `* level 1`)); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 2], [0, 2], [0, 3]); + }); + test('Smart select last list item', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `- level 1`, + `- level 2`, + `- level 2`, + `- level ${CURSOR}1`)); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [0, 3]); + }); + test('Smart select without multiple ranges', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + ``, + `- ${CURSOR}paragraph`, + `- content`)); + + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [1, 4], [0, 4]); + }); + test('Smart select on second level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level 2`, + ` * level 1`, + ` * level ${CURSOR}1`, + `* level 0`)); + + assertNestedLineNumbersEqual(ranges![0], [5, 5], [1, 5], [0, 5], [0, 6]); + }); + test('Smart select on third level of a list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 0`, + ` * level 1`, + ` * level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + ` * level 1`, + ` * level 1`, + `* level 0`)); + assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [2, 4], [1, 6], [0, 6], [0, 7]); + }); + test('Smart select level 2 then level 1', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `* level 1`, + ` * level ${CURSOR}2`, + ` * level 2`, + `* level 1`)); + assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 2], [0, 2], [0, 3]); + }); + test('Smart select bold', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `stuff here **new${CURSOR}item** and here` + )); + assertNestedRangesEqual(ranges![0], [0, 13, 0, 30], [0, 11, 0, 32], [0, 0, 0, 41]); + }); + test('Smart select link', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `stuff here [text](https${CURSOR}://google.com) and here` + )); + assertNestedRangesEqual(ranges![0], [0, 18, 0, 46], [0, 17, 0, 47], [0, 11, 0, 47], [0, 0, 0, 56]); + }); + test('Smart select brackets', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `stuff here [te${CURSOR}xt](https://google.com) and here` + )); + assertNestedRangesEqual(ranges![0], [0, 12, 0, 26], [0, 11, 0, 27], [0, 11, 0, 47], [0, 0, 0, 56]); + }); + test('Smart select brackets under header in list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `- list`, + `paragraph`, + `## sub header`, + `- list`, + `- stuff here [te${CURSOR}xt](https://google.com) and here`, + `- list` + )); + assertNestedRangesEqual(ranges![0], [6, 14, 6, 28], [6, 13, 6, 29], [6, 13, 6, 49], [6, 0, 6, 58], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); + }); + test('Smart select link under header in list', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `- list`, + `paragraph`, + `## sub header`, + `- list`, + `- stuff here [text](${CURSOR}https://google.com) and here`, + `- list` + )); + assertNestedRangesEqual(ranges![0], [6, 20, 6, 48], [6, 19, 6, 49], [6, 13, 6, 49], [6, 0, 6, 58], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); + }); + test('Smart select bold within list where multiple bold elements exists', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `# main header 1`, + ``, + `- list`, + `paragraph`, + `## sub header`, + `- list`, + `- stuff here [text] **${CURSOR}items in here** and **here**`, + `- list` + )); + assertNestedRangesEqual(ranges![0], [6, 22, 6, 45], [6, 20, 6, 47], [6, 0, 6, 60], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); + }); + test('Smart select link in paragraph with multiple links', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `This[extension](https://marketplace.visualstudio.com/items?itemName=meganrogge.template-string-converter) addresses this [requ${CURSOR}est](https://github.com/microsoft/vscode/issues/56704) to convert Javascript/Typescript quotes to backticks when has been entered within a string.` + )); + assertNestedRangesEqual(ranges![0], [0, 123, 0, 140], [0, 122, 0, 141], [0, 122, 0, 191], [0, 0, 0, 283]); + }); + test('Smart select bold link', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `**[extens${CURSOR}ion](https://google.com)**` + )); + assertNestedRangesEqual(ranges![0], [0, 3, 0, 22], [0, 2, 0, 23], [0, 2, 0, 43], [0, 2, 0, 43], [0, 0, 0, 45], [0, 0, 0, 45]); + }); + test('Smart select inline code block', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `[\`code ${CURSOR} link\`]` + )); + assertNestedRangesEqual(ranges![0], [0, 2, 0, 22], [0, 1, 0, 23], [0, 0, 0, 24]); + }); + test('Smart select link with inline code block text', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `[\`code ${CURSOR} link\`](http://example.com)` + )); + assertNestedRangesEqual(ranges![0], [0, 2, 0, 22], [0, 1, 0, 23], [0, 1, 0, 23], [0, 0, 0, 24], [0, 0, 0, 44], [0, 0, 0, 44]); + }); + test('Smart select italic', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `*some nice ${CURSOR}text*` + )); + assertNestedRangesEqual(ranges![0], [0, 1, 0, 25], [0, 0, 0, 26], [0, 0, 0, 26]); + }); + test('Smart select italic link', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `*[extens${CURSOR}ion](https://google.com)*` + )); + assertNestedRangesEqual(ranges![0], [0, 2, 0, 21], [0, 1, 0, 22], [0, 1, 0, 42], [0, 1, 0, 42], [0, 0, 0, 43], [0, 0, 0, 43]); + }); + test('Smart select italic on end', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `*word1 word2 word3${CURSOR}*` + )); + assertNestedRangesEqual(ranges![0], [0, 1, 0, 28], [0, 0, 0, 29], [0, 0, 0, 29]); + }); + test('Smart select italic then bold', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `outer text **bold words *italic ${CURSOR} words* bold words** outer text` + )); + assertNestedRangesEqual(ranges![0], [0, 25, 0, 48], [0, 24, 0, 49], [0, 13, 0, 60], [0, 11, 0, 62], [0, 0, 0, 73]); + }); + test('Smart select bold then italic', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `outer text *italic words **bold ${CURSOR} words** italic words* outer text` + )); + assertNestedRangesEqual(ranges![0], [0, 27, 0, 48], [0, 25, 0, 50], [0, 12, 0, 63], [0, 11, 0, 64], [0, 0, 0, 75]); + }); + test('Third level header from release notes', async () => { + const ranges = await getSelectionRangesForDocument( + joinLines( + `---`, + `Order: 60`, + `TOCTitle: October 2020`, + `PageTitle: Visual Studio Code October 2020`, + `MetaDescription: Learn what is new in the Visual Studio Code October 2020 Release (1.51)`, + `MetaSocialImage: 1_51/release-highlights.png`, + `Date: 2020-11-6`, + `DownloadVersion: 1.51.1`, + `---`, + `# October 2020 (version 1.51)`, + ``, + `**Update 1.51.1**: The update addresses these [issues](https://github.com/microsoft/vscode/issues?q=is%3Aissue+milestone%3A%22October+2020+Recovery%22+is%3Aclosed+).`, + ``, + ``, + ``, + `---`, + ``, + `Welcome to the October 2020 release of Visual Studio Code. As announced in the [October iteration plan](https://github.com/microsoft/vscode/issues/108473), we focused on housekeeping GitHub issues and pull requests as documented in our issue grooming guide.`, + ``, + `We also worked with our partners at GitHub on GitHub Codespaces, which ended up being more involved than originally anticipated. To that end, we'll continue working on housekeeping for part of the November iteration.`, + ``, + `During this housekeeping milestone, we also addressed several feature requests and community [pull requests](#thank-you). Read on to learn about new features and settings.`, + ``, + `## Workbench`, + ``, + `### More prominent pinned tabs`, + ``, + `${CURSOR}Pinned tabs will now always show their pin icon, even while inactive, to make them easier to identify. If an editor is both pinned and contains unsaved changes, the icon reflects both states.`, + ``, + `![Inactive pinned tabs showing pin icons](images/1_51/pinned-tabs.png)` + ) + ); + assertNestedRangesEqual(ranges![0], [27, 0, 27, 201], [26, 0, 29, 70], [25, 0, 29, 70], [24, 0, 29, 70], [23, 0, 29, 70], [10, 0, 29, 70], [9, 0, 29, 70]); }); }); -function assertNestedRangesEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) { +function assertNestedLineNumbersEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) { const lineage = getLineage(range); - assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length}`); + assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length} ${getValues(lineage)}`); for (let i = 0; i < lineage.length; i++) { - assertRangesEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`); + assertLineNumbersEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`); + } +} + +function assertNestedRangesEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number, number, number][]) { + const lineage = getLineage(range); + assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length} ${getValues(lineage)}`); + for (let i = 0; i < lineage.length; i++) { + assertLineNumbersEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][2], `parent at a depth of ${i}`); + assert(lineage[i].range.start.character === expectedRanges[i][1], `parent at a depth of ${i} on start char`); + assert(lineage[i].range.end.character === expectedRanges[i][3], `parent at a depth of ${i} on end char`); } } @@ -384,7 +655,13 @@ function getLineage(range: vscode.SelectionRange): vscode.SelectionRange[] { return result; } -function assertRangesEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) { +function getValues(ranges: vscode.SelectionRange[]): string[] { + return ranges.map(range => { + return range.range.start.line + ' ' + range.range.start.character + ' ' + range.range.end.line + ' ' + range.range.end.character; + }); +} + +function assertLineNumbersEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) { assert.strictEqual(selectionRange.range.start.line, startLine, `failed on start line ${message}`); assert.strictEqual(selectionRange.range.end.line, endLine, `failed on end line ${message}`); } diff --git a/extensions/markdown-language-features/src/util/arrays.ts b/extensions/markdown-language-features/src/util/arrays.ts index ec0ed25c..b778a24e 100644 --- a/extensions/markdown-language-features/src/util/arrays.ts +++ b/extensions/markdown-language-features/src/util/arrays.ts @@ -15,8 +15,4 @@ export function equals(one: ReadonlyArray, other: ReadonlyArray, itemEq } return true; -} - -export function flatten(arr: ReadonlyArray[]): T[] { - return ([] as T[]).concat.apply([], arr); } \ No newline at end of file diff --git a/extensions/markdown-language-features/test-workspace/a.md b/extensions/markdown-language-features/test-workspace/a.md index 9d709180..568bad19 100644 --- a/extensions/markdown-language-features/test-workspace/a.md +++ b/extensions/markdown-language-features/test-workspace/a.md @@ -1,4 +1,10 @@ [b](b) -[b](b.md) -[b](./b.md) -[b](/b.md) + +[b.md](b.md) + +[./b.md](./b.md) + +[/b.md](/b.md) + +[b#header1](b#header1) + diff --git a/extensions/markdown-language-features/test-workspace/b.md b/extensions/markdown-language-features/test-workspace/b.md index 730f53ee..524a6cb2 100644 --- a/extensions/markdown-language-features/test-workspace/b.md +++ b/extensions/markdown-language-features/test-workspace/b.md @@ -1,3 +1,5 @@ # b -[](./a) \ No newline at end of file +[./a](./a) + +# header1 \ No newline at end of file diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index ec7424a4..b02362c2 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -6,6 +6,8 @@ "lib": [ "es6", "es2015.promise", + "es2019.array", + "es2020.string", "dom" ] }, diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 5f1895e9..065943fa 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -2131,10 +2131,10 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -highlight.js@9.15.10: - version "9.15.10" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" - integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw== +highlight.js@10.1.2: + version "10.1.2" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c" + integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA== hmac-drbg@^1.0.0: version "1.0.1" diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 8220adc6..60455f02 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -276,7 +276,7 @@ export class AzureActiveDirectoryService { } return new Promise(async (resolve, reject) => { - if (vscode.env.uiKind === vscode.UIKind.Web) { + if (vscode.env.remoteName !== undefined) { resolve(this.loginWithoutLocalServer(scope)); return; } diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 144cdd27..82730c7e 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -34,7 +34,7 @@ The extension fetches data from https://registry.npmjs.org and https://registry. - `npm.autoDetect` - Enable detecting scripts as tasks, the default is `on`. - `npm.runSilent` - Run npm script with the `--silent` option, the default is `false`. -- `npm.packageManager` - The package manager used to run the scripts: `npm`, `yarn` or `pnpm`, the default is `npm`. +- `npm.packageManager` - The package manager used to run the scripts: `auto`, `npm`, `yarn` or `pnpm`, the default is `auto`, which detects your package manager based on your files. - `npm.exclude` - Glob patterns for folders that should be excluded from automatic script detection. The pattern is matched against the **absolute path** of the package.json. For example, to exclude all test folders use '**/test/**'. - `npm.enableScriptExplorer` - Enable an explorer view for npm scripts. - `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`. diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 575a2ef6..5210c529 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -18,15 +18,21 @@ "watch": "gulp watch-extension:npm" }, "dependencies": { + "find-up": "^4.1.0", + "find-yarn-workspace-root": "^2.0.0", "jsonc-parser": "^2.2.1", "minimatch": "^3.0.4", "request-light": "^0.4.0", - "vscode-nls": "^4.1.1" + "vscode-nls": "^4.1.1", + "which-pm": "^2.0.0" }, "devDependencies": { "@types/minimatch": "^3.0.3", "@types/node": "^12.11.7" }, + "resolutions": { + "which-pm/load-yaml-file/**/argparse": "1.0.9" + }, "main": "./out/npmMain", "browser": "./dist/browser/npmBrowserMain", "activationEvents": [ @@ -56,7 +62,7 @@ { "id": "npm", "name": "%view.name%", - "icon": "images/code.svg", + "icon": "$(code)", "visibility": "hidden" } ] @@ -217,11 +223,18 @@ "scope": "resource", "type": "string", "enum": [ + "auto", "npm", "yarn", "pnpm" ], - "default": "npm", + "enumDescriptions": [ + "%config.npm.packageManager.auto%", + "%config.npm.packageManager.npm%", + "%config.npm.packageManager.yarn%", + "%config.npm.packageManager.pnpm%" + ], + "default": "auto", "description": "%config.npm.packageManager%" }, "npm.exclude": { diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 8ecf3746..53ea1848 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -4,6 +4,10 @@ "config.npm.autoDetect": "Controls whether npm scripts should be automatically detected.", "config.npm.runSilent": "Run npm commands with the `--silent` option.", "config.npm.packageManager": "The package manager used to run scripts.", + "config.npm.packageManager.npm": "Use npm as the package manager for running scripts.", + "config.npm.packageManager.yarn": "Use yarn as the package manager for running scripts.", + "config.npm.packageManager.pnpm": "Use pnpm as the package manager for running scripts.", + "config.npm.packageManager.auto": "Auto-detect which package manager to use for running scripts based on lock files and installed package managers.", "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.", "config.npm.scriptExplorerAction": "The default click action used in the npm scripts explorer: `open` or `run`, the default is `open`.", diff --git a/extensions/npm/src/commands.ts b/extensions/npm/src/commands.ts index 7ca92879..4f9131a4 100644 --- a/extensions/npm/src/commands.ts +++ b/extensions/npm/src/commands.ts @@ -15,7 +15,7 @@ import { const localize = nls.loadMessageBundle(); -export function runSelectedScript() { +export function runSelectedScript(context: vscode.ExtensionContext) { let editor = vscode.window.activeTextEditor; if (!editor) { return; @@ -27,15 +27,15 @@ export function runSelectedScript() { let script = findScriptAtPosition(contents, offset); if (script) { - runScript(script, document); + runScript(context, script, document); } else { let message = localize('noScriptFound', 'Could not find a valid npm script at the selection.'); vscode.window.showErrorMessage(message); } } -export async function selectAndRunScriptFromFolder(selectedFolder: vscode.Uri) { - let taskList: FolderTaskItem[] = await detectNpmScriptsForFolder(selectedFolder); +export async function selectAndRunScriptFromFolder(context: vscode.ExtensionContext, selectedFolder: vscode.Uri) { + let taskList: FolderTaskItem[] = await detectNpmScriptsForFolder(context, selectedFolder); if (taskList && taskList.length > 0) { const quickPick = vscode.window.createQuickPick(); diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index c3a827fd..4fb0f063 100644 --- a/extensions/npm/src/features/bowerJSONContribution.ts +++ b/extensions/npm/src/features/bowerJSONContribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkdownString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode'; +import { MarkdownString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, Uri } from 'vscode'; import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; @@ -37,7 +37,7 @@ export class BowerJSONContribution implements IJSONContribution { return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); } - public collectDefaultSuggestions(_resource: string, collector: ISuggestionsCollector): Thenable { + public collectDefaultSuggestions(_resource: Uri, collector: ISuggestionsCollector): Thenable { const defaultValue = { 'name': '${1:name}', 'description': '${2:description}', @@ -53,7 +53,7 @@ export class BowerJSONContribution implements IJSONContribution { return Promise.resolve(null); } - public collectPropertySuggestions(_resource: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): Thenable | null { + public collectPropertySuggestions(_resource: Uri, location: Location, currentWord: string, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): Thenable | null { if (!this.isEnabled()) { return null; } @@ -125,7 +125,7 @@ export class BowerJSONContribution implements IJSONContribution { return null; } - public collectValueSuggestions(_resource: string, location: Location, collector: ISuggestionsCollector): Promise | null { + public collectValueSuggestions(_resource: Uri, location: Location, collector: ISuggestionsCollector): Promise | null { if (!this.isEnabled()) { return null; } @@ -141,7 +141,7 @@ export class BowerJSONContribution implements IJSONContribution { return null; } - public resolveSuggestion(item: CompletionItem): Thenable | null { + public resolveSuggestion(_resource: Uri | undefined, item: CompletionItem): Thenable | null { if (item.kind === CompletionItemKind.Property && item.documentation === '') { return this.getInfo(item.label).then(documentation => { if (documentation) { @@ -182,7 +182,7 @@ export class BowerJSONContribution implements IJSONContribution { }); } - public getInfoContribution(_resource: string, location: Location): Thenable | null { + public getInfoContribution(_resource: Uri, location: Location): Thenable | null { if (!this.isEnabled()) { return null; } diff --git a/extensions/npm/src/features/jsonContributions.ts b/extensions/npm/src/features/jsonContributions.ts index 071d57b3..3954c235 100644 --- a/extensions/npm/src/features/jsonContributions.ts +++ b/extensions/npm/src/features/jsonContributions.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Location, getLocation, createScanner, SyntaxKind, ScanError, JSONScanner } from 'jsonc-parser'; -import { basename } from 'path'; import { BowerJSONContribution } from './bowerJSONContribution'; import { PackageJSONContribution } from './packageJSONContribution'; import { XHRRequest } from 'request-light'; import { CompletionItem, CompletionItemProvider, CompletionList, TextDocument, Position, Hover, HoverProvider, - CancellationToken, Range, MarkedString, DocumentSelector, languages, Disposable + CancellationToken, Range, MarkedString, DocumentSelector, languages, Disposable, Uri } from 'vscode'; export interface ISuggestionsCollector { @@ -23,11 +22,11 @@ export interface ISuggestionsCollector { export interface IJSONContribution { getDocumentSelector(): DocumentSelector; - getInfoContribution(fileName: string, location: Location): Thenable | null; - collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable | null; - collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable | null; - collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable; - resolveSuggestion?(item: CompletionItem): Thenable | null; + getInfoContribution(resourceUri: Uri, location: Location): Thenable | null; + collectPropertySuggestions(resourceUri: Uri, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable | null; + collectValueSuggestions(resourceUri: Uri, location: Location, result: ISuggestionsCollector): Thenable | null; + collectDefaultSuggestions(resourceUri: Uri, result: ISuggestionsCollector): Thenable; + resolveSuggestion?(resourceUri: Uri | undefined, item: CompletionItem): Thenable | null; } export function addJSONProviders(xhr: XHRRequest, canRunNPM: boolean): Disposable { @@ -47,7 +46,6 @@ export class JSONHoverProvider implements HoverProvider { } public provideHover(document: TextDocument, position: Position, _token: CancellationToken): Thenable | null { - const fileName = basename(document.fileName); const offset = document.offsetAt(position); const location = getLocation(document.getText(), offset); if (!location.previousNode) { @@ -55,7 +53,7 @@ export class JSONHoverProvider implements HoverProvider { } const node = location.previousNode; if (node && node.offset <= offset && offset <= node.offset + node.length) { - const promise = this.jsonContribution.getInfoContribution(fileName, location); + const promise = this.jsonContribution.getInfoContribution(document.uri, location); if (promise) { return promise.then(htmlContent => { const range = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length)); @@ -73,12 +71,14 @@ export class JSONHoverProvider implements HoverProvider { export class JSONCompletionItemProvider implements CompletionItemProvider { + private lastResource: Uri | undefined; + constructor(private jsonContribution: IJSONContribution) { } public resolveCompletionItem(item: CompletionItem, _token: CancellationToken): Thenable { if (this.jsonContribution.resolveSuggestion) { - const resolver = this.jsonContribution.resolveSuggestion(item); + const resolver = this.jsonContribution.resolveSuggestion(this.lastResource, item); if (resolver) { return resolver; } @@ -87,8 +87,8 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { } public provideCompletionItems(document: TextDocument, position: Position, _token: CancellationToken): Thenable | null { + this.lastResource = document.uri; - const fileName = basename(document.fileName); const currentWord = this.getCurrentWord(document, position); let overwriteRange: Range; @@ -126,12 +126,12 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { const scanner = createScanner(document.getText(), true); const addValue = !location.previousNode || !this.hasColonAfter(scanner, location.previousNode.offset + location.previousNode.length); const isLast = this.isLast(scanner, document.offsetAt(position)); - collectPromise = this.jsonContribution.collectPropertySuggestions(fileName, location, currentWord, addValue, isLast, collector); + collectPromise = this.jsonContribution.collectPropertySuggestions(document.uri, location, currentWord, addValue, isLast, collector); } else { if (location.path.length === 0) { - collectPromise = this.jsonContribution.collectDefaultSuggestions(fileName, collector); + collectPromise = this.jsonContribution.collectDefaultSuggestions(document.uri, collector); } else { - collectPromise = this.jsonContribution.collectValueSuggestions(fileName, location, collector); + collectPromise = this.jsonContribution.collectValueSuggestions(document.uri, location, collector); } } if (collectPromise) { diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 7fc5475a..3249cb76 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -3,13 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, MarkdownString } from 'vscode'; +import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, MarkdownString, Uri } from 'vscode'; import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; import * as cp from 'child_process'; import * as nls from 'vscode-nls'; +import { dirname } from 'path'; const localize = nls.loadMessageBundle(); const LIMIT = 40; @@ -35,7 +36,7 @@ export class PackageJSONContribution implements IJSONContribution { public constructor(private xhr: XHRRequest, private canRunNPM: boolean) { } - public collectDefaultSuggestions(_fileName: string, result: ISuggestionsCollector): Thenable { + public collectDefaultSuggestions(_resource: Uri, result: ISuggestionsCollector): Thenable { const defaultValue = { 'name': '${1:name}', 'description': '${2:description}', @@ -60,7 +61,7 @@ export class PackageJSONContribution implements IJSONContribution { } public collectPropertySuggestions( - _resource: string, + _resource: Uri, location: Location, currentWord: string, addValue: boolean, @@ -183,7 +184,7 @@ export class PackageJSONContribution implements IJSONContribution { return Promise.resolve(null); } - public async collectValueSuggestions(_fileName: string, location: Location, result: ISuggestionsCollector): Promise { + public async collectValueSuggestions(resource: Uri, location: Location, result: ISuggestionsCollector): Promise { if (!this.isEnabled()) { return null; } @@ -191,7 +192,7 @@ export class PackageJSONContribution implements IJSONContribution { if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const currentKey = location.path[location.path.length - 1]; if (typeof currentKey === 'string') { - const info = await this.fetchPackageInfo(currentKey); + const info = await this.fetchPackageInfo(currentKey, resource); if (info && info.version) { let name = JSON.stringify(info.version); @@ -236,9 +237,9 @@ export class PackageJSONContribution implements IJSONContribution { return str; } - public resolveSuggestion(item: CompletionItem): Thenable | null { + public resolveSuggestion(resource: Uri | undefined, item: CompletionItem): Thenable | null { if (item.kind === CompletionItemKind.Property && !item.documentation) { - return this.fetchPackageInfo(item.label).then(info => { + return this.fetchPackageInfo(item.label, resource).then(info => { if (info) { item.documentation = this.getDocumentation(info.description, info.version, info.homepage); return item; @@ -266,13 +267,13 @@ export class PackageJSONContribution implements IJSONContribution { return false; } - private async fetchPackageInfo(pack: string): Promise { + private async fetchPackageInfo(pack: string, resource: Uri | undefined): Promise { if (!this.isValidNPMName(pack)) { return undefined; // avoid unnecessary lookups } let info: ViewPackageInfo | undefined; if (this.canRunNPM) { - info = await this.npmView(pack); + info = await this.npmView(pack, resource); } if (!info && this.onlineEnabled()) { info = await this.npmjsView(pack); @@ -280,10 +281,11 @@ export class PackageJSONContribution implements IJSONContribution { return info; } - private npmView(pack: string): Promise { + private npmView(pack: string, resource: Uri | undefined): Promise { return new Promise((resolve, _reject) => { const args = ['view', '--json', pack, 'description', 'dist-tags.latest', 'homepage', 'version']; - cp.execFile(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, (error, stdout) => { + let cwd = resource && resource.scheme === 'file' ? dirname(resource.fsPath) : undefined; + cp.execFile(process.platform === 'win32' ? 'npm.cmd' : 'npm', args, { cwd }, (error, stdout) => { if (!error) { try { const content = JSON.parse(stdout); @@ -325,14 +327,14 @@ export class PackageJSONContribution implements IJSONContribution { return undefined; } - public getInfoContribution(_fileName: string, location: Location): Thenable | null { + public getInfoContribution(resource: Uri, location: Location): Thenable | null { if (!this.isEnabled()) { return null; } if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const pack = location.path[location.path.length - 1]; if (typeof pack === 'string') { - return this.fetchPackageInfo(pack).then(info => { + return this.fetchPackageInfo(pack, resource).then(info => { if (info) { return [this.getDocumentation(info.description, info.version, info.homepage)]; } diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index 67cdf56c..568c5ea3 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -31,6 +31,7 @@ export async function activate(context: vscode.ExtensionContext): Promise const canRunNPM = canRunNpmInCurrentWorkspace(); context.subscriptions.push(addJSONProviders(httpRequest.xhr, canRunNPM)); + registerTaskProvider(context); treeDataProvider = registerExplorer(context); @@ -48,7 +49,6 @@ export async function activate(context: vscode.ExtensionContext): Promise } })); - registerTaskProvider(context); registerHoverProvider(context); context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript)); @@ -58,7 +58,7 @@ export async function activate(context: vscode.ExtensionContext): Promise })); context.subscriptions.push(vscode.commands.registerCommand('npm.packageManager', (args) => { if (args instanceof vscode.Uri) { - return getPackageManager(args); + return getPackageManager(context, args); } return ''; })); @@ -71,6 +71,7 @@ function canRunNpmInCurrentWorkspace() { return false; } +let taskProvider: NpmTaskProvider; function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined { if (vscode.workspace.workspaceFolders) { let watcher = vscode.workspace.createFileSystemWatcher('**/package.json'); @@ -82,8 +83,8 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab let workspaceWatcher = vscode.workspace.onDidChangeWorkspaceFolders((_e) => invalidateScriptCaches()); context.subscriptions.push(workspaceWatcher); - let provider: vscode.TaskProvider = new NpmTaskProvider(); - let disposable = vscode.tasks.registerTaskProvider('npm', provider); + taskProvider = new NpmTaskProvider(context); + let disposable = vscode.tasks.registerTaskProvider('npm', taskProvider); context.subscriptions.push(disposable); return disposable; } @@ -92,7 +93,7 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataProvider | undefined { if (vscode.workspace.workspaceFolders) { - let treeDataProvider = new NpmScriptsTreeDataProvider(context); + let treeDataProvider = new NpmScriptsTreeDataProvider(context, taskProvider!); const view = vscode.window.createTreeView('npm', { treeDataProvider: treeDataProvider, showCollapseAll: true }); context.subscriptions.push(view); return treeDataProvider; diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index c1b5f6fd..8617b7f7 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -7,14 +7,18 @@ import { JSONVisitor, visit } from 'jsonc-parser'; import * as path from 'path'; import { commands, Event, EventEmitter, ExtensionContext, + Range, Selection, Task, - TaskGroup, tasks, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, + TaskGroup, tasks, TextDocument, TextDocumentShowOptions, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, window, workspace, WorkspaceFolder } from 'vscode'; import * as nls from 'vscode-nls'; import { createTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, NpmTaskDefinition, - startDebugging + NpmTaskProvider, + startDebugging, + TaskLocation, + TaskWithLocation } from './tasks'; const localize = nls.loadMessageBundle(); @@ -43,7 +47,7 @@ class PackageJSON extends TreeItem { folder: Folder; scripts: NpmScript[] = []; - static getLabel(_folderName: string, relativePath: string): string { + static getLabel(relativePath: string): string { if (relativePath.length > 0) { return path.join(relativePath, packageName); } @@ -51,7 +55,7 @@ class PackageJSON extends TreeItem { } constructor(folder: Folder, relativePath: string) { - super(PackageJSON.getLabel(folder.label!, relativePath), TreeItemCollapsibleState.Expanded); + super(PackageJSON.getLabel(relativePath), TreeItemCollapsibleState.Expanded); this.folder = folder; this.path = relativePath; this.contextValue = 'packageJSON'; @@ -74,15 +78,20 @@ class NpmScript extends TreeItem { task: Task; package: PackageJSON; - constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task) { + constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task, public taskLocation?: TaskLocation) { super(task.name, TreeItemCollapsibleState.None); const command: ExplorerCommands = workspace.getConfiguration('npm').get('scriptExplorerAction') || 'open'; const commandList = { 'open': { title: 'Edit Script', - command: 'npm.openScript', - arguments: [this] + command: 'vscode.open', + arguments: [ + taskLocation?.document, + taskLocation ? { + selection: new Range(taskLocation.line, taskLocation.line) + } : undefined + ] }, 'run': { title: 'Run Script', @@ -123,7 +132,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { private _onDidChangeTreeData: EventEmitter = new EventEmitter(); readonly onDidChangeTreeData: Event = this._onDidChangeTreeData.event; - constructor(context: ExtensionContext) { + constructor(context: ExtensionContext, public taskProvider: NpmTaskProvider) { const subscriptions = context.subscriptions; this.extensionContext = context; subscriptions.push(commands.registerCommand('npm.runScript', this.runScript, this)); @@ -137,7 +146,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } private async debugScript(script: NpmScript) { - startDebugging(script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); + startDebugging(this.extensionContext, script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder()); } private findScript(document: TextDocument, script?: NpmScript): number { @@ -181,7 +190,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { if (!uri) { return; } - let task = createTask('install', 'install', selection.folder.workspaceFolder, uri, undefined, []); + let task = await createTask(this.extensionContext, 'install', 'install', selection.folder.workspaceFolder, uri, undefined, []); tasks.executeTask(task); } @@ -228,7 +237,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { async getChildren(element?: TreeItem): Promise { if (!this.taskTree) { - let taskItems = await tasks.fetchTasks({ type: 'npm' }); + const taskItems = await this.taskProvider.tasksWithLocation; if (taskItems) { this.taskTree = this.buildTaskTree(taskItems); if (this.taskTree.length === 0) { @@ -265,7 +274,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { return fullName === task.name; } - private buildTaskTree(tasks: Task[]): Folder[] | PackageJSON[] | NoScripts[] { + private buildTaskTree(tasks: TaskWithLocation[]): Folder[] | PackageJSON[] | NoScripts[] { let folders: Map = new Map(); let packages: Map = new Map(); @@ -273,22 +282,22 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { let packageJson = null; tasks.forEach(each => { - if (isWorkspaceFolder(each.scope) && !this.isInstallTask(each)) { - folder = folders.get(each.scope.name); + if (isWorkspaceFolder(each.task.scope) && !this.isInstallTask(each.task)) { + folder = folders.get(each.task.scope.name); if (!folder) { - folder = new Folder(each.scope); - folders.set(each.scope.name, folder); + folder = new Folder(each.task.scope); + folders.set(each.task.scope.name, folder); } - let definition: NpmTaskDefinition = each.definition; + let definition: NpmTaskDefinition = each.task.definition; let relativePath = definition.path ? definition.path : ''; - let fullPath = path.join(each.scope.name, relativePath); + let fullPath = path.join(each.task.scope.name, relativePath); packageJson = packages.get(fullPath); if (!packageJson) { packageJson = new PackageJSON(folder, relativePath); folder.addPackage(packageJson); packages.set(fullPath, packageJson); } - let script = new NpmScript(this.extensionContext, packageJson, each); + let script = new NpmScript(this.extensionContext, packageJson, each.task, each.location); packageJson.addScript(script); } }); diff --git a/extensions/npm/src/preferred-pm.ts b/extensions/npm/src/preferred-pm.ts new file mode 100644 index 00000000..f9582bc7 --- /dev/null +++ b/extensions/npm/src/preferred-pm.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import findWorkspaceRoot = require('../node_modules/find-yarn-workspace-root'); +import findUp = require('find-up'); +import * as path from 'path'; +import whichPM = require('which-pm'); +import { Uri, workspace } from 'vscode'; + +async function pathExists(filePath: string) { + try { + await workspace.fs.stat(Uri.file(filePath)); + } catch { + return false; + } + return true; +} + +async function isPNPMPreferred(pkgPath: string) { + if (await pathExists(path.join(pkgPath, 'pnpm-lock.yaml'))) { + return true; + } + if (await pathExists(path.join(pkgPath, 'shrinkwrap.yaml'))) { + return true; + } + if (await findUp('pnpm-lock.yaml', { cwd: pkgPath })) { + return true; + } + + return false; +} + +async function isYarnPreferred(pkgPath: string) { + if (await pathExists(path.join(pkgPath, 'yarn.lock'))) { + return true; + } + + try { + if (typeof findWorkspaceRoot(pkgPath) === 'string') { + return true; + } + } catch (err) { } + + return false; +} + +const isNPMPreferred = (pkgPath: string) => { + return pathExists(path.join(pkgPath, 'package-lock.json')); +}; + +export async function findPreferredPM(pkgPath: string): Promise<{ name: string, multiplePMDetected: boolean }> { + const detectedPackageManagers: string[] = []; + + if (await isNPMPreferred(pkgPath)) { + detectedPackageManagers.push('npm'); + } + + if (await isYarnPreferred(pkgPath)) { + detectedPackageManagers.push('yarn'); + } + + if (await isPNPMPreferred(pkgPath)) { + detectedPackageManagers.push('pnpm'); + } + + const pmUsedForInstallation: { name: string } | null = await whichPM(pkgPath); + + if (pmUsedForInstallation && !detectedPackageManagers.includes(pmUsedForInstallation.name)) { + detectedPackageManagers.push(pmUsedForInstallation.name); + } + + const multiplePMDetected = detectedPackageManagers.length > 1; + + return { + name: detectedPackageManagers[0] || 'npm', + multiplePMDetected + }; +} diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index b1edc275..48482660 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -30,7 +30,7 @@ export function invalidateHoverScriptsCache(document?: TextDocument) { export class NpmScriptHoverProvider implements HoverProvider { - constructor(context: ExtensionContext) { + constructor(private context: ExtensionContext) { context.subscriptions.push(commands.registerCommand('npm.runScriptFromHover', this.runScriptFromHover, this)); context.subscriptions.push(commands.registerCommand('npm.debugScriptFromHover', this.debugScriptFromHover, this)); context.subscriptions.push(workspace.onDidChangeTextDocument((e) => { @@ -98,13 +98,13 @@ export class NpmScriptHoverProvider implements HoverProvider { return `${prefix}[${label}](command:${cmd}?${encodedArgs} "${tooltip}")`; } - public runScriptFromHover(args: any) { + public async runScriptFromHover(args: any) { let script = args.script; let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - let task = createTask(script, `run ${script}`, folder, documentUri); - tasks.executeTask(task); + let task = await createTask(this.context, script, `run ${script}`, folder, documentUri); + await tasks.executeTask(task); } } @@ -113,7 +113,7 @@ export class NpmScriptHoverProvider implements HoverProvider { let documentUri = args.documentUri; let folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - startDebugging(script, dirname(documentUri.fsPath), folder); + startDebugging(this.context, script, dirname(documentUri.fsPath), folder); } } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 02075cf1..fac16a39 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -5,13 +5,14 @@ import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, - DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem + DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem, window, Position, ExtensionContext, env } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import * as minimatch from 'minimatch'; import * as nls from 'vscode-nls'; import { JSONVisitor, visit, ParseErrorCode } from 'jsonc-parser'; +import { findPreferredPM } from './preferred-pm'; const localize = nls.loadMessageBundle(); @@ -27,20 +28,35 @@ export interface FolderTaskItem extends QuickPickItem { type AutoDetect = 'on' | 'off'; -let cachedTasks: Task[] | undefined = undefined; +let cachedTasks: TaskWithLocation[] | undefined = undefined; const INSTALL_SCRIPT = 'install'; +export interface TaskLocation { + document: Uri, + line: Position +} + +export interface TaskWithLocation { + task: Task, + location?: TaskLocation +} + export class NpmTaskProvider implements TaskProvider { - constructor() { + constructor(private context: ExtensionContext) { } - public provideTasks() { - return provideNpmScripts(); + get tasksWithLocation(): Promise { + return provideNpmScripts(this.context); } - public resolveTask(_task: Task): Task | undefined { + public async provideTasks() { + const tasks = await provideNpmScripts(this.context); + return tasks.map(task => task.task); + } + + public resolveTask(_task: Task): Promise | undefined { const npmTask = (_task.definition).script; if (npmTask) { const kind: NpmTaskDefinition = (_task.definition); @@ -54,7 +70,7 @@ export class NpmTaskProvider implements TaskProvider { } else { packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/package.json' }); } - return createTask(kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri); + return createTask(this.context, kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri); } return undefined; } @@ -107,8 +123,27 @@ export function isWorkspaceFolder(value: any): value is WorkspaceFolder { return value && typeof value !== 'number'; } -export function getPackageManager(folder: Uri): string { - return workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); +export async function getPackageManager(extensionContext: ExtensionContext, folder: Uri): Promise { + let packageManagerName = workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); + + if (packageManagerName === 'auto') { + const { name, multiplePMDetected } = await findPreferredPM(folder.fsPath); + packageManagerName = name; + const neverShowWarning = 'npm.multiplePMWarning.neverShow'; + if (multiplePMDetected && !extensionContext.globalState.get(neverShowWarning)) { + const multiplePMWarning = localize('npm.multiplePMWarning', 'Using {0} as the preferred package manager. Found multiple lockfiles for {1}.', packageManagerName, folder.fsPath); + const neverShowAgain = localize('npm.multiplePMWarning.doNotShow', "Do not show again"); + const learnMore = localize('npm.multiplePMWarning.learnMore', "Learn more"); + window.showInformationMessage(multiplePMWarning, learnMore, neverShowAgain).then(result => { + switch (result) { + case neverShowAgain: extensionContext.globalState.update(neverShowWarning, true); break; + case learnMore: env.openExternal(Uri.parse('https://nodejs.dev/learn/the-package-lock-json-file')); + } + }); + } + } + + return packageManagerName; } export async function hasNpmScripts(): Promise { @@ -132,10 +167,10 @@ export async function hasNpmScripts(): Promise { } } -async function detectNpmScripts(): Promise { +async function detectNpmScripts(context: ExtensionContext): Promise { - let emptyTasks: Task[] = []; - let allTasks: Task[] = []; + let emptyTasks: TaskWithLocation[] = []; + let allTasks: TaskWithLocation[] = []; let visitedPackageJsonFiles: Set = new Set(); let folders = workspace.workspaceFolders; @@ -149,7 +184,7 @@ async function detectNpmScripts(): Promise { let paths = await workspace.findFiles(relativePattern, '**/{node_modules,.vscode-test}/**'); for (const path of paths) { if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) { - let tasks = await provideNpmScriptsForFolder(path); + let tasks = await provideNpmScriptsForFolder(context, path); visitedPackageJsonFiles.add(path.fsPath); allTasks.push(...tasks); } @@ -163,7 +198,7 @@ async function detectNpmScripts(): Promise { } -export async function detectNpmScriptsForFolder(folder: Uri): Promise { +export async function detectNpmScriptsForFolder(context: ExtensionContext, folder: Uri): Promise { let folderTasks: FolderTaskItem[] = []; @@ -174,9 +209,9 @@ export async function detectNpmScriptsForFolder(folder: Uri): Promise = new Set(); for (const path of paths) { if (!visitedPackageJsonFiles.has(path.fsPath)) { - let tasks = await provideNpmScriptsForFolder(path); + let tasks = await provideNpmScriptsForFolder(context, path); visitedPackageJsonFiles.add(path.fsPath); - folderTasks.push(...tasks.map(t => ({ label: t.name, task: t }))); + folderTasks.push(...tasks.map(t => ({ label: t.task.name, task: t.task }))); } } return folderTasks; @@ -185,9 +220,9 @@ export async function detectNpmScriptsForFolder(folder: Uri): Promise { +export async function provideNpmScripts(context: ExtensionContext): Promise { if (!cachedTasks) { - cachedTasks = await detectNpmScripts(); + cachedTasks = await detectNpmScripts(context); } return cachedTasks; } @@ -223,8 +258,8 @@ function isDebugScript(script: string): boolean { return match !== null; } -async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise { - let emptyTasks: Task[] = []; +async function provideNpmScriptsForFolder(context: ExtensionContext, packageJsonUri: Uri): Promise { + let emptyTasks: TaskWithLocation[] = []; let folder = workspace.getWorkspaceFolder(packageJsonUri); if (!folder) { @@ -235,11 +270,13 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise return emptyTasks; } - const result: Task[] = []; + const result: TaskWithLocation[] = []; const prePostScripts = getPrePostScripts(scripts); - Object.keys(scripts).forEach(each => { - const task = createTask(each, `run ${each}`, folder!, packageJsonUri, scripts![each]); + + for (const each of scripts.keys()) { + const scriptValue = scripts.get(each)!; + const task = await createTask(context, each, `run ${each}`, folder!, packageJsonUri, scriptValue.script); const lowerCaseTaskName = each.toLowerCase(); if (isBuildTask(lowerCaseTaskName)) { task.group = TaskGroup.Build; @@ -251,13 +288,14 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise } // todo@connor4312: all scripts are now debuggable, what is a 'debug script'? - if (isDebugScript(scripts![each])) { + if (isDebugScript(scriptValue.script)) { task.group = TaskGroup.Rebuild; // hack: use Rebuild group to tag debug scripts } - result.push(task); - }); + result.push({ task, location: scriptValue.location }); + } + // always add npm install (without a problem matcher) - result.push(createTask(INSTALL_SCRIPT, INSTALL_SCRIPT, folder, packageJsonUri, 'install dependencies from package', [])); + result.push({ task: await createTask(context, INSTALL_SCRIPT, INSTALL_SCRIPT, folder, packageJsonUri, 'install dependencies from package', []) }); return result; } @@ -268,7 +306,7 @@ export function getTaskName(script: string, relativePath: string | undefined) { return script; } -export function createTask(script: NpmTaskDefinition | string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Task { +export async function createTask(context: ExtensionContext, script: NpmTaskDefinition | string, cmd: string, folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Promise { let kind: NpmTaskDefinition; if (typeof script === 'string') { kind = { type: 'npm', script: script }; @@ -276,27 +314,27 @@ export function createTask(script: NpmTaskDefinition | string, cmd: string, fold kind = script; } - function getCommandLine(folder: WorkspaceFolder, cmd: string): string { - let packageManager = getPackageManager(folder.uri); + const packageManager = await getPackageManager(context, folder.uri); + async function getCommandLine(cmd: string): Promise { if (workspace.getConfiguration('npm', folder.uri).get('runSilent')) { return `${packageManager} --silent ${cmd}`; } return `${packageManager} ${cmd}`; } - function getRelativePath(folder: WorkspaceFolder, packageJsonUri: Uri): string { + function getRelativePath(packageJsonUri: Uri): string { let rootUri = folder.uri; let absolutePath = packageJsonUri.path.substring(0, packageJsonUri.path.length - 'package.json'.length); return absolutePath.substring(rootUri.path.length + 1); } - let relativePackageJson = getRelativePath(folder, packageJsonUri); + let relativePackageJson = getRelativePath(packageJsonUri); if (relativePackageJson.length) { - kind.path = getRelativePath(folder, packageJsonUri); + kind.path = relativePackageJson; } let taskName = getTaskName(kind.script, relativePackageJson); let cwd = path.dirname(packageJsonUri.fsPath); - const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(getCommandLine(folder, cmd), { cwd: cwd }), matcher); + const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(await getCommandLine(cmd), { cwd: cwd }), matcher); task.detail = detail; return task; } @@ -337,33 +375,22 @@ async function exists(file: string): Promise { }); } -async function readFile(file: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(file, (err, data) => { - if (err) { - reject(err); - } - resolve(data.toString()); - }); - }); -} - -export function runScript(script: string, document: TextDocument) { +export async function runScript(context: ExtensionContext, script: string, document: TextDocument) { let uri = document.uri; let folder = workspace.getWorkspaceFolder(uri); if (folder) { - let task = createTask(script, `run ${script}`, folder, uri); + let task = await createTask(context, script, `run ${script}`, folder, uri); tasks.executeTask(task); } } -export function startDebugging(scriptName: string, cwd: string, folder: WorkspaceFolder) { +export async function startDebugging(context: ExtensionContext, scriptName: string, cwd: string, folder: WorkspaceFolder) { const config: DebugConfiguration = { type: 'pwa-node', request: 'launch', name: `Debug ${scriptName}`, cwd, - runtimeExecutable: getPackageManager(folder.uri), + runtimeExecutable: await getPackageManager(context, folder.uri), runtimeArgs: [ 'run', scriptName, @@ -378,10 +405,11 @@ export function startDebugging(scriptName: string, cwd: string, folder: Workspac export type StringMap = { [s: string]: string; }; -async function findAllScripts(buffer: string): Promise { - let scripts: StringMap = {}; +async function findAllScripts(document: TextDocument, buffer: string): Promise> { + let scripts: Map = new Map(); let script: string | undefined = undefined; let inScripts = false; + let scriptOffset = 0; let visitor: JSONVisitor = { onError(_error: ParseErrorCode, _offset: number, _length: number) { @@ -395,17 +423,18 @@ async function findAllScripts(buffer: string): Promise { onLiteralValue(value: any, _offset: number, _length: number) { if (script) { if (typeof value === 'string') { - scripts[script] = value; + scripts.set(script, { script: value, location: { document: document.uri, line: document.positionAt(scriptOffset) } }); } script = undefined; } }, - onObjectProperty(property: string, _offset: number, _length: number) { + onObjectProperty(property: string, offset: number, _length: number) { if (property === 'scripts') { inScripts = true; } else if (inScripts && !script) { script = property; + scriptOffset = offset; } else { // nested object which is invalid, ignore the script script = undefined; } @@ -493,7 +522,7 @@ export function findScriptAtPosition(buffer: string, offset: number): string | u return foundScript; } -export async function getScripts(packageJsonUri: Uri): Promise { +export async function getScripts(packageJsonUri: Uri): Promise | undefined> { if (packageJsonUri.scheme !== 'file') { return undefined; @@ -505,8 +534,9 @@ export async function getScripts(packageJsonUri: Uri): Promise)", - "decreaseIndentPattern": "^(.*\\*\\/)?\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else:)|\\b((end(if|for(each)?|while|switch)|break);))" + "decreaseIndentPattern": "^(.*\\*\\/)?\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else:)|\\b((end(if|for(each)?|while|switch));))" }, "folding": { "markers": { diff --git a/extensions/rust/cgmanifest.json b/extensions/rust/cgmanifest.json index 5181efba..932a3926 100644 --- a/extensions/rust/cgmanifest.json +++ b/extensions/rust/cgmanifest.json @@ -4,14 +4,14 @@ "component": { "type": "git", "git": { - "name": "language-rust", - "repositoryUrl": "https://github.com/zargony/atom-language-rust", - "commitHash": "7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f" + "name": "rust-syntax", + "repositoryUrl": "https://github.com/dustypomerleau/rust-syntax", + "commitHash": "19f9aa86c0850b98db175754f019a2e79413353e" } }, "license": "MIT", - "description": "The files syntaxes/rust.tmLanguage.json was derived from the Atom package https://atom.io/packages/language-rust.", - "version": "0.4.12" + "description": "A TextMate-style grammar for Rust.", + "version": "0.2.13" } ], "version": 1 diff --git a/extensions/rust/package.json b/extensions/rust/package.json index 66078388..431e9fa2 100644 --- a/extensions/rust/package.json +++ b/extensions/rust/package.json @@ -5,21 +5,32 @@ "version": "1.0.0", "publisher": "vscode", "license": "MIT", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "scripts": { - "update-grammar": "node ../../build/npm/update-grammar.js zargony/atom-language-rust grammars/rust.cson ./syntaxes/rust.tmLanguage.json" + "update-grammar": "node ../../build/npm/update-grammar.js dustypomerleau/rust-syntax syntaxes/rust.tmLanguage.json ./syntaxes/rust.tmLanguage.json" }, "contributes": { - "languages": [{ - "id": "rust", - "extensions": [".rs"], - "aliases": ["Rust", "rust"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "rust", - "path": "./syntaxes/rust.tmLanguage.json", - "scopeName":"source.rust" - }] + "languages": [ + { + "id": "rust", + "extensions": [ + ".rs" + ], + "aliases": [ + "Rust", + "rust" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "rust", + "path": "./syntaxes/rust.tmLanguage.json", + "scopeName": "source.rust" + } + ] } -} \ No newline at end of file +} diff --git a/extensions/rust/syntaxes/rust.tmLanguage.json b/extensions/rust/syntaxes/rust.tmLanguage.json index 784bd8c9..fdced079 100644 --- a/extensions/rust/syntaxes/rust.tmLanguage.json +++ b/extensions/rust/syntaxes/rust.tmLanguage.json @@ -1,690 +1,1135 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/zargony/atom-language-rust/blob/master/grammars/rust.cson", + "This file has been converted from https://github.com/dustypomerleau/rust-syntax/blob/master/syntaxes/rust.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/zargony/atom-language-rust/commit/7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f", + "version": "https://github.com/dustypomerleau/rust-syntax/commit/19f9aa86c0850b98db175754f019a2e79413353e", "name": "Rust", "scopeName": "source.rust", "patterns": [ { - "comment": "Implementation", - "begin": "\\b(impl)\\b", - "end": "\\{", + "comment": "boxed slice literal", + "begin": "(<)(\\[)", "beginCaptures": { "1": { - "name": "storage.type.rust" + "name": "punctuation.brackets.angle.rust" + }, + "2": { + "name": "punctuation.brackets.square.rust" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.brackets.angle.rust" } }, "patterns": [ { - "include": "#block_comment" + "include": "#block-comments" }, { - "include": "#line_comment" + "include": "#comments" }, { - "include": "#sigils" + "include": "#gtypes" }, { - "include": "#mut" + "include": "#lvariables" }, { - "include": "#dyn" + "include": "#lifetimes" }, { - "include": "#ref_lifetime" + "include": "#punctuation" }, { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#where" - }, - { - "name": "storage.type.rust", - "match": "\\bfor\\b" - }, - { - "include": "#type" + "include": "#types" } ] }, { - "include": "#block_doc_comment" - }, - { - "include": "#block_comment" - }, - { - "include": "#line_doc_comment" - }, - { - "include": "#line_comment" - }, - { - "comment": "Attribute", - "name": "meta.attribute.rust", - "begin": "#\\!?\\[", - "end": "\\]", - "patterns": [ - { - "include": "#string_literal" - }, - { - "include": "#block_doc_comment" - }, - { - "include": "#block_comment" - }, - { - "include": "#line_doc_comment" - }, - { - "include": "#line_comment" - } - ] - }, - { - "comment": "Single-quote string literal (character)", - "name": "string.quoted.single.rust", - "match": "b?'([^'\\\\]|\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.))'" - }, - { - "include": "#string_literal" - }, - { - "include": "#raw_string_literal" - }, - { - "comment": "Floating point literal (fraction)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*\\.[0-9][0-9_]*([eE][+-]?[0-9_]+)?(f32|f64)?\\b" - }, - { - "comment": "Floating point literal (exponent)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?[eE][+-]?[0-9_]+(f32|f64)?\\b" - }, - { - "comment": "Floating point literal (typed)", - "name": "constant.numeric.float.rust", - "match": "\\b[0-9][0-9_]*(\\.[0-9][0-9_]*)?([eE][+-]?[0-9_]+)?(f32|f64)\\b" - }, - { - "comment": "Integer literal (decimal)", - "name": "constant.numeric.integer.decimal.rust", - "match": "\\b[0-9][0-9_]*([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (hexadecimal)", - "name": "constant.numeric.integer.hexadecimal.rust", - "match": "\\b0x[a-fA-F0-9_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (octal)", - "name": "constant.numeric.integer.octal.rust", - "match": "\\b0o[0-7_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Integer literal (binary)", - "name": "constant.numeric.integer.binary.rust", - "match": "\\b0b[01_]+([ui](8|16|32|64|128|s|size))?\\b" - }, - { - "comment": "Static storage modifier", - "name": "storage.modifier.static.rust", - "match": "\\bstatic\\b" - }, - { - "comment": "Boolean constant", - "name": "constant.language.boolean.rust", - "match": "\\b(true|false)\\b" - }, - { - "comment": "Control keyword", - "name": "keyword.control.rust", - "match": "\\b(async|await|break|continue|else|if|in|for|loop|match|return|try|while)\\b" - }, - { - "comment": "Keyword", - "name": "keyword.other.rust", - "match": "\\b(crate|extern|mod|let|ref|use|super|move)\\b" - }, - { - "comment": "Reserved keyword", - "name": "invalid.deprecated.rust", - "match": "\\b(abstract|alignof|become|do|final|macro|offsetof|override|priv|proc|pure|sizeof|typeof|virtual|yield)\\b" - }, - { - "include": "#unsafe" - }, - { - "include": "#sigils" - }, - { - "include": "#self" - }, - { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#box" - }, - { - "include": "#lifetime" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#const" - }, - { - "include": "#pub" - }, - { - "comment": "Miscellaneous operator", - "name": "keyword.operator.misc.rust", - "match": "(=>|::|\\bas\\b)" - }, - { - "comment": "Comparison operator", - "name": "keyword.operator.comparison.rust", - "match": "(&&|\\|\\||==|!=)" - }, - { - "comment": "Assignment operator", - "name": "keyword.operator.assignment.rust", - "match": "(\\+=|-=|/=|\\*=|%=|\\^=|&=|\\|=|<<=|>>=|=)" - }, - { - "comment": "Arithmetic operator", - "name": "keyword.operator.arithmetic.rust", - "match": "(!|\\+|-|/|\\*|%|\\^|&|\\||<<|>>)" - }, - { - "comment": "Comparison operator (second group because of regex precedence)", - "name": "keyword.operator.comparison.rust", - "match": "(<=|>=|<|>)" - }, - { - "include": "#core_types" - }, - { - "include": "#core_vars" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "comment": "Built-in macro", - "name": "support.function.builtin.rust", - "match": "\\b(macro_rules|compile_error|format_args|env|option_env|concat_idents|concat|line|column|file|stringify|include|include_str|include_bytes|module_path|cfg)!" - }, - { - "comment": "Core macro", - "name": "support.function.core.rust", - "match": "\\b(panic|assert|assert_eq|assert_ne|debug_assert|debug_assert_eq|debug_assert_ne|try|write|writeln|unreachable|unimplemented)!" - }, - { - "comment": "Standard library macro", - "name": "support.function.std.rust", - "match": "\\b(format|print|println|eprint|eprintln|select|vec)!" - }, - { - "comment": "Logging macro", - "name": "support.function.log.rust", - "match": "\\b(log|error|warn|info|debug|trace|log_enabled)!" - }, - { - "comment": "Invokation of a macro", - "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*\\!)\\s*[({\\[]", + "comment": "macro type metavariables", + "name": "meta.macro.metavariable.type.rust", + "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", "captures": { "1": { + "name": "keyword.operator.macro.dollar.rust" + }, + "3": { + "name": "keyword.other.crate.rust" + }, + "4": { + "name": "entity.name.type.metavariable.rust" + }, + "6": { + "name": "keyword.operator.key-value.rust" + }, + "7": { + "name": "variable.other.metavariable.specifier.rust" + } + }, + "patterns": [ + { + "include": "#keywords" + } + ] + }, + { + "comment": "macro metavariables", + "name": "meta.macro.metavariable.rust", + "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?", + "captures": { + "1": { + "name": "keyword.operator.macro.dollar.rust" + }, + "2": { + "name": "variable.other.metavariable.name.rust" + }, + "4": { + "name": "keyword.operator.key-value.rust" + }, + "5": { + "name": "variable.other.metavariable.specifier.rust" + } + }, + "patterns": [ + { + "include": "#keywords" + } + ] + }, + { + "comment": "macro rules", + "name": "meta.macro.rules.rust", + "match": "\\b(macro_rules!)\\s+(([a-z0-9_]+)|([A-Z][a-z0-9_]*))\\s+(\\{)", + "captures": { + "1": { + "name": "entity.name.function.macro.rules.rust" + }, + "3": { "name": "entity.name.function.macro.rust" + }, + "4": { + "name": "entity.name.type.macro.rust" + }, + "5": { + "name": "punctuation.brackets.curly.rust" } } }, { - "comment": "Function call", - "match": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*\\(", + "comment": "attributes", + "name": "meta.attribute.rust", + "begin": "(#)(\\!?)(\\[)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.attribute.rust" + }, + "2": { + "name": "keyword.operator.attribute.inner.rust" + }, + "3": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#gtypes" + }, + { + "include": "#types" + } + ] + }, + { + "comment": "modules", + "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", "captures": { "1": { - "name": "entity.name.function.rust" + "name": "keyword.control.rust" + }, + "2": { + "name": "entity.name.module.rust" } } }, { - "comment": "Function call with type parameters", - "begin": "\\b([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)\\s*(::)(?=\\s*<.*>\\s*\\()", - "end": "\\(", - "captures": { - "1": { - "name": "entity.name.function.rust" - }, - "2": { - "name": "keyword.operator.misc.rust" - } - }, - "patterns": [ - { - "include": "#type_params" - } - ] - }, - { - "comment": "Function definition", - "begin": "\\b(fn)\\s+([A-Za-z][A-Za-z0-9_]*|_[A-Za-z0-9_]+)", - "end": "[\\{;]", + "comment": "external crate imports", + "name": "meta.import.rust", + "begin": "\\b(extern)\\s+(crate)", "beginCaptures": { "1": { - "name": "keyword.other.fn.rust" + "name": "keyword.control.rust" }, "2": { - "name": "entity.name.function.rust" + "name": "keyword.other.crate.rust" } }, - "patterns": [ - { - "include": "#block_comment" - }, - { - "include": "#line_comment" - }, - { - "include": "#sigils" - }, - { - "include": "#self" - }, - { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#const" - }, - { - "include": "#where" - }, - { - "include": "#unsafe" - }, - { - "comment": "Function arguments", - "match": "\bfn\b", - "name": "keyword.other.fn.rust" - } - ] - }, - { - "comment": "Type declaration", - "begin": "\\b(enum|struct|trait|union)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", - "end": "[\\{\\(;]", - "beginCaptures": { - "1": { - "name": "storage.type.rust" - }, - "2": { - "name": "entity.name.type.rust" - } - }, - "patterns": [ - { - "include": "#block_comment" - }, - { - "include": "#line_comment" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" - }, - { - "include": "#core_types" - }, - { - "include": "#pub" - }, - { - "include": "#where" - } - ] - }, - { - "comment": "Type alias", - "begin": "\\b(type)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", "end": ";", - "beginCaptures": { - "1": { - "name": "storage.type.rust" - }, - "2": { - "name": "entity.name.type.rust" + "endCaptures": { + "0": { + "name": "punctuation.semi.rust" } }, "patterns": [ { - "include": "#block_comment" + "include": "#block-comments" }, { - "include": "#line_comment" + "include": "#comments" }, { - "include": "#sigils" + "include": "#keywords" }, { - "include": "#mut" - }, - { - "include": "#dyn" - }, - { - "include": "#impl" - }, - { - "include": "#lifetime" - }, - { - "include": "#ref_lifetime" - }, - { - "include": "#core_types" - }, - { - "include": "#core_marker" - }, - { - "include": "#core_traits" - }, - { - "include": "#std_types" - }, - { - "include": "#std_traits" - }, - { - "include": "#type_params" + "include": "#punctuation" } ] + }, + { + "comment": "use statements", + "name": "meta.use.rust", + "begin": "\\b(use)\\s", + "beginCaptures": { + "1": { + "name": "keyword.control.rust" + } + }, + "end": ";", + "endCaptures": { + "0": { + "name": "punctuation.semi.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#types" + }, + { + "include": "#lvariables" + } + ] + }, + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#types" + }, + { + "include": "#keywords" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#variables" } ], "repository": { - "block_doc_comment": { - "comment": "Block documentation comment", - "name": "comment.block.documentation.rust", - "begin": "/\\*[\\*!](?![\\*/])", - "end": "\\*/", + "comments": { "patterns": [ { - "include": "#block_doc_comment" + "comment": "documentation comments", + "name": "comment.line.documentation.rust", + "match": "^\\s*///.*" }, { - "include": "#block_comment" + "comment": "line comments", + "name": "comment.line.double-slash.rust", + "match": "\\s*//.*" } ] }, - "block_comment": { - "comment": "Block comment", - "name": "comment.block.rust", - "begin": "/\\*", - "end": "\\*/", + "block-comments": { "patterns": [ { - "include": "#block_doc_comment" + "comment": "block comments", + "name": "comment.block.rust", + "begin": "/\\*(?!\\*)", + "end": "\\*/", + "patterns": [ + { + "include": "#block-comments" + } + ] }, { - "include": "#block_comment" + "comment": "block documentation comments", + "name": "comment.block.documentation.rust", + "begin": "/\\*\\*", + "end": "\\*/", + "patterns": [ + { + "include": "#block-comments" + } + ] } ] }, - "line_doc_comment": { - "comment": "Single-line documentation comment", - "name": "comment.line.documentation.rust", - "begin": "//[!/](?=[^/])", - "end": "$" + "constants": { + "patterns": [ + { + "comment": "ALL CAPS constants", + "name": "constant.other.caps.rust", + "match": "\\b[A-Z]{2}[A-Z0-9_]*\\b" + }, + { + "comment": "constant declarations", + "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "keyword.control.rust" + }, + "2": { + "name": "constant.other.caps.rust" + } + } + }, + { + "comment": "decimal integers and floats", + "name": "constant.numeric.decimal.rust", + "match": "\\b\\d[\\d_]*(\\.?)[\\d_]*(?:(E)([+-])([\\d_]+))?(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "punctuation.separator.dot.decimal.rust" + }, + "2": { + "name": "keyword.operator.exponent.rust" + }, + "3": { + "name": "keyword.operator.exponent.sign.rust" + }, + "4": { + "name": "constant.numeric.decimal.exponent.mantissa.rust" + }, + "5": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "hexadecimal integers", + "name": "constant.numeric.hex.rust", + "match": "\\b0x[\\da-fA-F_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "octal integers", + "name": "constant.numeric.oct.rust", + "match": "\\b0o[0-7_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "binary integers", + "name": "constant.numeric.bin.rust", + "match": "\\b0b[01_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b", + "captures": { + "1": { + "name": "entity.name.type.numeric.rust" + } + } + }, + { + "comment": "booleans", + "name": "constant.language.bool.rust", + "match": "\\btrue|false\\b" + } + ] }, - "line_comment": { - "comment": "Single-line comment", - "name": "comment.line.double-slash.rust", - "begin": "//", - "end": "$" - }, - "escaped_character": { + "escapes": { + "comment": "escapes: ASCII, byte, Unicode, quote, regex", "name": "constant.character.escape.rust", - "match": "\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)" - }, - "string_literal": { - "comment": "Double-quote string literal", - "name": "string.quoted.double.rust", - "begin": "b?\"", - "end": "\"", - "patterns": [ - { - "include": "#escaped_character" - } - ] - }, - "raw_string_literal": { - "comment": "Raw double-quote string literal", - "name": "string.quoted.double.raw.rust", - "begin": "b?r(#*)\"", - "end": "\"\\1" - }, - "sigils": { - "comment": "Sigil", - "name": "keyword.operator.sigil.rust", - "match": "[&*](?=[a-zA-Z0-9_\\(\\[\\|\\\"]+)" - }, - "self": { - "comment": "Self variable", - "name": "variable.language.rust", - "match": "\\bself\\b" - }, - "mut": { - "comment": "Mutable storage modifier", - "name": "storage.modifier.mut.rust", - "match": "\\bmut\\b" - }, - "dyn": { - "comment": "Dynamic modifier", - "name": "storage.modifier.dyn.rust", - "match": "\\bdyn\\b" - }, - "impl": { - "comment": "Existential type modifier", - "name": "storage.modifier.impl.rust", - "match": "\\bimpl\\b" - }, - "box": { - "comment": "Box storage modifier", - "name": "storage.modifier.box.rust", - "match": "\\bbox\\b" - }, - "const": { - "comment": "Const storage modifier", - "name": "storage.modifier.const.rust", - "match": "\\bconst\\b" - }, - "pub": { - "comment": "Visibility modifier", - "name": "storage.modifier.visibility.rust", - "match": "\\bpub\\b" - }, - "unsafe": { - "comment": "Unsafe code keyword", - "name": "keyword.other.unsafe.rust", - "match": "\\bunsafe\\b" - }, - "where": { - "comment": "Generic where clause", - "name": "keyword.other.where.rust", - "match": "\\bwhere\\b" - }, - "lifetime": { - "comment": "Named lifetime", - "name": "storage.modifier.lifetime.rust", - "match": "'([a-zA-Z_][a-zA-Z0-9_]*)\\b", + "match": "(\\\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))", "captures": { "1": { - "name": "entity.name.lifetime.rust" - } - } - }, - "ref_lifetime": { - "comment": "Reference with named lifetime", - "match": "&('([a-zA-Z_][a-zA-Z0-9_]*))\\b", - "captures": { - "1": { - "name": "storage.modifier.lifetime.rust" + "name": "constant.character.escape.backslash.rust" }, "2": { - "name": "entity.name.lifetime.rust" + "name": "constant.character.escape.bit.rust" + }, + "3": { + "name": "constant.character.escape.unicode.rust" + }, + "4": { + "name": "constant.character.escape.unicode.punctuation.rust" + }, + "5": { + "name": "constant.character.escape.unicode.punctuation.rust" } } }, - "core_types": { - "comment": "Built-in/core type", - "name": "storage.type.core.rust", - "match": "\\b(bool|char|usize|isize|u8|u16|u32|u64|u128|i8|i16|i32|i64|i128|f32|f64|str|Self|Option|Result)\\b" - }, - "core_vars": { - "comment": "Core type variant", - "name": "support.constant.core.rust", - "match": "\\b(Some|None|Ok|Err)\\b" - }, - "core_marker": { - "comment": "Core trait (marker)", - "name": "support.type.marker.rust", - "match": "\\b(Copy|Send|Sized|Sync)\\b" - }, - "core_traits": { - "comment": "Core trait", - "name": "support.type.core.rust", - "match": "\\b(Drop|Fn|FnMut|FnOnce|Clone|PartialEq|PartialOrd|Eq|Ord|AsRef|AsMut|Into|From|Default|Iterator|Extend|IntoIterator|DoubleEndedIterator|ExactSizeIterator)\\b" - }, - "std_types": { - "comment": "Standard library type", - "name": "storage.class.std.rust", - "match": "\\b(Box|String|Vec|Path|PathBuf)\\b" - }, - "std_traits": { - "comment": "Standard library trait", - "name": "support.type.std.rust", - "match": "\\b(ToOwned|ToString)\\b" - }, - "type": { - "comment": "A type", - "name": "entity.name.type.rust", - "match": "\\b([A-Za-z][_A-Za-z0-9]*|_[_A-Za-z0-9]+)\\b" - }, - "type_params": { - "comment": "Type parameters", - "name": "meta.type_params.rust", - "begin": "<(?![=<])", - "end": "(?", + "functions": { "patterns": [ { - "include": "#block_comment" + "comment": "pub as a function", + "match": "\\b(pub)(\\()", + "captures": { + "1": { + "name": "keyword.other.rust" + }, + "2": { + "name": "punctuation.brackets.round.rust" + } + } }, { - "include": "#line_comment" + "comment": "function definition", + "name": "meta.function.definition.rust", + "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))", + "beginCaptures": { + "1": { + "name": "keyword.control.fn.rust" + }, + "2": { + "name": "entity.name.function.rust" + }, + "4": { + "name": "punctuation.brackets.round.rust" + }, + "5": { + "name": "punctuation.brackets.angle.rust" + } + }, + "end": "\\{|;", + "endCaptures": { + "0": { + "name": "punctuation.brackets.curly.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] }, { - "include": "#sigils" + "comment": "function/method calls, chaining", + "name": "meta.function.call.rust", + "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.rust" + }, + "2": { + "name": "punctuation.brackets.round.rust" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.brackets.round.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] }, { - "include": "#mut" + "comment": "function/method calls with turbofish", + "name": "meta.function.call.rust", + "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(?=::<.*>\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.rust" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.brackets.round.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#constants" + }, + { + "include": "#gtypes" + }, + { + "include": "#functions" + }, + { + "include": "#lifetimes" + }, + { + "include": "#macros" + }, + { + "include": "#namespaces" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] + } + ] + }, + "keywords": { + "patterns": [ + { + "comment": "control flow keywords", + "name": "keyword.control.rust", + "match": "\\b(async|await|break|continue|do|else|for|if|loop|match|move|return|try|where|while|yield)\\b" }, { - "include": "#dyn" + "comment": "storage keywords", + "name": "storage.type.rust", + "match": "\\b(const|enum|extern|let|macro|mod|struct|trait|type)\\b" }, { - "include": "#impl" + "comment": "storage modifiers", + "name": "storage.modifier.rust", + "match": "\\b(abstract|static)\\b" }, { - "include": "#lifetime" + "comment": "other keywords", + "name": "keyword.other.rust", + "match": "\\b(as|become|box|dyn|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual)\\b" }, { - "include": "#core_types" + "comment": "fn", + "name": "keyword.other.fn.rust", + "match": "\\bfn\\b" }, { - "include": "#core_marker" + "comment": "crate", + "name": "keyword.other.crate.rust", + "match": "\\bcrate\\b" }, { - "include": "#core_traits" + "comment": "mut", + "name": "storage.modifier.mut.rust", + "match": "\\bmut\\b" }, { - "include": "#std_types" + "comment": "math operators", + "name": "keyword.operator.math.rust", + "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))" }, { - "include": "#std_traits" + "comment": "logical operators", + "name": "keyword.operator.logical.rust", + "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)" }, { - "include": "#type_params" + "comment": "logical AND, borrow references", + "name": "keyword.operator.borrow.and.rust", + "match": "&(?![&=])" + }, + { + "comment": "assignment operators", + "name": "keyword.operator.assignment.rust", + "match": "(-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)" + }, + { + "comment": "single equal", + "name": "keyword.operator.assignment.equal.rust", + "match": "(?])=(?!=|>)" + }, + { + "comment": "comparison operators", + "name": "keyword.operator.comparison.rust", + "match": "(=(=)?(?!>)|!=|<=|(?=)" + }, + { + "comment": "less than, greater than (special case)", + "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))", + "captures": { + "1": { + "name": "punctuation.brackets.round.rust" + }, + "2": { + "name": "punctuation.brackets.square.rust" + }, + "3": { + "name": "punctuation.brackets.curly.rust" + }, + "4": { + "name": "keyword.operator.comparison.rust" + }, + "5": { + "name": "punctuation.brackets.round.rust" + }, + "6": { + "name": "punctuation.brackets.square.rust" + }, + "7": { + "name": "punctuation.brackets.curly.rust" + } + } + }, + { + "comment": "namespace operator", + "name": "keyword.operator.namespace.rust", + "match": "::" + }, + { + "comment": "dereference asterisk", + "match": "(\\*)(?=\\w+)", + "captures": { + "1": { + "name": "keyword.operator.dereference.rust" + } + } + }, + { + "comment": "subpattern binding", + "name": "keyword.operator.subpattern.rust", + "match": "@" + }, + { + "comment": "dot access", + "name": "keyword.operator.access.dot.rust", + "match": "\\.(?!\\.)" + }, + { + "comment": "ranges, range patterns", + "name": "keyword.operator.range.rust", + "match": "\\.{2}(=|\\.)?" + }, + { + "comment": "colon", + "name": "keyword.operator.key-value.rust", + "match": ":(?!:)" + }, + { + "comment": "dashrocket, skinny arrow", + "name": "keyword.operator.arrow.skinny.rust", + "match": "->" + }, + { + "comment": "hashrocket, fat arrow", + "name": "keyword.operator.arrow.fat.rust", + "match": "=>" + }, + { + "comment": "dollar macros", + "name": "keyword.operator.macro.dollar.rust", + "match": "\\$" + }, + { + "comment": "question mark operator, questionably sized, macro kleene matcher", + "name": "keyword.operator.question.rust", + "match": "\\?" + } + ] + }, + "interpolations": { + "comment": "curly brace interpolations", + "name": "meta.interpolation.rust", + "match": "({)[^\"{}]*(})", + "captures": { + "1": { + "name": "punctuation.definition.interpolation.rust" + }, + "2": { + "name": "punctuation.definition.interpolation.rust" + } + } + }, + "lifetimes": { + "patterns": [ + { + "comment": "named lifetime parameters", + "match": "(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", + "captures": { + "1": { + "name": "punctuation.definition.lifetime.rust" + }, + "2": { + "name": "entity.name.type.lifetime.rust" + } + } + }, + { + "comment": "borrowing references to named lifetimes", + "match": "(\\&)(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b", + "captures": { + "1": { + "name": "keyword.operator.borrow.rust" + }, + "2": { + "name": "punctuation.definition.lifetime.rust" + }, + "3": { + "name": "entity.name.type.lifetime.rust" + } + } + } + ] + }, + "macros": { + "patterns": [ + { + "comment": "macros", + "name": "meta.macro.rust", + "match": "(([a-z_][A-Za-z0-9_]*!)|([A-Z_][A-Za-z0-9_]*!))", + "captures": { + "2": { + "name": "entity.name.function.macro.rust" + }, + "3": { + "name": "entity.name.type.macro.rust" + } + } + } + ] + }, + "namespaces": { + "patterns": [ + { + "comment": "namespace (non-type, non-function path segment)", + "match": "(?", + "endCaptures": { + "0": { + "name": "punctuation.brackets.angle.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lvariables" + }, + { + "include": "#lifetimes" + }, + { + "include": "#punctuation" + }, + { + "include": "#types" + }, + { + "include": "#variables" + } + ] + }, + { + "comment": "primitive types", + "name": "entity.name.type.primitive.rust", + "match": "\\b(bool|char|str)\\b" + }, + { + "comment": "trait declarations", + "match": "\\b(trait)\\s+([A-Z][A-Za-z0-9]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.trait.rust" + } + } + }, + { + "comment": "struct declarations", + "match": "\\b(struct)\\s+([A-Z][A-Za-z0-9]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.struct.rust" + } + } + }, + { + "comment": "enum declarations", + "match": "\\b(enum)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.enum.rust" + } + } + }, + { + "comment": "type declarations", + "match": "\\b(type)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "captures": { + "1": { + "name": "storage.type.rust" + }, + "2": { + "name": "entity.name.type.declaration.rust" + } + } + }, + { + "comment": "types", + "name": "entity.name.type.rust", + "match": "\\b[A-Z][A-Za-z0-9]*\\b(?!!)" + } + ] + }, + "gtypes": { + "patterns": [ + { + "comment": "option types", + "name": "entity.name.type.option.rust", + "match": "\\b(Some|None)\\b" + }, + { + "comment": "result types", + "name": "entity.name.type.result.rust", + "match": "\\b(Ok|Err)\\b" + } + ] + }, + "punctuation": { + "patterns": [ + { + "comment": "comma", + "name": "punctuation.comma.rust", + "match": "," + }, + { + "comment": "curly braces", + "name": "punctuation.brackets.curly.rust", + "match": "[{}]" + }, + { + "comment": "parentheses, round brackets", + "name": "punctuation.brackets.round.rust", + "match": "[()]" + }, + { + "comment": "semicolon", + "name": "punctuation.semi.rust", + "match": ";" + }, + { + "comment": "square brackets", + "name": "punctuation.brackets.square.rust", + "match": "[\\[\\]]" + }, + { + "comment": "angle brackets", + "name": "punctuation.brackets.angle.rust", + "match": "(?]" + } + ] + }, + "strings": { + "patterns": [ + { + "comment": "double-quoted strings and byte strings", + "name": "string.quoted.double.rust", + "begin": "(b?)(\")", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.string.rust" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.rust" + } + }, + "patterns": [ + { + "include": "#escapes" + }, + { + "include": "#interpolations" + } + ] + }, + { + "comment": "double-quoted raw strings and raw byte strings", + "name": "string.quoted.double.rust", + "begin": "(b?r)(#*)(\")", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.string.raw.rust" + }, + "3": { + "name": "punctuation.definition.string.rust" + } + }, + "end": "(\")(\\2)", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.rust" + }, + "2": { + "name": "punctuation.definition.string.raw.rust" + } + } + }, + { + "comment": "characters and bytes", + "name": "string.quoted.single.char.rust", + "begin": "(b)?(')", + "beginCaptures": { + "1": { + "name": "string.quoted.byte.raw.rust" + }, + "2": { + "name": "punctuation.definition.char.rust" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.char.rust" + } + }, + "patterns": [ + { + "include": "#escapes" + } + ] + } + ] + }, + "lvariables": { + "patterns": [ + { + "comment": "self", + "name": "variable.language.self.rust", + "match": "\\b[Ss]elf\\b" + }, + { + "comment": "super", + "name": "variable.language.super.rust", + "match": "\\bsuper\\b" + } + ] + }, + "variables": { + "patterns": [ + { + "comment": "variables", + "name": "variable.other.rust", + "match": "\\b(?", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -56,13 +100,13 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -88,7 +132,18 @@ } }, { - "c": ": ", + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -110,7 +165,18 @@ } }, { - "c": "{ }", + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -121,14 +187,25 @@ } }, { - "c": "impl", - "t": "source.rust storage.type.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "impl", + "t": "source.rust keyword.other.rust", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -154,8 +231,52 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,13 +298,13 @@ }, { "c": "for", - "t": "source.rust storage.type.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -221,13 +342,13 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -253,7 +374,18 @@ } }, { - "c": ": ", + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -275,7 +407,18 @@ } }, { - "c": "{ }", + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -286,14 +429,25 @@ } }, { - "c": "impl", - "t": "source.rust storage.type.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "impl", + "t": "source.rust keyword.other.rust", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -319,8 +473,52 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -342,13 +540,13 @@ }, { "c": "for", - "t": "source.rust storage.type.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { @@ -375,7 +573,7 @@ }, { "c": "{", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -397,18 +595,18 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +617,7 @@ }, { "c": "foo", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -429,8 +627,8 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": "<", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -440,8 +638,19 @@ } }, { - "c": " -> C", - "t": "source.rust", + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust meta.function.definition.rust punctuation.comma.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -450,9 +659,75 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "->", + "t": "source.rust meta.function.definition.rust keyword.operator.arrow.skinny.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,18 +738,18 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust meta.function.definition.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", - "t": "source.rust", + "c": " ", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -484,7 +759,73 @@ } }, { - "c": " { }", + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust meta.function.definition.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -496,7 +837,18 @@ }, { "c": "}", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,13 +859,222 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "foo", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "<", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ",", + "t": "source.rust meta.function.definition.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust meta.function.definition.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "->", + "t": "source.rust meta.function.definition.rust keyword.operator.arrow.skinny.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "where", + "t": "source.rust meta.function.definition.rust keyword.control.rust", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust meta.function.definition.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust meta.function.definition.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { @@ -528,74 +1089,8 @@ } }, { - "c": "foo", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "", - "t": "source.rust meta.type_params.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " -> C", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "where", - "t": "source.rust keyword.other.where.rust", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, - { - "c": " A: B", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "{ }", - "t": "source.rust", + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -628,6 +1123,28 @@ }, { "c": "Foo", + "t": "source.rust entity.name.type.struct.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", "t": "source.rust entity.name.type.rust", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -638,8 +1155,30 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -661,17 +1200,17 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -682,7 +1221,29 @@ } }, { - "c": "{ }", + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -692,6 +1253,50 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "trait", "t": "source.rust storage.type.rust", @@ -716,6 +1321,28 @@ }, { "c": "Foo", + "t": "source.rust entity.name.type.trait.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "<", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "A", "t": "source.rust entity.name.type.rust", "r": { "dark_plus": "entity.name.type: #4EC9B0", @@ -726,8 +1353,8 @@ } }, { - "c": "", - "t": "source.rust meta.type_params.rust", + "c": ",", + "t": "source.rust punctuation.comma.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -737,7 +1364,29 @@ } }, { - "c": " : C", + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ">", + "t": "source.rust punctuation.brackets.angle.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -747,6 +1396,39 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "C", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, { "c": " ", "t": "source.rust", @@ -760,17 +1442,17 @@ }, { "c": "where", - "t": "source.rust keyword.other.where.rust", + "t": "source.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " A: B", + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -781,7 +1463,29 @@ } }, { - "c": "{ }", + "c": "A", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": ":", + "t": "source.rust keyword.operator.key-value.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -790,5 +1494,49 @@ "light_vs": "default: #000000", "hc_black": "default: #FFFFFF" } + }, + { + "c": "B", + "t": "source.rust entity.name.type.rust", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.rust punctuation.brackets.curly.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } } ] \ No newline at end of file diff --git a/extensions/rust/test/colorize-results/test_rs.json b/extensions/rust/test/colorize-results/test_rs.json index 977fe6bd..82615d43 100644 --- a/extensions/rust/test/colorize-results/test_rs.json +++ b/extensions/rust/test/colorize-results/test_rs.json @@ -1,18 +1,18 @@ [ { "c": "use", - "t": "source.rust keyword.other.rust", + "t": "source.rust meta.use.rust keyword.control.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": " std", - "t": "source.rust", + "c": " ", + "t": "source.rust meta.use.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -21,9 +21,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "std", + "t": "source.rust meta.use.rust entity.name.namespace.rust", + "r": { + "dark_plus": "entity.name.namespace: #4EC9B0", + "light_plus": "entity.name.namespace: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.namespace: #4EC9B0" + } + }, { "c": "::", - "t": "source.rust keyword.operator.misc.rust", + "t": "source.rust meta.use.rust keyword.operator.namespace.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -33,8 +44,19 @@ } }, { - "c": "io;", - "t": "source.rust", + "c": "io", + "t": "source.rust meta.use.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust meta.use.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -45,18 +67,18 @@ }, { "c": "fn", - "t": "source.rust keyword.other.fn.rust", + "t": "source.rust meta.function.definition.rust keyword.control.fn.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { "c": " ", - "t": "source.rust", + "t": "source.rust meta.function.definition.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -67,7 +89,7 @@ }, { "c": "main", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.definition.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -77,8 +99,30 @@ } }, { - "c": "() {", - "t": "source.rust", + "c": "()", + "t": "source.rust meta.function.definition.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust meta.function.definition.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.rust meta.function.definition.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -100,18 +144,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -121,7 +165,18 @@ } }, { - "c": "\"Guess the number!\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Guess the number!", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -132,8 +187,30 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,18 +232,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -176,7 +253,18 @@ } }, { - "c": "\"Please input your guess.\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Please input your guess.", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -187,8 +275,30 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,13 +320,13 @@ }, { "c": "let", - "t": "source.rust keyword.other.rust", + "t": "source.rust storage.type.rust", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" } }, { @@ -242,7 +352,29 @@ } }, { - "c": " guess ", + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "guess", + "t": "source.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -254,7 +386,7 @@ }, { "c": "=", - "t": "source.rust keyword.operator.assignment.rust", + "t": "source.rust keyword.operator.assignment.equal.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -276,18 +408,18 @@ }, { "c": "String", - "t": "source.rust storage.class.std.rust", + "t": "source.rust entity.name.type.rust", "r": { - "dark_plus": "storage: #569CD6", - "light_plus": "storage: #0000FF", - "dark_vs": "storage: #569CD6", - "light_vs": "storage: #0000FF", - "hc_black": "storage: #569CD6" + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" } }, { "c": "::", - "t": "source.rust keyword.operator.misc.rust", + "t": "source.rust keyword.operator.namespace.rust", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -298,139 +430,7 @@ }, { "c": "new", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "();", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " io", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "::", - "t": "source.rust keyword.operator.misc.rust", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "stdin", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "().", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "read_line", - "t": "source.rust entity.name.function.rust", - "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "&", - "t": "source.rust keyword.operator.sigil.rust", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "mut", - "t": "source.rust storage.modifier.mut.rust", - "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" - } - }, - { - "c": " guess)", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " .", - "t": "source.rust", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "ok", - "t": "source.rust entity.name.function.rust", + "t": "source.rust meta.function.call.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -441,6 +441,28 @@ }, { "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -451,8 +473,41 @@ } }, { - "c": " .", - "t": "source.rust", + "c": "io", + "t": "source.rust entity.name.namespace.rust", + "r": { + "dark_plus": "entity.name.namespace: #4EC9B0", + "light_plus": "entity.name.namespace: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.namespace: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.rust keyword.operator.namespace.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "stdin", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -462,8 +517,19 @@ } }, { - "c": "expect", - "t": "source.rust entity.name.function.rust", + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "read_line", + "t": "source.rust meta.function.call.rust entity.name.function.rust", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -474,6 +540,72 @@ }, { "c": "(", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "&", + "t": "source.rust meta.function.call.rust keyword.operator.borrow.and.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "mut", + "t": "source.rust meta.function.call.rust storage.modifier.mut.rust", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6" + } + }, + { + "c": " ", + "t": "source.rust meta.function.call.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "guess", + "t": "source.rust meta.function.call.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -484,8 +616,85 @@ } }, { - "c": "\"Failed to read line\"", - "t": "source.rust string.quoted.double.rust", + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "ok", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.rust keyword.operator.access.dot.rust", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "expect", + "t": "source.rust meta.function.call.rust entity.name.function.rust", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.rust meta.function.call.rust string.quoted.double.rust punctuation.definition.string.rust", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -495,8 +704,41 @@ } }, { - "c": ");", - "t": "source.rust", + "c": "Failed to read line", + "t": "source.rust meta.function.call.rust string.quoted.double.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.rust meta.function.call.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.rust meta.function.call.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,18 +760,18 @@ }, { "c": "println!", - "t": "source.rust support.function.std.rust", + "t": "source.rust meta.macro.rust entity.name.function.macro.rust", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.rust", + "t": "source.rust punctuation.brackets.round.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -539,7 +781,18 @@ } }, { - "c": "\"You guessed: {}\"", + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "You guessed: ", "t": "source.rust string.quoted.double.rust", "r": { "dark_plus": "string: #CE9178", @@ -550,7 +803,40 @@ } }, { - "c": ", guess);", + "c": "{}", + "t": "source.rust string.quoted.double.rust meta.interpolation.rust punctuation.definition.interpolation.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.rust punctuation.comma.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", "t": "source.rust", "r": { "dark_plus": "default: #D4D4D4", @@ -560,9 +846,42 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "guess", + "t": "source.rust variable.other.rust", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.rust punctuation.brackets.round.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "}", - "t": "source.rust", + "t": "source.rust punctuation.brackets.curly.rust", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index 7b43da24..9635a6c5 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -3,6 +3,7 @@ "displayName": "%displayName%", "description": "%description%", "version": "1.0.0", + "enableProposedApi": true, "publisher": "vscode", "license": "MIT", "engines": { diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 0c0c9b85..6bc5a573 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -8,7 +8,8 @@ import * as pathUtils from 'path'; const FILE_LINE_REGEX = /^(\S.*):$/; const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; -const SEARCH_RESULT_SELECTOR = { language: 'search-result' }; +const ELISION_REGEX = /âŸĒ ([0-9]+) characters skipped âŸĢ/g; +const SEARCH_RESULT_SELECTOR = { language: 'search-result', exclusive: true }; const DIRECTIVES = ['# Query:', '# Flags:', '# Including:', '# Excluding:', '# ContextLines:']; const FLAGS = ['RegExp', 'CaseSensitive', 'IgnoreExcludeSettings', 'WordMatch']; @@ -80,12 +81,18 @@ export function activate(context: vscode.ExtensionContext) { return lineResult.allLocations; } - const translateRangeSidewaysBy = (r: vscode.Range, n: number) => - r.with({ start: new vscode.Position(r.start.line, Math.max(0, n - r.start.character)), end: new vscode.Position(r.end.line, Math.max(0, n - r.end.character)) }); + const location = lineResult.locations.find(l => l.originSelectionRange.contains(position)); + if (!location) { + return []; + } + const targetPos = new vscode.Position( + location.targetSelectionRange.start.line, + location.targetSelectionRange.start.character + (position.character - location.originSelectionRange.start.character) + ); return [{ - ...lineResult.location, - targetSelectionRange: translateRangeSidewaysBy(lineResult.location.targetSelectionRange!, position.character - 1) + ...location, + targetSelectionRange: new vscode.Range(targetPos, targetPos), }]; } }), @@ -93,7 +100,7 @@ export function activate(context: vscode.ExtensionContext) { vscode.languages.registerDocumentLinkProvider(SEARCH_RESULT_SELECTOR, { async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { return parseSearchResults(document, token) - .filter(({ type }) => type === 'file') + .filter(isFileLine) .map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri })); } }), @@ -162,7 +169,7 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u } type ParsedSearchFileLine = { type: 'file', location: vscode.LocationLink, allLocations: vscode.LocationLink[], path: string }; -type ParsedSearchResultLine = { type: 'result', location: vscode.LocationLink, isContext: boolean, prefixRange: vscode.Range }; +type ParsedSearchResultLine = { type: 'result', locations: Required[], isContext: boolean, prefixRange: vscode.Range }; type ParsedSearchResults = Array; const isFileLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchFileLine => line.type === 'file'; const isResultLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchResultLine => line.type === 'result'; @@ -211,17 +218,35 @@ function parseSearchResults(document: vscode.TextDocument, token?: vscode.Cancel const lineNumber = +_lineNumber - 1; const resultStart = (indentation + _lineNumber + seperator + resultIndentation).length; const metadataOffset = (indentation + _lineNumber + seperator).length; + const targetRange = new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length); - const location: vscode.LocationLink = { - targetRange: new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length), - targetSelectionRange: new vscode.Range(lineNumber, metadataOffset, lineNumber, metadataOffset), - targetUri: currentTarget, - originSelectionRange: new vscode.Range(i, resultStart, i, line.length), - }; + let lastEnd = resultStart; + let offset = 0; + let locations: Required[] = []; + ELISION_REGEX.lastIndex = resultStart; + for (let match: RegExpExecArray | null; (match = ELISION_REGEX.exec(line));) { + locations.push({ + targetRange, + targetSelectionRange: new vscode.Range(lineNumber, offset, lineNumber, offset), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, lastEnd, i, ELISION_REGEX.lastIndex - match[0].length), + }); - currentTargetLocations?.push(location); + offset += (ELISION_REGEX.lastIndex - lastEnd - match[0].length) + Number(match[1]); + lastEnd = ELISION_REGEX.lastIndex; + } - links[i] = { type: 'result', location, isContext: seperator === ' ', prefixRange: new vscode.Range(i, 0, i, metadataOffset) }; + if (lastEnd < line.length) { + locations.push({ + targetRange, + targetSelectionRange: new vscode.Range(lineNumber, offset, lineNumber, offset), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, lastEnd, i, line.length), + }); + } + + currentTargetLocations?.push(...locations); + links[i] = { type: 'result', locations, isContext: seperator === ' ', prefixRange: new vscode.Range(i, 0, i, metadataOffset) }; } } diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js index fb74d369..c0170e51 100644 --- a/extensions/search-result/syntaxes/generateTMLanguage.js +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -83,6 +83,7 @@ const scopes = { meta: 'meta.resultLine.search', metaSingleLine: 'meta.resultLine.singleLine.search', metaMultiLine: 'meta.resultLine.multiLine.search', + elision: 'comment meta.resultLine.elision', prefix: { meta: 'constant.numeric.integer meta.resultLinePrefix.search', metaContext: 'meta.resultLinePrefix.contextLinePrefix.search', @@ -220,6 +221,10 @@ const plainText = [ '4': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') }, '5': { name: scopes.resultBlock.result.prefix.lineNumber }, } + }, + { + match: 'âŸĒ [0-9]+ characters skipped âŸĢ', + name: [scopes.resultBlock.meta, scopes.resultBlock.result.elision].join(' '), } ]; diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json index e2687fe8..e319191c 100644 --- a/extensions/search-result/syntaxes/searchResult.tmLanguage.json +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -263,6 +263,10 @@ "name": "meta.resultLinePrefix.lineNumber.search" } } + }, + { + "match": "âŸĒ [0-9]+ characters skipped âŸĢ", + "name": "meta.resultBlock.search comment meta.resultLine.elision" } ], "repository": { diff --git a/extensions/sql/build/update-grammar.js b/extensions/sql/build/update-grammar.js new file mode 100644 index 00000000..7f95e256 --- /dev/null +++ b/extensions/sql/build/update-grammar.js @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +var updateGrammar = require('../../../build/npm/update-grammar'); +updateGrammar.update('microsoft/vscode-mssql', 'syntaxes/SQL.plist', './syntaxes/sql.tmLanguage.json', undefined, 'main'); + + diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index 110a300a..3f0ac384 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/sql/package.json b/extensions/sql/package.json index 5063c9d7..437d114c 100644 --- a/extensions/sql/package.json +++ b/extensions/sql/package.json @@ -5,21 +5,32 @@ "version": "1.0.0", "publisher": "vscode", "license": "MIT", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "scripts": { - "update-grammar": "node ../../build/npm/update-grammar.js microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json" + "update-grammar": "node ./build/update-grammar.js" }, "contributes": { - "languages": [{ - "id": "sql", - "extensions": [ ".sql", ".dsql" ], - "aliases": [ "SQL" ], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "sql", - "scopeName": "source.sql", - "path": "./syntaxes/sql.tmLanguage.json" - }] + "languages": [ + { + "id": "sql", + "extensions": [ + ".sql", + ".dsql" + ], + "aliases": [ + "SQL" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "sql", + "scopeName": "source.sql", + "path": "./syntaxes/sql.tmLanguage.json" + } + ] } } diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 446d2008..76b4c39b 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.tmLanguage.json @@ -17,7 +17,7 @@ "name": "text.bracketed" }, { - "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blocksize|bmk|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|cast|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|close|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\s+or\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare(\\s+cursor)?|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|schema|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|waitfor|webmethod|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\\b", + "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|add|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blocksize|bmk|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|close|clustered|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\s+or\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|index|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|matched|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nonclustered|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|schema|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|unique|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|waitfor|webmethod|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\\b", "name": "keyword.other.sql" }, { @@ -131,7 +131,7 @@ "match": "(?xi)\n\n\t\t\t\t# normal stuff, capture 1\n\t\t\t\t \\b(bigint|bigserial|bit|boolean|box|bytea|cidr|circle|date|double\\sprecision|inet|int|integer|line|lseg|macaddr|money|oid|path|point|polygon|real|serial|smallint|sysdate|text)\\b\n\n\t\t\t\t# numeric suffix, capture 2 + 3i\n\t\t\t\t|\\b(bit\\svarying|character\\s(?:varying)?|tinyint|var\\schar|float|interval)\\((\\d+)\\)\n\n\t\t\t\t# optional numeric suffix, capture 4 + 5i\n\t\t\t\t|\\b(char|number|varchar\\d?)\\b(?:\\((\\d+)\\))?\n\n\t\t\t\t# special case, capture 6 + 7i + 8i\n\t\t\t\t|\\b(numeric|decimal)\\b(?:\\((\\d+),(\\d+)\\))?\n\n\t\t\t\t# special case, captures 9, 10i, 11\n\t\t\t\t|\\b(times?)\\b(?:\\((\\d+)\\))?(\\swith(?:out)?\\stime\\szone\\b)?\n\n\t\t\t\t# special case, captures 12, 13, 14i, 15\n\t\t\t\t|\\b(timestamp)(?:(s|tz))?\\b(?:\\((\\d+)\\))?(\\s(with|without)\\stime\\szone\\b)?\n\n\t\t\t" }, { - "match": "(?i:\\b((?:primary|foreign)\\s+key|references|on\\sdelete(\\s+cascade)?|check|constraint)\\b)", + "match": "(?i:\\b((?:primary|foreign)\\s+key|references|on\\sdelete(\\s+cascade)?|nocheck|check|constraint|collate|default)\\b)", "name": "storage.modifier.sql" }, { @@ -139,7 +139,7 @@ "name": "constant.numeric.sql" }, { - "match": "(?i:\\b(select(\\s+distinct)?|insert\\s+(ignore\\s+)?into|update|delete|from|set|where|group\\sby|or|like|and|union(\\s+all)?|having|order\\sby|limit|(inner|cross)\\s+join|join|straight_join|full\\s+outer\\s+join|(left|right)(\\s+outer)?\\s+join|natural(\\s+(left|right)(\\s+outer)?)?\\s+join)\\b)", + "match": "(?i:\\b(select(\\s+distinct)?|insert\\s+(ignore\\s+)?into|update|delete|from|set|where|group\\s+by|or|like|and|union(\\s+all)?|having|order\\s+by|limit|(inner|cross)\\s+join|join|straight_join|full\\s+outer\\s+join|(left|right)(\\s+outer)?\\s+join|natural(\\s+(left|right)(\\s+outer)?)?\\s+join)\\b)", "name": "keyword.other.DML.sql" }, { @@ -239,7 +239,7 @@ "name": "support.function.security.sql" }, { - "match": "(?i)\\b(ascii|char|charindex|concat|difference|format|left|len|lower|ltrim|nchar|patindex|quotename|replace|replicate|reverse|rtrim|soundex|space|str|string_agg|string_escape|string_split|stuff|substring|translate|trim|unicode|upper)\\b", + "match": "(?i)\\b(ascii|char|charindex|concat|difference|format|left|len|lower|ltrim|nchar|nodes|patindex|quotename|replace|replicate|reverse|right|rtrim|soundex|space|str|string_agg|string_escape|string_split|stuff|substring|translate|trim|unicode|upper)\\b", "name": "support.function.string.sql" }, { @@ -516,4 +516,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/theme-abyss/package.json b/extensions/theme-abyss/package.json index bdeb6a2a..9543824f 100644 --- a/extensions/theme-abyss/package.json +++ b/extensions/theme-abyss/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Abyss", + "id": "Abyss", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/abyss-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-abyss/package.nls.json b/extensions/theme-abyss/package.nls.json index 48fbcd85..a25492d7 100644 --- a/extensions/theme-abyss/package.nls.json +++ b/extensions/theme-abyss/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Abyss Theme", - "description": "Abyss theme for Visual Studio Code" -} \ No newline at end of file + "description": "Abyss theme for Visual Studio Code", + "themeLabel": "Abyss" +} diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 642a9e3d..8e97bbb6 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -172,14 +172,14 @@ "scope": "invalid", "settings": { "fontStyle": "", - "foreground": "#F8F8F0" + "foreground": "#A22D44" } }, { "name": "Invalid deprecated", "scope": "invalid.deprecated", "settings": { - "foreground": "#F8F8F0" + "foreground": "#A22D44" } }, { diff --git a/extensions/theme-defaults/package.json b/extensions/theme-defaults/package.json index 56d5fa3f..75e15834 100644 --- a/extensions/theme-defaults/package.json +++ b/extensions/theme-defaults/package.json @@ -11,31 +11,31 @@ "themes": [ { "id": "Default Dark+", - "label": "Dark+ (default dark)", + "label": "%darkPlusColorThemeLabel%", "uiTheme": "vs-dark", "path": "./themes/dark_plus.json" }, { "id": "Default Light+", - "label": "Light+ (default light)", + "label": "%lightPlusColorThemeLabel%", "uiTheme": "vs", "path": "./themes/light_plus.json" }, { "id": "Visual Studio Dark", - "label": "Dark (Visual Studio)", + "label": "%darkColorThemeLabel%", "uiTheme": "vs-dark", "path": "./themes/dark_vs.json" }, { "id": "Visual Studio Light", - "label": "Light (Visual Studio)", + "label": "%lightColorThemeLabel%", "uiTheme": "vs", "path": "./themes/light_vs.json" }, { "id": "Default High Contrast", - "label": "High Contrast", + "label": "%hcColorThemeLabel%", "uiTheme": "hc-black", "path": "./themes/hc_black.json" } @@ -43,9 +43,9 @@ "iconThemes": [ { "id": "vs-minimal", - "label": "Minimal (Visual Studio Code)", + "label": "%minimalIconThemeLabel%", "path": "./fileicons/vs_minimal-icon-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-defaults/package.nls.json b/extensions/theme-defaults/package.nls.json index f03211ee..4cf3da53 100644 --- a/extensions/theme-defaults/package.nls.json +++ b/extensions/theme-defaults/package.nls.json @@ -1,4 +1,10 @@ { "displayName": "Default Themes", - "description": "The default Visual Studio light and dark themes" -} \ No newline at end of file + "description": "The default Visual Studio light and dark themes", + "darkPlusColorThemeLabel": "Dark+ (default dark)", + "lightPlusColorThemeLabel": "Light+ (default light)", + "darkColorThemeLabel": "Dark (Visual Studio)", + "lightColorThemeLabel": "Light (Visual Studio)", + "hcColorThemeLabel": "High Contrast", + "minimalIconThemeLabel": "Minimal (Visual Studio Code)" +} diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index d0382cec..ea03fb3f 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -9,21 +9,14 @@ "statusBarItem.remoteBackground": "#00000000", "sideBarTitle.foreground": "#FFFFFF" }, - "settings": [ - { - "settings": { - "foreground": "#FFFFFF", - "background": "#000000" - } - }, + "tokenColors": [ { "scope": [ "meta.embedded", "source.groovy.embedded" ], "settings": { - "foreground": "#FFFFFF", - "background": "#000000" + "foreground": "#FFFFFF" } }, { diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index 0f82502d..05fce6cd 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -21,9 +21,8 @@ "sideBarSectionHeader.background": "#0000", "sideBarSectionHeader.border": "#61616130", "tab.lastPinnedBorder": "#61616130", - "notebook.focusedCellBackground": "#c8ddf150", - "notebook.cellBorderColor": "#dae3e9", - "notebook.outputContainerBackgroundColor": "#c8ddf150" + "notebook.cellBorderColor": "#E8E8E8", + "statusBarItem.errorBackground": "#c72e0f" }, "semanticHighlighting": true } diff --git a/extensions/theme-kimbie-dark/package.json b/extensions/theme-kimbie-dark/package.json index 1031c34d..7c7ff5ea 100644 --- a/extensions/theme-kimbie-dark/package.json +++ b/extensions/theme-kimbie-dark/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Kimbie Dark", + "id": "Kimbie Dark", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/kimbie-dark-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-kimbie-dark/package.nls.json b/extensions/theme-kimbie-dark/package.nls.json index 85c736ce..0d96b6f4 100644 --- a/extensions/theme-kimbie-dark/package.nls.json +++ b/extensions/theme-kimbie-dark/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Kimbie Dark Theme", - "description": "Kimbie dark theme for Visual Studio Code" -} \ No newline at end of file + "description": "Kimbie dark theme for Visual Studio Code", + "themeLabel": "Kimbie Dark" +} diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index 8997b80d..3453c53d 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -390,7 +390,7 @@ }, { "name": "Invalid", - "scope": "invalid.illegal", + "scope": "invalid", "settings": { "foreground": "#dc3958" } diff --git a/extensions/theme-monokai-dimmed/package.json b/extensions/theme-monokai-dimmed/package.json index 66c4711d..43d950eb 100644 --- a/extensions/theme-monokai-dimmed/package.json +++ b/extensions/theme-monokai-dimmed/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Monokai Dimmed", + "id": "Monokai Dimmed", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/dimmed-monokai-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-monokai-dimmed/package.nls.json b/extensions/theme-monokai-dimmed/package.nls.json index 3d93898e..47baa226 100644 --- a/extensions/theme-monokai-dimmed/package.nls.json +++ b/extensions/theme-monokai-dimmed/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Monokai Dimmed Theme", - "description": "Monokai dimmed theme for Visual Studio Code" -} \ No newline at end of file + "description": "Monokai dimmed theme for Visual Studio Code", + "themeLabel": "Monokai Dimmed" +} diff --git a/extensions/theme-monokai/package.json b/extensions/theme-monokai/package.json index 13b2db10..b21aded1 100644 --- a/extensions/theme-monokai/package.json +++ b/extensions/theme-monokai/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Monokai", + "id": "Monokai", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/monokai-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-monokai/package.nls.json b/extensions/theme-monokai/package.nls.json index 8e8d73c7..a5a17dc5 100644 --- a/extensions/theme-monokai/package.nls.json +++ b/extensions/theme-monokai/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Monokai Theme", - "description": "Monokai theme for Visual Studio Code" -} \ No newline at end of file + "description": "Monokai theme for Visual Studio Code", + "themeLabel": "Monokai" +} diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index aa30b85b..969455d0 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -36,7 +36,7 @@ "tab.border": "#1e1f1c", "tab.inactiveForeground": "#ccccc7", // needs to be bright so it's readable when another editor group is focused "tab.lastPinnedBorder": "#414339", - "widget.shadow": "#000000", + "widget.shadow": "#00000098", "progressBar.background": "#75715E", "badge.background": "#75715E", "badge.foreground": "#f8f8f2", @@ -284,14 +284,14 @@ "scope": "invalid", "settings": { "fontStyle": "", - "foreground": "#F8F8F0" + "foreground": "#F44747" } }, { "name": "Invalid deprecated", "scope": "invalid.deprecated", "settings": { - "foreground": "#F8F8F0" + "foreground": "#F44747" } }, { diff --git a/extensions/theme-quietlight/package.json b/extensions/theme-quietlight/package.json index 0263925e..f2e66e7a 100644 --- a/extensions/theme-quietlight/package.json +++ b/extensions/theme-quietlight/package.json @@ -11,10 +11,11 @@ "contributes": { "themes": [ { - "label": "Quiet Light", + "id": "Quiet Light", + "label": "%themeLabel%", "uiTheme": "vs", "path": "./themes/quietlight-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-quietlight/package.nls.json b/extensions/theme-quietlight/package.nls.json index 1873df05..30354d62 100644 --- a/extensions/theme-quietlight/package.nls.json +++ b/extensions/theme-quietlight/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Quiet Light Theme", - "description": "Quiet light theme for Visual Studio Code" -} \ No newline at end of file + "description": "Quiet light theme for Visual Studio Code", + "themeLabel": "Quiet Light" +} diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 2a12b369..e395b99e 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -45,6 +45,13 @@ "foreground": "#448C27" } }, + { + "name": "Invalid", + "scope": "invalid", + "settings": { + "foreground": "#cd3131" + } + }, { "name": "Invalid - Illegal", "scope": "invalid.illegal", @@ -409,16 +416,37 @@ }, { "name": "Extra: Diff From", - "scope": "meta.diff.header.from-file", + "scope": ["meta.diff.header.from-file", "punctuation.definition.from-file.diff"], "settings": { - "foreground": "#434343" + "foreground": "#4B69C6" } }, { "name": "Extra: Diff To", - "scope": "meta.diff.header.to-file", + "scope": ["meta.diff.header.to-file", "punctuation.definition.to-file.diff"], "settings": { - "foreground": "#434343" + "foreground": "#4B69C6" + } + }, + { + "name": "diff: deleted", + "scope": "markup.deleted.diff", + "settings": { + "foreground": "#C73D20" + } + }, + { + "name": "diff: changed", + "scope": "markup.changed.diff", + "settings": { + "foreground": "#9C5D27" + } + }, + { + "name": "diff: inserted", + "scope": "markup.inserted.diff", + "settings": { + "foreground": "#448C27" } }, { diff --git a/extensions/theme-red/package.json b/extensions/theme-red/package.json index ba751a33..a9920fdf 100644 --- a/extensions/theme-red/package.json +++ b/extensions/theme-red/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Red", + "id": "Red", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/Red-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-red/package.nls.json b/extensions/theme-red/package.nls.json index 680fde60..d58a547e 100644 --- a/extensions/theme-red/package.nls.json +++ b/extensions/theme-red/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Red Theme", - "description": "Red theme for Visual Studio Code" -} \ No newline at end of file + "description": "Red theme for Visual Studio Code", + "themeLabel": "Red" +} diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 20f54daa..4548b981 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "7ea773d195eac3f40261897b49a2499815e9346c" + "commitHash": "4bbf2132df28c71302e305077ce20a811bf7d64b" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index 021e5270..82e4a516 100644 Binary files a/extensions/theme-seti/icons/seti.woff and b/extensions/theme-seti/icons/seti.woff differ diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index bca70445..545003e9 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -438,6 +438,14 @@ "fontCharacter": "\\E032", "fontColor": "#41535b" }, + "_github_light": { + "fontCharacter": "\\E035", + "fontColor": "#bfc2c1" + }, + "_github": { + "fontCharacter": "\\E035", + "fontColor": "#d4d7d6" + }, "_go_light": { "fontCharacter": "\\E036", "fontColor": "#498ba7" @@ -878,519 +886,535 @@ "fontCharacter": "\\E061", "fontColor": "#cbcb41" }, - "_npm_light": { + "_notebook_light": { "fontCharacter": "\\E062", + "fontColor": "#498ba7" + }, + "_notebook": { + "fontCharacter": "\\E062", + "fontColor": "#519aba" + }, + "_npm_light": { + "fontCharacter": "\\E063", "fontColor": "#3b4b52" }, "_npm": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E063", "fontColor": "#41535b" }, "_npm_1_light": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E063", "fontColor": "#b8383d" }, "_npm_1": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E063", "fontColor": "#cc3e44" }, "_npm_ignored_light": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E064", "fontColor": "#3b4b52" }, "_npm_ignored": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E064", "fontColor": "#41535b" }, "_nunjucks_light": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E065", "fontColor": "#7fae42" }, "_nunjucks": { - "fontCharacter": "\\E064", + "fontCharacter": "\\E065", "fontColor": "#8dc149" }, "_ocaml_light": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E066", "fontColor": "#cc6d2e" }, "_ocaml": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E066", "fontColor": "#e37933" }, "_odata_light": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E067", "fontColor": "#cc6d2e" }, "_odata": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E067", "fontColor": "#e37933" }, "_pddl_light": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E068", "fontColor": "#9068b0" }, "_pddl": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E068", "fontColor": "#a074c4" }, "_pdf_light": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E069", "fontColor": "#b8383d" }, "_pdf": { - "fontCharacter": "\\E068", + "fontCharacter": "\\E069", "fontColor": "#cc3e44" }, "_perl_light": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06A", "fontColor": "#498ba7" }, "_perl": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06A", "fontColor": "#519aba" }, "_photoshop_light": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06B", "fontColor": "#498ba7" }, "_photoshop": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06B", "fontColor": "#519aba" }, "_php_light": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06C", "fontColor": "#9068b0" }, "_php": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06C", "fontColor": "#a074c4" }, "_plan_light": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06D", "fontColor": "#7fae42" }, "_plan": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06D", "fontColor": "#8dc149" }, "_platformio_light": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E06E", "fontColor": "#cc6d2e" }, "_platformio": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E06E", "fontColor": "#e37933" }, "_powershell_light": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E06F", "fontColor": "#498ba7" }, "_powershell": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E06F", + "fontColor": "#519aba" + }, + "_prisma_light": { + "fontCharacter": "\\E070", + "fontColor": "#498ba7" + }, + "_prisma": { + "fontCharacter": "\\E070", "fontColor": "#519aba" }, "_prolog_light": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E072", "fontColor": "#cc6d2e" }, "_prolog": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E072", "fontColor": "#e37933" }, "_pug_light": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E073", "fontColor": "#b8383d" }, "_pug": { - "fontCharacter": "\\E071", + "fontCharacter": "\\E073", "fontColor": "#cc3e44" }, "_puppet_light": { - "fontCharacter": "\\E072", + "fontCharacter": "\\E074", "fontColor": "#b7b73b" }, "_puppet": { - "fontCharacter": "\\E072", + "fontCharacter": "\\E074", "fontColor": "#cbcb41" }, "_python_light": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#498ba7" }, "_python": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#519aba" }, "_react_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#498ba7" }, "_react": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#519aba" }, "_react_1_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#cc6d2e" }, "_react_1": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#e37933" }, "_react_2_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#b7b73b" }, "_react_2": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#cbcb41" }, "_reasonml_light": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E078", "fontColor": "#b8383d" }, "_reasonml": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E078", "fontColor": "#cc3e44" }, "_rollup_light": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E079", "fontColor": "#b8383d" }, "_rollup": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E079", "fontColor": "#cc3e44" }, "_ruby_light": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07A", "fontColor": "#b8383d" }, "_ruby": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07A", "fontColor": "#cc3e44" }, "_rust_light": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07B", "fontColor": "#627379" }, "_rust": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07B", "fontColor": "#6d8086" }, "_salesforce_light": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E07C", "fontColor": "#498ba7" }, "_salesforce": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E07C", "fontColor": "#519aba" }, "_sass_light": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07D", "fontColor": "#dd4b78" }, "_sass": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07D", "fontColor": "#f55385" }, "_sbt_light": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07E", "fontColor": "#498ba7" }, "_sbt": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07E", "fontColor": "#519aba" }, "_scala_light": { - "fontCharacter": "\\E07D", + "fontCharacter": "\\E07F", "fontColor": "#b8383d" }, "_scala": { - "fontCharacter": "\\E07D", + "fontCharacter": "\\E07F", "fontColor": "#cc3e44" }, "_shell_light": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E082", "fontColor": "#455155" }, "_shell": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E082", "fontColor": "#4d5a5e" }, "_slim_light": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E083", "fontColor": "#cc6d2e" }, "_slim": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E083", "fontColor": "#e37933" }, "_smarty_light": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E084", "fontColor": "#b7b73b" }, "_smarty": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E084", "fontColor": "#cbcb41" }, "_spring_light": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E085", "fontColor": "#7fae42" }, "_spring": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E085", "fontColor": "#8dc149" }, "_stylelint_light": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E086", "fontColor": "#bfc2c1" }, "_stylelint": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E086", "fontColor": "#d4d7d6" }, "_stylelint_1_light": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E086", "fontColor": "#455155" }, "_stylelint_1": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E086", "fontColor": "#4d5a5e" }, "_stylus_light": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E087", "fontColor": "#7fae42" }, "_stylus": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E087", "fontColor": "#8dc149" }, "_sublime_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E088", "fontColor": "#cc6d2e" }, "_sublime": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E088", "fontColor": "#e37933" }, "_svg_light": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E089", "fontColor": "#9068b0" }, "_svg": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E089", "fontColor": "#a074c4" }, "_svg_1_light": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E089", "fontColor": "#498ba7" }, "_svg_1": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E089", "fontColor": "#519aba" }, "_swift_light": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#cc6d2e" }, "_swift": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08A", "fontColor": "#e37933" }, "_terraform_light": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E08B", "fontColor": "#9068b0" }, "_terraform": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E08B", "fontColor": "#a074c4" }, "_tex_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#498ba7" }, "_tex": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#519aba" }, "_tex_1_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#b7b73b" }, "_tex_1": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#cbcb41" }, "_tex_2_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#cc6d2e" }, "_tex_2": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#e37933" }, "_tex_3_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#bfc2c1" }, "_tex_3": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08C", "fontColor": "#d4d7d6" }, "_todo": { - "fontCharacter": "\\E08C" + "fontCharacter": "\\E08E" }, "_tsconfig_light": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08F", "fontColor": "#498ba7" }, "_tsconfig": { - "fontCharacter": "\\E08D", + "fontCharacter": "\\E08F", "fontColor": "#519aba" }, "_twig_light": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E090", "fontColor": "#7fae42" }, "_twig": { - "fontCharacter": "\\E08E", + "fontCharacter": "\\E090", "fontColor": "#8dc149" }, "_typescript_light": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E091", "fontColor": "#498ba7" }, "_typescript": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E091", "fontColor": "#519aba" }, "_typescript_1_light": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E091", "fontColor": "#b7b73b" }, "_typescript_1": { - "fontCharacter": "\\E08F", + "fontCharacter": "\\E091", "fontColor": "#cbcb41" }, "_vala_light": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E092", "fontColor": "#627379" }, "_vala": { - "fontCharacter": "\\E090", + "fontCharacter": "\\E092", "fontColor": "#6d8086" }, "_video_light": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E093", "fontColor": "#dd4b78" }, "_video": { - "fontCharacter": "\\E091", + "fontCharacter": "\\E093", "fontColor": "#f55385" }, "_vue_light": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E094", "fontColor": "#7fae42" }, "_vue": { - "fontCharacter": "\\E092", + "fontCharacter": "\\E094", "fontColor": "#8dc149" }, "_wasm_light": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E095", "fontColor": "#9068b0" }, "_wasm": { - "fontCharacter": "\\E093", + "fontCharacter": "\\E095", "fontColor": "#a074c4" }, "_wat_light": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E096", "fontColor": "#9068b0" }, "_wat": { - "fontCharacter": "\\E094", + "fontCharacter": "\\E096", "fontColor": "#a074c4" }, "_webpack_light": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E097", "fontColor": "#498ba7" }, "_webpack": { - "fontCharacter": "\\E095", + "fontCharacter": "\\E097", "fontColor": "#519aba" }, "_wgt_light": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E098", "fontColor": "#498ba7" }, "_wgt": { - "fontCharacter": "\\E096", + "fontCharacter": "\\E098", "fontColor": "#519aba" }, "_windows_light": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E099", "fontColor": "#498ba7" }, "_windows": { - "fontCharacter": "\\E097", + "fontCharacter": "\\E099", "fontColor": "#519aba" }, "_word_light": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E09A", "fontColor": "#498ba7" }, "_word": { - "fontCharacter": "\\E098", + "fontCharacter": "\\E09A", "fontColor": "#519aba" }, "_xls_light": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09B", "fontColor": "#7fae42" }, "_xls": { - "fontCharacter": "\\E099", + "fontCharacter": "\\E09B", "fontColor": "#8dc149" }, "_xml_light": { - "fontCharacter": "\\E09A", + "fontCharacter": "\\E09C", "fontColor": "#cc6d2e" }, "_xml": { - "fontCharacter": "\\E09A", + "fontCharacter": "\\E09C", "fontColor": "#e37933" }, "_yarn_light": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09D", "fontColor": "#498ba7" }, "_yarn": { - "fontCharacter": "\\E09B", + "fontCharacter": "\\E09D", "fontColor": "#519aba" }, "_yml_light": { - "fontCharacter": "\\E09C", + "fontCharacter": "\\E09E", "fontColor": "#9068b0" }, "_yml": { - "fontCharacter": "\\E09C", + "fontCharacter": "\\E09E", "fontColor": "#a074c4" }, "_zip_light": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09F", "fontColor": "#b8383d" }, "_zip": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09F", "fontColor": "#cc3e44" }, "_zip_1_light": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09F", "fontColor": "#627379" }, "_zip_1": { - "fontCharacter": "\\E09D", + "fontCharacter": "\\E09F", "fontColor": "#6d8086" } }, @@ -1449,6 +1473,7 @@ "gsp": "_grails", "gql": "_graphql", "graphql": "_graphql", + "graphqls": "_graphql", "haml": "_haml", "hs": "_haskell", "lhs": "_haskell", @@ -1480,6 +1505,8 @@ "stache": "_mustache", "nim": "_nim", "nims": "_nim", + "github-issues": "_github", + "ipynb": "_notebook", "njk": "_nunjucks", "nunjucks": "_nunjucks", "nunjs": "_nunjucks", @@ -1496,6 +1523,7 @@ "pddl": "_pddl", "plan": "_plan", "happenings": "_happenings", + "prisma": "_prisma", "pp": "_puppet", "epp": "_puppet", "spec.jsx": "_react_1", @@ -1786,6 +1814,7 @@ "gsp": "_grails_light", "gql": "_graphql_light", "graphql": "_graphql_light", + "graphqls": "_graphql_light", "haml": "_haml_light", "hs": "_haskell_light", "lhs": "_haskell_light", @@ -1817,6 +1846,8 @@ "stache": "_mustache_light", "nim": "_nim_light", "nims": "_nim_light", + "github-issues": "_github_light", + "ipynb": "_notebook_light", "njk": "_nunjucks_light", "nunjucks": "_nunjucks_light", "nunjs": "_nunjucks_light", @@ -1833,6 +1864,7 @@ "pddl": "_pddl_light", "plan": "_plan_light", "happenings": "_happenings_light", + "prisma": "_prisma_light", "pp": "_puppet_light", "epp": "_puppet_light", "spec.jsx": "_react_1_light", @@ -2066,5 +2098,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/7ea773d195eac3f40261897b49a2499815e9346c" + "version": "https://github.com/jesseweed/seti-ui/commit/4bbf2132df28c71302e305077ce20a811bf7d64b" } \ No newline at end of file diff --git a/extensions/theme-seti/package.json b/extensions/theme-seti/package.json index a9721611..bcdbb320 100644 --- a/extensions/theme-seti/package.json +++ b/extensions/theme-seti/package.json @@ -15,7 +15,7 @@ "iconThemes": [ { "id": "vs-seti", - "label": "Seti (Visual Studio Code)", + "label": "%themeLabel%", "path": "./icons/vs-seti-icon-theme.json" } ] diff --git a/extensions/theme-seti/package.nls.json b/extensions/theme-seti/package.nls.json index 173d99dd..779302d7 100644 --- a/extensions/theme-seti/package.nls.json +++ b/extensions/theme-seti/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Seti File Icon Theme", - "description": "A file icon theme made out of the Seti UI file icons" -} \ No newline at end of file + "description": "A file icon theme made out of the Seti UI file icons", + "themeLabel": "Seti (Visual Studio Code)" +} diff --git a/extensions/theme-solarized-dark/package.json b/extensions/theme-solarized-dark/package.json index 427b50fe..eb7dc5ff 100644 --- a/extensions/theme-solarized-dark/package.json +++ b/extensions/theme-solarized-dark/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Solarized Dark", + "id": "Solarized Dark", + "label": "%themeLabel%", "uiTheme": "vs-dark", "path": "./themes/solarized-dark-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-solarized-dark/package.nls.json b/extensions/theme-solarized-dark/package.nls.json index c226e15f..46228484 100644 --- a/extensions/theme-solarized-dark/package.nls.json +++ b/extensions/theme-solarized-dark/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Solarized Dark Theme", - "description": "Solarized dark theme for Visual Studio Code" -} \ No newline at end of file + "description": "Solarized dark theme for Visual Studio Code", + "themeLabel": "Solarized Dark" +} diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 9289f694..8a9deb0c 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -210,7 +210,9 @@ { "name": "Invalid", "scope": "invalid", - "settings": {} + "settings": { + "foreground": "#D30102" + } }, { "name": "diff: header", diff --git a/extensions/theme-solarized-light/package.json b/extensions/theme-solarized-light/package.json index 3afb2b7e..421aa7a8 100644 --- a/extensions/theme-solarized-light/package.json +++ b/extensions/theme-solarized-light/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Solarized Light", + "id": "Solarized Light", + "label": "%themeLabel%", "uiTheme": "vs", "path": "./themes/solarized-light-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-solarized-light/package.nls.json b/extensions/theme-solarized-light/package.nls.json index f7a3d0ce..a2e9c31f 100644 --- a/extensions/theme-solarized-light/package.nls.json +++ b/extensions/theme-solarized-light/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Solarized Light Theme", - "description": "Solarized light theme for Visual Studio Code" -} \ No newline at end of file + "description": "Solarized light theme for Visual Studio Code", + "themeLabel": "Solarized Light" +} diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 52a096b5..427b7c3c 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -213,7 +213,9 @@ { "name": "Invalid", "scope": "invalid", - "settings": {} + "settings": { + "foreground": "#cd3131" + } }, { "name": "diff: header", diff --git a/extensions/theme-tomorrow-night-blue/package.json b/extensions/theme-tomorrow-night-blue/package.json index 1266ac58..e03f05d8 100644 --- a/extensions/theme-tomorrow-night-blue/package.json +++ b/extensions/theme-tomorrow-night-blue/package.json @@ -9,10 +9,11 @@ "contributes": { "themes": [ { - "label": "Tomorrow Night Blue", + "id": "Tomorrow Night Blue", + "label": "%themeLabel%", "uiTheme": "vs-dark", - "path": "./themes/tomorrow-night-blue-theme.json" + "path": "./themes/tomorrow-night-blue-color-theme.json" } ] } -} \ No newline at end of file +} diff --git a/extensions/theme-tomorrow-night-blue/package.nls.json b/extensions/theme-tomorrow-night-blue/package.nls.json index 044df352..77b44577 100644 --- a/extensions/theme-tomorrow-night-blue/package.nls.json +++ b/extensions/theme-tomorrow-night-blue/package.nls.json @@ -1,4 +1,5 @@ { "displayName": "Tomorrow Night Blue Theme", - "description": "Tomorrow night blue theme for Visual Studio Code" -} \ No newline at end of file + "description": "Tomorrow night blue theme for Visual Studio Code", + "themeLabel": "Tomorrow Night Blue" +} diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json similarity index 96% rename from extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json rename to extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json index b5f836bf..91c13534 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json @@ -27,7 +27,6 @@ "editorGroup.dropBackground": "#25375daa", "peekViewResult.background": "#001c40", "tab.inactiveBackground": "#001c40", - "tab.modifiedBorder": "#FFEEAD", "tab.lastPinnedBorder": "#007acc80", "debugToolBar.background": "#001c40", "titleBar.activeBackground": "#001126", @@ -67,7 +66,7 @@ { "scope": ["meta.embedded", "source.groovy.embedded"], "settings": { - "background": "#002451", + //"background": "#002451", "foreground": "#FFFFFF" } }, @@ -144,16 +143,16 @@ "name": "Invalid", "scope": "invalid", "settings": { - "background": "#F99DA5", + //"background": "#F99DA5", "fontStyle": "", - "foreground": "#FFFFFF" + "foreground": "#a92049" } }, { "name": "Separator", "scope": "meta.separator", "settings": { - "background": "#BBDAFE", + //"background": "#BBDAFE", "foreground": "#FFFFFF" } }, @@ -161,9 +160,9 @@ "name": "Deprecated", "scope": "invalid.deprecated", "settings": { - "background": "#EBBBFF", + //"background": "#EBBBFF", "fontStyle": "", - "foreground": "#FFFFFF" + "foreground": "#cd9731" } }, { @@ -191,8 +190,7 @@ "name": "Diff header", "scope": "meta.diff.header.from-file, meta.diff.header.to-file", "settings": { - "foreground": "#FFFFFF", - "background": "#4271ae" + "foreground": "#4271ae" } }, { diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index 3a42696a..0a81ee86 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/microsoft/TypeScript-TmLanguage", - "commitHash": "fa4e0d3a918db0eab8e5c5be952f3bd649968456" + "commitHash": "59b62cbc624e3a01368e5432d85af0c99a27d49d" } }, "license": "MIT", diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index f22ebbf2..4f7b3c71 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/59b62cbc624e3a01368e5432d85af0c99a27d49d", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -128,8 +128,18 @@ "match": "(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" @@ -484,7 +494,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.ts", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts variable.other.constant.ts entity.name.function.ts" @@ -868,7 +878,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -1108,7 +1118,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.ts entity.name.function.ts" @@ -1248,6 +1258,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1431,7 +1444,7 @@ }, { "name": "meta.arrow.ts", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2466,7 +2479,7 @@ }, { "name": "string.regexp.ts", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.ts" @@ -2625,7 +2638,7 @@ }, { "name": "meta.object.member.ts", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.ts" @@ -2716,7 +2729,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2749,7 +2762,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2785,7 +2798,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.ts" @@ -2973,7 +2986,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -3057,7 +3070,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -3677,7 +3690,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3889,7 +3902,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.ts meta.return.type.arrow.ts keyword.operator.type.annotation.ts" @@ -4208,7 +4221,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?[\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.ts" @@ -4726,7 +4739,7 @@ }, { "name": "string.regexp.ts", - "begin": "((?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\!)?(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" @@ -487,7 +497,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.tsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx variable.other.constant.tsx entity.name.function.tsx" @@ -871,7 +881,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -1111,7 +1121,7 @@ "include": "#comment" }, { - "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(\\#?[_$[:alpha:]][_$[:alnum:]]*)(?:(\\?)|(\\!))?(?=\\s*\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.tsx entity.name.function.tsx" @@ -1251,6 +1261,9 @@ { "include": "#return-type" }, + { + "include": "#type-function-return-type" + }, { "include": "#decl-block" }, @@ -1434,7 +1447,7 @@ }, { "name": "meta.arrow.tsx", - "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -2469,7 +2482,7 @@ }, { "name": "string.regexp.tsx", - "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)*\\])+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.tsx" @@ -2628,7 +2641,7 @@ }, { "name": "meta.object.member.tsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\s*\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/)*\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.tsx" @@ -2719,7 +2732,7 @@ "end": "(?=,|\\})", "patterns": [ { - "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?(?=\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -2752,7 +2765,7 @@ ] }, { - "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=:)\\s*(async)?\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -2788,7 +2801,7 @@ ] }, { - "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "meta.brace.round.tsx" @@ -2976,7 +2989,7 @@ "paren-expression-possibly-arrow": { "patterns": [ { - "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "begin": "(?<=[(=,])\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*))?\\(\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -3060,7 +3073,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -3628,7 +3641,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*\\>)*>\\s*)? # typeparameters\n \\(\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()\\'\\\"\\`]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3840,7 +3853,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.tsx meta.return.type.arrow.tsx keyword.operator.type.annotation.tsx" @@ -4159,7 +4172,7 @@ }, "patterns": [ { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*(?\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<([^<>]|\\<[^<>]+\\>)+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*))))))))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -4677,7 +4690,7 @@ }, { "name": "string.regexp.tsx", - "begin": "((? { public static readonly cancelledCommand: vscode.Command = { // Cancellation is not an error. Just show nothing until we can properly re-compute the code lens @@ -47,7 +47,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP return this.onDidChangeCodeLensesEmitter.event; } - async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return []; diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts index f06411ec..4e5293e7 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts @@ -19,11 +19,9 @@ const localize = nls.loadMessageBundle(); export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider { public async resolveCodeLens( - inputCodeLens: vscode.CodeLens, + codeLens: ReferencesCodeLens, token: vscode.CancellationToken, ): Promise { - const codeLens = inputCodeLens as ReferencesCodeLens; - const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); const response = await this.client.execute('implementation', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document }); if (response.type !== 'response' || !response.body) { diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts index 8fe118de..57a9e599 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts @@ -26,8 +26,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens super(client, _cachedResponse); } - public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise { - const codeLens = inputCodeLens as ReferencesCodeLens; + public async resolveCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise { const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); const response = await this.client.execute('references', args, token, { lowPriority: true, @@ -42,12 +41,9 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens } const locations = response.body.refs + .filter(reference => !reference.isDefinition) .map(reference => - typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)) - .filter(location => - // Exclude original definition from references - !(location.uri.toString() === codeLens.document.toString() && - location.range.start.isEqual(codeLens.range.start))); + typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)); codeLens.command = { title: this.getCodeLensLabel(locations), diff --git a/extensions/typescript-language-features/src/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts index 73f54f20..e316dd6f 100644 --- a/extensions/typescript-language-features/src/languageFeatures/completions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -62,6 +62,13 @@ class MyCompletionItem extends vscode.CompletionItem { // De-prioritze auto-imports // https://github.com/microsoft/vscode/issues/40311 this.sortText = '\uffff' + tsEntry.sortText; + + // Render "fancy" when source is a workspace path + const qualifierCandidate = vscode.workspace.asRelativePath(tsEntry.source); + if (qualifierCandidate !== tsEntry.source) { + this.label2 = { name: tsEntry.name, qualifier: qualifierCandidate }; + } + } else { this.sortText = tsEntry.sortText; } @@ -561,7 +568,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider< type: response?.type ?? 'unknown', count: response?.type === 'response' && response.body ? response.body.entries.length : 0, updateGraphDurationMs: response?.type === 'response' ? response.performanceData?.updateGraphDurationMs : undefined, - createAutoImportProviderProgramDurationMs: response?.type === 'response' ? (response.performanceData as Proto.PerformanceData & { createAutoImportProviderProgramDurationMs?: number })?.createAutoImportProviderProgramDurationMs : undefined, + createAutoImportProviderProgramDurationMs: response?.type === 'response' ? response.performanceData?.createAutoImportProviderProgramDurationMs : undefined, includesPackageJsonImport: includesPackageJsonImport ? 'true' : undefined, }); } diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 03ebff6d..05fa16cf 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -13,18 +13,6 @@ import { isTypeScriptDocument } from '../utils/languageModeIds'; import { equals } from '../utils/objects'; import { ResourceMap } from '../utils/resourceMap'; -namespace Experimental { - // https://github.com/microsoft/TypeScript/pull/37871/ - export interface UserPreferences extends Proto.UserPreferences { - readonly provideRefactorNotApplicableReason?: boolean; - } - - // https://github.com/microsoft/TypeScript/issues/41208 - export interface FormatCodeSettings extends Proto.FormatCodeSettings { - readonly insertSpaceAfterOpeningAndBeforeClosingEmptyBraces?: boolean; - } -} - interface FileConfiguration { readonly formatOptions: Proto.FormatCodeSettings; readonly preferences: Proto.UserPreferences; @@ -141,7 +129,7 @@ export default class FileConfigurationManager extends Disposable { private getFormatOptions( document: vscode.TextDocument, options: vscode.FormattingOptions - ): Experimental.FormatCodeSettings { + ): Proto.FormatCodeSettings { const config = vscode.workspace.getConfiguration( isTypeScriptDocument(document) ? 'typescript.format' : 'javascript.format', document.uri); @@ -185,8 +173,9 @@ export default class FileConfigurationManager extends Disposable { isTypeScriptDocument(document) ? 'typescript.preferences' : 'javascript.preferences', document.uri); - const preferences: Experimental.UserPreferences = { + const preferences: Proto.UserPreferences = { quotePreference: this.getQuoteStylePreference(preferencesConfig), + // @ts-expect-error until TypeScript 4.2 API importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferencesConfig), importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig), allowTextChangesInNewFiles: document.uri.scheme === fileSchemes.file, @@ -210,6 +199,7 @@ export default class FileConfigurationManager extends Disposable { function getImportModuleSpecifierPreference(config: vscode.WorkspaceConfiguration) { switch (config.get('importModuleSpecifier')) { + case 'project-relative': return 'project-relative'; case 'relative': return 'relative'; case 'non-relative': return 'non-relative'; default: return undefined; diff --git a/extensions/typescript-language-features/src/languageFeatures/folding.ts b/extensions/typescript-language-features/src/languageFeatures/folding.ts index 5b18decb..5dc6dbd1 100644 --- a/extensions/typescript-language-features/src/languageFeatures/folding.ts +++ b/extensions/typescript-language-features/src/languageFeatures/folding.ts @@ -54,14 +54,24 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { } const start = range.start.line; - // workaround for #47240 - const end = (range.end.character > 0 && ['}', ']'].includes(document.getText(new vscode.Range(range.end.translate(0, -1), range.end)))) - ? Math.max(range.end.line - 1, range.start.line) - : range.end.line; - + const end = this.adjustFoldingEnd(range, document); return new vscode.FoldingRange(start, end, kind); } + private static readonly foldEndPairCharacters = ['}', ']', ')', '`']; + + private adjustFoldingEnd(range: vscode.Range, document: vscode.TextDocument) { + // workaround for #47240 + if (range.end.character > 0) { + const foldEndCharacter = document.getText(new vscode.Range(range.end.translate(0, -1), range.end)); + if (TypeScriptFoldingProvider.foldEndPairCharacters.includes(foldEndCharacter)) { + return Math.max(range.end.line - 1, range.start.line); + } + } + + return range.end.line; + } + private static getFoldingRangeKind(span: Proto.OutliningSpan): vscode.FoldingRangeKind | undefined { switch (span.kind) { case 'comment': return vscode.FoldingRangeKind.Comment; diff --git a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts index 0965a574..2af23b27 100644 --- a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -51,6 +51,9 @@ class ApplyCodeActionCommand implements Command { } } +type ApplyFixAllCodeAction_args = { + readonly action: VsCodeFixAllCodeAction; +}; class ApplyFixAllCodeAction implements Command { public static readonly ID = '_typescript.applyFixAllCodeAction'; @@ -61,14 +64,7 @@ class ApplyFixAllCodeAction implements Command { private readonly telemetryReporter: TelemetryReporter, ) { } - public async execute( - file: string, - tsAction: Proto.CodeFixAction, - ): Promise { - if (!tsAction.fixId) { - return; - } - + public async execute(args: ApplyFixAllCodeAction_args): Promise { /* __GDPR__ "quickFixAll.execute" : { "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, @@ -78,25 +74,12 @@ class ApplyFixAllCodeAction implements Command { } */ this.telemetryReporter.logTelemetry('quickFixAll.execute', { - fixName: tsAction.fixName + fixName: args.action.tsAction.fixName }); - const args: Proto.GetCombinedCodeFixRequestArgs = { - scope: { - type: 'file', - args: { file } - }, - fixId: tsAction.fixId, - }; - - const response = await this.client.execute('getCombinedCodeFix', args, nulToken); - if (response.type !== 'response' || !response.body) { - return undefined; + if (args.action.combinedResponse) { + await applyCodeActionCommands(this.client, args.action.combinedResponse.body.commands, nulToken); } - - const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); - await vscode.workspace.applyEdit(edit); - await applyCodeActionCommands(this.client, response.body.commands, nulToken); } } @@ -134,13 +117,25 @@ class VsCodeCodeAction extends vscode.CodeAction { constructor( public readonly tsAction: Proto.CodeFixAction, title: string, - kind: vscode.CodeActionKind, - public readonly isFixAll: boolean, + kind: vscode.CodeActionKind ) { super(title, kind); } } +class VsCodeFixAllCodeAction extends VsCodeCodeAction { + constructor( + tsAction: Proto.CodeFixAction, + public readonly file: string, + title: string, + kind: vscode.CodeActionKind + ) { + super(tsAction, title, kind); + } + + public combinedResponse?: Proto.GetCombinedCodeFixResponse; +} + class CodeActionSet { private readonly _actions = new Set(); private readonly _fixAllActions = new Map<{}, VsCodeCodeAction>(); @@ -202,7 +197,7 @@ class SupportedCodeActionProvider { } } -class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { +class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { public static readonly metadata: vscode.CodeActionProviderMetadata = { providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] @@ -257,6 +252,28 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { return allActions; } + public async resolveCodeAction(codeAction: VsCodeCodeAction, token: vscode.CancellationToken): Promise { + if (!(codeAction instanceof VsCodeFixAllCodeAction) || !codeAction.tsAction.fixId) { + return codeAction; + } + + const arg: Proto.GetCombinedCodeFixRequestArgs = { + scope: { + type: 'file', + args: { file: codeAction.file } + }, + fixId: codeAction.tsAction.fixId, + }; + + const response = await this.client.execute('getCombinedCodeFix', arg, token); + if (response.type === 'response') { + codeAction.combinedResponse = response; + codeAction.edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); + } + + return codeAction; + } + private async getFixesForDiagnostic( document: vscode.TextDocument, file: string, @@ -295,7 +312,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction ): VsCodeCodeAction { - const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix, false); + const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix); codeAction.edit = getEditForCodeAction(this.client, tsAction); codeAction.diagnostics = [diagnostic]; codeAction.command = { @@ -328,14 +345,16 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { return results; } - const action = new VsCodeCodeAction( + const action = new VsCodeFixAllCodeAction( tsAction, + file, tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), - vscode.CodeActionKind.QuickFix, true); + vscode.CodeActionKind.QuickFix); + action.diagnostics = [diagnostic]; action.command = { command: ApplyFixAllCodeAction.ID, - arguments: [file, tsAction], + arguments: [{ action }], title: '' }; results.addFixAllAction(tsAction.fixId, action); @@ -370,7 +389,7 @@ function isPreferredFix( action: VsCodeCodeAction, allActions: readonly VsCodeCodeAction[] ): boolean { - if (action.isFixAll) { + if (action instanceof VsCodeFixAllCodeAction) { return false; } @@ -384,7 +403,7 @@ function isPreferredFix( return true; } - if (otherAction.isFixAll) { + if (otherAction instanceof VsCodeFixAllCodeAction) { return true; } diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index ab0186c8..cc8c0a5d 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -20,11 +20,6 @@ import FormattingOptionsManager from './fileConfigurationManager'; const localize = nls.loadMessageBundle(); -namespace Experimental { - export interface RefactorActionInfo extends Proto.RefactorActionInfo { - readonly notApplicableReason?: string; - } -} interface DidApplyRefactoringCommand_Args { readonly codeAction: InlinedCodeAction @@ -58,10 +53,13 @@ class DidApplyRefactoringCommand implements Command { const renameLocation = args.codeAction.renameLocation; if (renameLocation) { - await vscode.commands.executeCommand('editor.action.rename', [ - args.codeAction.document.uri, - typeConverters.Position.fromLocation(renameLocation) - ]); + // Disable renames in interactive playground https://github.com/microsoft/vscode/issues/75137 + if (args.codeAction.document.uri.scheme !== fileSchemes.walkThroughSnippet) { + await vscode.commands.executeCommand('editor.action.rename', [ + args.codeAction.document.uri, + typeConverters.Position.fromLocation(renameLocation) + ]); + } } } } @@ -354,7 +352,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider string | undefined, onCaseInsenitiveFileSystem: boolean ) { - this._pending = new ResourceMap(undefined, { + this._pending = new ResourceMap(pathNormalizer, { onCaseInsenitiveFileSystem }); } @@ -280,7 +281,7 @@ class PendingDiagnostics extends ResourceMap { .sort((a, b) => a.value - b.value) .map(entry => entry.resource); - const map = new ResourceMap(undefined, this.config); + const map = new ResourceMap(this._normalizePath, this.config); for (const resource of orderedResources) { map.set(resource, undefined); } @@ -367,7 +368,7 @@ export default class BufferSyncSupport extends Disposable { const pathNormalizer = (path: vscode.Uri) => this.client.normalizedPath(path); this.syncedBuffers = new SyncedBufferMap(pathNormalizer, { onCaseInsenitiveFileSystem }); this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer, { onCaseInsenitiveFileSystem }); - this.synchronizer = new BufferSynchronizer(client, onCaseInsenitiveFileSystem); + this.synchronizer = new BufferSynchronizer(client, pathNormalizer, onCaseInsenitiveFileSystem); this.updateConfiguration(); vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); @@ -482,7 +483,7 @@ export default class BufferSyncSupport extends Disposable { } } - public interuptGetErr(f: () => R): R { + public interruptGetErr(f: () => R): R { if (!this.pendingGetErr || this.client.configuration.enableProjectDiagnostics // `geterr` happens on seperate server so no need to cancel it. ) { diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index bc06d18e..33845073 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -3,10 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; import type * as Proto from '../protocol'; import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { memoize } from '../utils/memoize'; import { TsServerProcess, TsServerProcessKind } from './server'; + +const localize = nls.loadMessageBundle(); + declare const Worker: any; declare type Worker = any; @@ -37,6 +43,11 @@ export class WorkerServerProcess implements TsServerProcess { args: readonly string[], ) { worker.addEventListener('message', (msg: any) => { + if (msg.data.type === 'log') { + this.output.appendLine(msg.data.body); + return; + } + for (const handler of this._onDataHandlers) { handler(msg.data); } @@ -44,6 +55,11 @@ export class WorkerServerProcess implements TsServerProcess { worker.postMessage(args); } + @memoize + private get output(): vscode.OutputChannel { + return vscode.window.createOutputChannel(localize('channelName', 'TypeScript Server Log')); + } + write(serverRequest: Proto.Request): void { this.worker.postMessage(serverRequest); } diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index a3ad0abb..58fd431e 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -128,7 +128,7 @@ export class TypeScriptServerSpawner { const apiVersion = version.apiVersion || API.defaultVersion; const canceller = cancellerFactory.create(kind, this._tracer); - const { args, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager, canceller.cancellationPipeName); + const { args, tsServerLogFile, tsServerTraceDirectory } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager, canceller.cancellationPipeName); if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { if (tsServerLogFile) { @@ -138,6 +138,14 @@ export class TypeScriptServerSpawner { } } + if (configuration.enableTsServerTracing) { + if (tsServerTraceDirectory) { + this._logger.info(`<${kind}> Trace directory: ${tsServerTraceDirectory}`); + } else { + this._logger.error(`<${kind}> Could not create trace directory`); + } + } + this._logger.info(`<${kind}> Forking...`); const process = this._factory.fork(version.tsServerPath, args, kind, configuration, this._versionManager); this._logger.info(`<${kind}> Starting...`); @@ -173,9 +181,10 @@ export class TypeScriptServerSpawner { apiVersion: API, pluginManager: PluginManager, cancellationPipeName: string | undefined, - ): { args: string[], tsServerLogFile: string | undefined } { + ): { args: string[], tsServerLogFile: string | undefined, tsServerTraceDirectory: string | undefined } { const args: string[] = []; let tsServerLogFile: string | undefined; + let tsServerTraceDirectory: string | undefined; if (kind === TsServerProcessKind.Syntax) { if (apiVersion.gte(API.v401)) { @@ -216,6 +225,13 @@ export class TypeScriptServerSpawner { } } + if (configuration.enableTsServerTracing && !isWeb()) { + tsServerTraceDirectory = this._logDirectoryProvider.getNewLogDirectory(); + if (tsServerTraceDirectory) { + args.push('--traceDirectory', tsServerTraceDirectory); + } + } + if (!isWeb()) { const pluginPaths = this._pluginPathsProvider.getPluginPaths(); @@ -251,7 +267,7 @@ export class TypeScriptServerSpawner { args.push('--validateDefaultNpmLocation'); } - return { args, tsServerLogFile }; + return { args, tsServerLogFile, tsServerTraceDirectory }; } private static isLoggingEnabled(configuration: TypeScriptServiceConfiguration) { diff --git a/extensions/typescript-language-features/src/tsServer/versionStatus.ts b/extensions/typescript-language-features/src/tsServer/versionStatus.ts index 20b9debc..1d4e1289 100644 --- a/extensions/typescript-language-features/src/tsServer/versionStatus.ts +++ b/extensions/typescript-language-features/src/tsServer/versionStatus.ts @@ -58,7 +58,6 @@ class ProjectStatusCommand implements Command { public async execute(): Promise { const info = this._delegate(); - const result = await vscode.window.showQuickPick(coalesce([ this.getProjectItem(info), this.getVersionItem(), @@ -73,7 +72,7 @@ class ProjectStatusCommand implements Command { private getVersionItem(): QuickPickItem { return { label: localize('projectQuickPick.version.label', "Select TypeScript Version..."), - description: this._client.apiVersion.displayName, + description: localize('projectQuickPick.version.description', "[current = {0}]", this._client.apiVersion.displayName), run: () => { this._client.showVersionPicker(); } @@ -169,7 +168,7 @@ export default class VersionStatus extends Disposable { const doc = vscode.window.activeTextEditor.document; if (isTypeScriptDocument(doc)) { - const file = this._client.normalizedPath(doc.uri); + const file = this._client.toOpenedFilePath(doc, { suppressAlertOnFailure: true }); if (file) { this._statusBarEntry.show(); if (!this._ready) { diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index d225ef60..76471a20 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -30,12 +30,6 @@ import * as typeConverters from './utils/typeConverters'; import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'; import * as ProjectStatus from './utils/largeProjectStatus'; -namespace Experimental { - export interface Diagnostic extends Proto.Diagnostic { - readonly reportsDeprecated?: {} - } -} - // Style check diagnostics that can be reported as warnings const styleCheckDiagnostics = new Set([ ...errorCodes.variableDeclaredButNeverUsed, @@ -256,7 +250,7 @@ export default class TypeScriptServiceClientHost extends Disposable { return diagnostics.map(tsDiag => this.tsDiagnosticToVsDiagnostic(tsDiag, source)); } - private tsDiagnosticToVsDiagnostic(diagnostic: Experimental.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any } { + private tsDiagnosticToVsDiagnostic(diagnostic: Proto.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any, reportDeprecated: any } { const { start, end, text } = diagnostic; const range = new vscode.Range(typeConverters.Position.fromLocation(start), typeConverters.Position.fromLocation(end)); const converted = new vscode.Diagnostic(range, text, this.getDiagnosticSeverity(diagnostic)); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 58a78ad1..588925ec 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -147,7 +147,9 @@ export interface ITypeScriptServiceClient { * * @return The normalized path or `undefined` if the document is not open on the server. */ - toOpenedFilePath(document: vscode.TextDocument): string | undefined; + toOpenedFilePath(document: vscode.TextDocument, options?: { + suppressAlertOnFailure?: boolean + }): string | undefined; /** * Checks if `resource` has a given capability. diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 2d9dad63..e4c8c840 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -189,9 +189,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.tracer.updateConfiguration(); if (this.serverState.type === ServerState.Type.Running) { - if (this._configuration.checkJs !== oldConfiguration.checkJs - || this._configuration.experimentalDecorators !== oldConfiguration.experimentalDecorators - ) { + if (!this._configuration.implictProjectConfiguration.isEqualTo(oldConfiguration.implictProjectConfiguration)) { this.setCompilerOptionsForInferredProjects(this._configuration); } @@ -663,14 +661,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this.normalizedPath(resource); } - public toOpenedFilePath(document: vscode.TextDocument): string | undefined { + public toOpenedFilePath(document: vscode.TextDocument, options: { suppressAlertOnFailure?: boolean } = {}): string | undefined { if (!this.bufferSyncSupport.ensureHasBuffer(document.uri)) { - if (!fileSchemes.disabledSchemes.has(document.uri.scheme)) { + if (!options.suppressAlertOnFailure && !fileSchemes.disabledSchemes.has(document.uri.scheme)) { console.error(`Unexpected resource ${document.uri}`); } return undefined; } - return this.toPath(document.uri) || undefined; + return this.toPath(document.uri); } public hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean { @@ -778,7 +776,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public interruptGetErr(f: () => R): R { - return this.bufferSyncSupport.interuptGetErr(f); + return this.bufferSyncSupport.interruptGetErr(f); } private fatalError(command: string, error: unknown): void { diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index f549ff6f..c43a1210 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -7,7 +7,6 @@ import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; import * as objects from '../utils/objects'; -import * as arrays from './arrays'; export enum TsServerLogLevel { Off, @@ -51,6 +50,43 @@ export const enum SeparateSyntaxServerConfiguration { Enabled, } +export class ImplicitProjectConfiguration { + + public readonly checkJs: boolean; + public readonly experimentalDecorators: boolean; + public readonly strictNullChecks: boolean; + public readonly strictFunctionTypes: boolean; + + constructor(configuration: vscode.WorkspaceConfiguration) { + this.checkJs = ImplicitProjectConfiguration.readCheckJs(configuration); + this.experimentalDecorators = ImplicitProjectConfiguration.readExperimentalDecorators(configuration); + this.strictNullChecks = ImplicitProjectConfiguration.readImplicitStrictNullChecks(configuration); + this.strictFunctionTypes = ImplicitProjectConfiguration.readImplicitStrictFunctionTypes(configuration); + } + + public isEqualTo(other: ImplicitProjectConfiguration): boolean { + return objects.equals(this, other); + } + + private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.checkJs') + ?? configuration.get('javascript.implicitProjectConfig.checkJs', false); + } + + private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.experimentalDecorators') + ?? configuration.get('javascript.implicitProjectConfig.experimentalDecorators', false); + } + + private static readImplicitStrictNullChecks(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.strictNullChecks', false); + } + + private static readImplicitStrictFunctionTypes(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('js/ts.implicitProjectConfig.strictFunctionTypes', true); + } +} + export class TypeScriptServiceConfiguration { public readonly locale: string | null; public readonly globalTsdk: string | null; @@ -58,8 +94,7 @@ export class TypeScriptServiceConfiguration { public readonly npmLocation: string | null; public readonly tsServerLogLevel: TsServerLogLevel = TsServerLogLevel.Off; public readonly tsServerPluginPaths: readonly string[]; - public readonly checkJs: boolean; - public readonly experimentalDecorators: boolean; + public readonly implictProjectConfiguration: ImplicitProjectConfiguration; public readonly disableAutomaticTypeAcquisition: boolean; public readonly separateSyntaxServer: SeparateSyntaxServerConfiguration; public readonly enableProjectDiagnostics: boolean; @@ -67,6 +102,7 @@ export class TypeScriptServiceConfiguration { public readonly enablePromptUseWorkspaceTsdk: boolean; public readonly watchOptions: protocol.WatchOptions | undefined; public readonly includePackageJsonAutoImports: 'auto' | 'on' | 'off' | undefined; + public readonly enableTsServerTracing: boolean; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -81,8 +117,7 @@ export class TypeScriptServiceConfiguration { this.npmLocation = TypeScriptServiceConfiguration.readNpmLocation(configuration); this.tsServerLogLevel = TypeScriptServiceConfiguration.readTsServerLogLevel(configuration); this.tsServerPluginPaths = TypeScriptServiceConfiguration.readTsServerPluginPaths(configuration); - this.checkJs = TypeScriptServiceConfiguration.readCheckJs(configuration); - this.experimentalDecorators = TypeScriptServiceConfiguration.readExperimentalDecorators(configuration); + this.implictProjectConfiguration = new ImplicitProjectConfiguration(configuration); this.disableAutomaticTypeAcquisition = TypeScriptServiceConfiguration.readDisableAutomaticTypeAcquisition(configuration); this.separateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration); this.enableProjectDiagnostics = TypeScriptServiceConfiguration.readEnableProjectDiagnostics(configuration); @@ -90,24 +125,11 @@ export class TypeScriptServiceConfiguration { this.enablePromptUseWorkspaceTsdk = TypeScriptServiceConfiguration.readEnablePromptUseWorkspaceTsdk(configuration); this.watchOptions = TypeScriptServiceConfiguration.readWatchOptions(configuration); this.includePackageJsonAutoImports = TypeScriptServiceConfiguration.readIncludePackageJsonAutoImports(configuration); + this.enableTsServerTracing = TypeScriptServiceConfiguration.readEnableTsServerTracing(configuration); } public isEqualTo(other: TypeScriptServiceConfiguration): boolean { - return this.locale === other.locale - && this.globalTsdk === other.globalTsdk - && this.localTsdk === other.localTsdk - && this.npmLocation === other.npmLocation - && this.tsServerLogLevel === other.tsServerLogLevel - && this.checkJs === other.checkJs - && this.experimentalDecorators === other.experimentalDecorators - && this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition - && arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths) - && this.separateSyntaxServer === other.separateSyntaxServer - && this.enableProjectDiagnostics === other.enableProjectDiagnostics - && this.maxTsServerMemory === other.maxTsServerMemory - && objects.equals(this.watchOptions, other.watchOptions) - && this.enablePromptUseWorkspaceTsdk === other.enablePromptUseWorkspaceTsdk - && this.includePackageJsonAutoImports === other.includePackageJsonAutoImports; + return objects.equals(this, other); } private static fixPathPrefixes(inspectValue: string): string { @@ -145,14 +167,6 @@ export class TypeScriptServiceConfiguration { return configuration.get('typescript.tsserver.pluginPaths', []); } - private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('javascript.implicitProjectConfig.checkJs', false); - } - - private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('javascript.implicitProjectConfig.experimentalDecorators', false); - } - private static readNpmLocation(configuration: vscode.WorkspaceConfiguration): string | null { return configuration.get('typescript.npm', null); } @@ -198,4 +212,8 @@ export class TypeScriptServiceConfiguration { private static readEnablePromptUseWorkspaceTsdk(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('typescript.enablePromptUseWorkspaceTsdk', false); } + + private static readEnableTsServerTracing(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('typescript.tsserver.enableTracing', false); + } } diff --git a/extensions/typescript-language-features/src/utils/resourceMap.ts b/extensions/typescript-language-features/src/utils/resourceMap.ts index ea4889de..1b7285aa 100644 --- a/extensions/typescript-language-features/src/utils/resourceMap.ts +++ b/extensions/typescript-language-features/src/utils/resourceMap.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as fileSchemes from '../utils/fileSchemes'; /** * Maps of file resources @@ -12,10 +13,18 @@ import * as vscode from 'vscode'; * file systems. */ export class ResourceMap { - private readonly _map = new Map(); + + private static readonly defaultPathNormalizer = (resource: vscode.Uri): string => { + if (resource.scheme === fileSchemes.file) { + return resource.fsPath; + } + return resource.toString(true); + }; + + private readonly _map = new Map(); constructor( - private readonly _normalizePath: (resource: vscode.Uri) => string | undefined = (resource) => resource.fsPath, + protected readonly _normalizePath: (resource: vscode.Uri) => string | undefined = ResourceMap.defaultPathNormalizer, protected readonly config: { readonly onCaseInsenitiveFileSystem: boolean, }, diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index f6802ca3..9d1c309e 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -32,17 +32,25 @@ export function inferredProjectCompilerOptions( jsx: 'preserve' as Proto.JsxEmit, }; - if (serviceConfig.checkJs) { + if (serviceConfig.implictProjectConfiguration.checkJs) { projectConfig.checkJs = true; if (projectType === ProjectType.TypeScript) { projectConfig.allowJs = true; } } - if (serviceConfig.experimentalDecorators) { + if (serviceConfig.implictProjectConfiguration.experimentalDecorators) { projectConfig.experimentalDecorators = true; } + if (serviceConfig.implictProjectConfiguration.strictNullChecks) { + projectConfig.strictNullChecks = true; + } + + if (serviceConfig.implictProjectConfiguration.strictFunctionTypes) { + projectConfig.strictFunctionTypes = true; + } + if (projectType === ProjectType.TypeScript) { projectConfig.sourceMap = true; } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index ad1d7044..82681ca0 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -1014,4 +1014,38 @@ suite('vscode API - workspace', () => { } }); + + test('issue #110141 - TextEdit.setEndOfLine applies an edit and invalidates redo stack even when no change is made', async () => { + const file = await createRandomFile('hello\nworld'); + + const document = await vscode.workspace.openTextDocument(file); + await vscode.window.showTextDocument(document); + + // apply edit + { + const we = new vscode.WorkspaceEdit(); + we.insert(file, new vscode.Position(0, 5), '2'); + await vscode.workspace.applyEdit(we); + } + + // check the document + { + assert.equal(document.getText(), 'hello2\nworld'); + assert.equal(document.isDirty, true); + } + + // apply no-op edit + { + const we = new vscode.WorkspaceEdit(); + we.set(file, [vscode.TextEdit.setEndOfLine(vscode.EndOfLine.LF)]); + await vscode.workspace.applyEdit(we); + } + + // undo + { + await vscode.commands.executeCommand('undo'); + assert.equal(document.getText(), 'hello\nworld'); + assert.equal(document.isDirty, false); + } + }); }); diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 20586e5c..9e9d4faf 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" - integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== +typescript@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9" + integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ== diff --git a/package.json b/package.json index 28f8a69a..c856e311 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.51.1", - "distro": "d32b7eb5181a656f005556e20c54bc5fd73fd080", + "version": "1.52.1", + "distro": "11fd6f1230738827638f000f641d01b2b178db20", "author": { "name": "Microsoft Corporation" }, @@ -37,14 +37,15 @@ "smoketest": "cd test/smoke && node test/index.js", "download-builtin-extensions": "node build/lib/builtInExtensions.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", - "tsec-compile-check": "node_modules/tsec/bin/tsec -p src/tsconfig.json --noEmit", + "tsec-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.json --noEmit", "valid-layers-check": "node build/lib/layersChecker.js", "strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes", "update-distro": "node build/npm/update-distro.js", "web": "node resources/web/code-web.js", "compile-web": "gulp compile-web --max_old_space_size=4095", "watch-web": "gulp watch-web --max_old_space_size=4095", - "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions" + "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions", + "electron-rebuild": "electron-rebuild --arch=arm64 --force --version=11.0.2" }, "dependencies": { "applicationinsights": "1.0.8", @@ -54,10 +55,10 @@ "https-proxy-agent": "^2.2.3", "iconv-lite-umd": "0.6.8", "jschardet": "2.2.1", - "keytar": "^5.5.0", + "keytar": "7.2.0", "minimist": "^1.2.5", "native-is-elevated": "0.4.1", - "native-keymap": "2.2.0", + "native-keymap": "2.2.1", "native-watchdog": "1.3.0", "node-pty": "0.10.0-beta17", "spdlog": "^0.11.1", @@ -67,7 +68,8 @@ "vscode-nsfw": "1.2.9", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.9.0", + "vscode-regexpp": "^3.1.0", + "vscode-ripgrep": "^1.11.1", "vscode-sqlite3": "4.0.10", "vscode-textmate": "5.2.0", "xterm": "4.10.0-beta.4", @@ -110,7 +112,8 @@ "css-loader": "^3.2.0", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "9.3.3", + "electron": "9.3.5", + "electron-rebuild": "2.0.3", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "eslint-plugin-mocha": "8.0.0", @@ -157,7 +160,7 @@ "opn": "^6.0.0", "optimist": "0.3.5", "p-all": "^1.0.0", - "playwright": "1.3.0", + "playwright": "1.6.2", "pump": "^1.0.1", "queue": "3.0.6", "rcedit": "^1.1.0", @@ -167,13 +170,13 @@ "style-loader": "^1.0.0", "ts-loader": "^4.4.2", "tsec": "googleinterns/tsec", - "typescript": "^4.1.0-dev.20201018", + "typescript": "^4.2.0-dev.20201119", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-debugprotocol": "1.41.0", + "vscode-debugprotocol": "1.43.0", "vscode-nls-dev": "^3.3.1", "webpack": "^4.43.0", "webpack-cli": "^3.3.12", diff --git a/product.json b/product.json index 7cab6d1b..207bcf8f 100644 --- a/product.json +++ b/product.json @@ -25,13 +25,13 @@ "extensionAllowedProposedApi": [ "ms-vscode.vscode-js-profile-flame", "ms-vscode.vscode-js-profile-table", - "ms-vscode.references-view", - "ms-vscode.github-browser" + "ms-vscode.github-browser", + "ms-vscode.github-richnav" ], "builtInExtensions": [ { "name": "ms-vscode.node-debug", - "version": "1.44.14", + "version": "1.44.15", "repo": "https://github.com/microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", @@ -61,7 +61,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.71", + "version": "0.0.74", "repo": "https://github.com/microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", @@ -91,7 +91,7 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.51.0", + "version": "1.52.2", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", @@ -123,7 +123,7 @@ "webBuiltInExtensions": [ { "name": "ms-vscode.github-browser", - "version": "0.0.13", + "version": "0.0.14", "repo": "https://github.com/microsoft/vscode-github-browser", "metadata": { "id": "c1bcff4b-4ecb-466e-b8f6-b02788b5fb5a", diff --git a/remote/package.json b/remote/package.json index d353586d..f68deac6 100644 --- a/remote/package.json +++ b/remote/package.json @@ -18,8 +18,9 @@ "vscode-nsfw": "1.2.9", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.9.0", + "vscode-ripgrep": "^1.11.1", "vscode-textmate": "5.2.0", + "vscode-regexpp": "^3.1.0", "xterm": "4.10.0-beta.4", "xterm-addon-search": "0.8.0-beta.3", "xterm-addon-unicode11": "0.3.0-beta.3", diff --git a/remote/yarn.lock b/remote/yarn.lock index 5ba96e1c..22d8ebe3 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -167,11 +167,16 @@ glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -graceful-fs@4.2.3, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -279,7 +284,12 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -nan@^2.10.0, nan@^2.14.0: +nan@^2.10.0: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -410,10 +420,15 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db" - integrity sha512-7jyAC/NNfvMPZgCVkyqIn0STYJ7wIk3PF2qA2cX1sEutx1g/e2VtgKAodXnfpreJq4993JT/BSIigOv/0lBSzg== +vscode-regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/vscode-regexpp/-/vscode-regexpp-3.1.0.tgz#42d059b6fffe99bd42939c0d013f632f0cad823f" + integrity sha512-pqtN65VC1jRLawfluX4Y80MMG0DHJydWhe5ZwMHewZD6sys4LbU6lHwFAHxeuaVE6Y6+xZOtAw+9hvq7/0ejkg== + +vscode-ripgrep@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.11.1.tgz#9fa3c0a96c2939d5a2389f71218bd1bb6eaa8679" + integrity sha512-oHJfpqeXuTQhOO+szqIObYOddwQ9o+lzd4PQLlTQN+sQ7ex8D1qqFip207O2iJyFc5oWE8Bekf4YHTibdbW66w== dependencies: https-proxy-agent "^4.0.0" proxy-from-env "^1.1.0" diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index 9f26b350..c72fe5f9 100755 --- a/resources/linux/debian/postinst.template +++ b/resources/linux/debian/postinst.template @@ -73,6 +73,6 @@ NdCFTW7wY0Fb1fWJ+/KTsC4= if [ "$WRITE_SOURCE" -eq "1" ]; then echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### # You may comment out this entry, but any other modifications may be lost. -deb [arch=amd64] http://packages.microsoft.com/repos/vscode stable main" > $CODE_SOURCE_PART +deb [arch=amd64,arm64,armhf] http://packages.microsoft.com/repos/@@REPOSITORY_NAME@@ stable main" > $CODE_SOURCE_PART fi fi diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index 7f95cd3e..8a055565 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -66,133 +66,120 @@ "libgbm.so.1()(64bit)" ], "aarch64": [ - "libpthread.so.0()(aarch64)", - "libpthread.so.0(GLIBC_2.2.5)(aarch64)", - "libpthread.so.0(GLIBC_2.3.2)(aarch64)", - "libpthread.so.0(GLIBC_2.3.3)(aarch64)", - "libgtk-3.so.0()(aarch64)", - "libgdk-x11-2.0.so.0()(aarch64)", - "libatk-1.0.so.0()(aarch64)", - "libgio-2.0.so.0()(aarch64)", - "libpangocairo-1.0.so.0()(aarch64)", - "libgdk_pixbuf-2.0.so.0()(aarch64)", - "libcairo.so.2()(aarch64)", - "libpango-1.0.so.0()(aarch64)", - "libfreetype.so.6()(aarch64)", - "libfontconfig.so.1()(aarch64)", - "libgobject-2.0.so.0()(aarch64)", - "libdbus-1.so.3()(aarch64)", - "libXi.so.6()(aarch64)", - "libXcursor.so.1()(aarch64)", - "libXdamage.so.1()(aarch64)", - "libXrandr.so.2()(aarch64)", - "libXcomposite.so.1()(aarch64)", - "libXext.so.6()(aarch64)", - "libXfixes.so.3()(aarch64)", - "libXrender.so.1()(aarch64)", - "libX11.so.6()(aarch64)", - "libXss.so.1()(aarch64)", - "libXtst.so.6()(aarch64)", - "libgmodule-2.0.so.0()(aarch64)", - "librt.so.1()(aarch64)", - "libglib-2.0.so.0()(aarch64)", - "libnss3.so()(aarch64)", - "libnssutil3.so()(aarch64)", - "libsmime3.so()(aarch64)", - "libnspr4.so()(aarch64)", - "libasound.so.2()(aarch64)", - "libcups.so.2()(aarch64)", - "libdl.so.2()(aarch64)", - "libexpat.so.1()(aarch64)", - "libstdc++.so.6()(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.10)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.11)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.14)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.15)(aarch64)", - "libstdc++.so.6(GLIBCXX_3.4.9)(aarch64)", - "libm.so.6()(aarch64)", - "libm.so.6(GLIBC_2.2.5)(aarch64)", - "libgcc_s.so.1()(aarch64)", - "libgcc_s.so.1(GCC_3.0)(aarch64)", - "libgcc_s.so.1(GCC_4.0.0)(aarch64)", - "libc.so.6()(aarch64)", - "libc.so.6(GLIBC_2.11)(aarch64)", - "libc.so.6(GLIBC_2.2.5)(aarch64)", - "libc.so.6(GLIBC_2.3)(aarch64)", - "libc.so.6(GLIBC_2.3.2)(aarch64)", - "libc.so.6(GLIBC_2.3.4)(aarch64)", - "libc.so.6(GLIBC_2.4)(aarch64)", - "libc.so.6(GLIBC_2.6)(aarch64)", - "libc.so.6(GLIBC_2.7)(aarch64)", - "libc.so.6(GLIBC_2.9)(aarch64)", - "libxcb.so.1()(aarch64)", - "libxkbfile.so.1()(aarch64)", - "libsecret-1.so.0()(aarch64)" + "libpthread.so.0()(64bit)", + "libpthread.so.0(GLIBC_2.17)(64bit)", + "libgtk-3.so.0()(64bit)", + "libgdk-x11-2.0.so.0()(64bit)", + "libatk-1.0.so.0()(64bit)", + "libgio-2.0.so.0()(64bit)", + "libpangocairo-1.0.so.0()(64bit)", + "libgdk_pixbuf-2.0.so.0()(64bit)", + "libcairo.so.2()(64bit)", + "libpango-1.0.so.0()(64bit)", + "libfreetype.so.6()(64bit)", + "libfontconfig.so.1()(64bit)", + "libgobject-2.0.so.0()(64bit)", + "libdbus-1.so.3()(64bit)", + "libXi.so.6()(64bit)", + "libXcursor.so.1()(64bit)", + "libXdamage.so.1()(64bit)", + "libXrandr.so.2()(64bit)", + "libXcomposite.so.1()(64bit)", + "libXext.so.6()(64bit)", + "libXfixes.so.3()(64bit)", + "libXrender.so.1()(64bit)", + "libX11.so.6()(64bit)", + "libXss.so.1()(64bit)", + "libXtst.so.6()(64bit)", + "libgmodule-2.0.so.0()(64bit)", + "librt.so.1()(64bit)", + "libglib-2.0.so.0()(64bit)", + "libnss3.so()(64bit)", + "libnssutil3.so()(64bit)", + "libsmime3.so()(64bit)", + "libnspr4.so()(64bit)", + "libasound.so.2()(64bit)", + "libcups.so.2()(64bit)", + "libdl.so.2()(64bit)", + "libexpat.so.1()(64bit)", + "libstdc++.so.6()(64bit)", + "libstdc++.so.6(GLIBCXX_3.4)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.10)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.11)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.14)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.15)(64bit)", + "libstdc++.so.6(GLIBCXX_3.4.9)(64bit)", + "libm.so.6()(64bit)", + "libm.so.6(GLIBC_2.17)(64bit)", + "libgcc_s.so.1()(64bit)", + "libgcc_s.so.1(GCC_3.0)(64bit)", + "libgcc_s.so.1(GCC_4.0.0)(64bit)", + "libc.so.6()(64bit)", + "libc.so.6(GLIBC_2.17)(64bit)", + "libxcb.so.1()(64bit)", + "libxkbfile.so.1()(64bit)", + "libsecret-1.so.0()(64bit)" ], "armv7hl": [ - "libpthread.so.0()(armv7hl)", - "libpthread.so.0(GLIBC_2.2.5)(armv7hl)", - "libpthread.so.0(GLIBC_2.3.2)(armv7hl)", - "libpthread.so.0(GLIBC_2.3.3)(armv7hl)", - "libgtk-3.so.0()(armv7hl)", - "libgdk-x11-2.0.so.0()(armv7hl)", - "libatk-1.0.so.0()(armv7hl)", - "libgio-2.0.so.0()(armv7hl)", - "libpangocairo-1.0.so.0()(armv7hl)", - "libgdk_pixbuf-2.0.so.0()(armv7hl)", - "libcairo.so.2()(armv7hl)", - "libpango-1.0.so.0()(armv7hl)", - "libfreetype.so.6()(armv7hl)", - "libfontconfig.so.1()(armv7hl)", - "libgobject-2.0.so.0()(armv7hl)", - "libdbus-1.so.3()(armv7hl)", - "libXi.so.6()(armv7hl)", - "libXcursor.so.1()(armv7hl)", - "libXdamage.so.1()(armv7hl)", - "libXrandr.so.2()(armv7hl)", - "libXcomposite.so.1()(armv7hl)", - "libXext.so.6()(armv7hl)", - "libXfixes.so.3()(armv7hl)", - "libXrender.so.1()(armv7hl)", - "libX11.so.6()(armv7hl)", - "libXss.so.1()(armv7hl)", - "libXtst.so.6()(armv7hl)", - "libgmodule-2.0.so.0()(armv7hl)", - "librt.so.1()(armv7hl)", - "libglib-2.0.so.0()(armv7hl)", - "libnss3.so()(armv7hl)", - "libnssutil3.so()(armv7hl)", - "libsmime3.so()(armv7hl)", - "libnspr4.so()(armv7hl)", - "libasound.so.2()(armv7hl)", - "libcups.so.2()(armv7hl)", - "libdl.so.2()(armv7hl)", - "libexpat.so.1()(armv7hl)", - "libstdc++.so.6()(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.10)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.11)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.14)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.15)(armv7hl)", - "libstdc++.so.6(GLIBCXX_3.4.9)(armv7hl)", - "libm.so.6()(armv7hl)", - "libm.so.6(GLIBC_2.2.5)(armv7hl)", - "libgcc_s.so.1()(armv7hl)", - "libgcc_s.so.1(GCC_3.0)(armv7hl)", - "libgcc_s.so.1(GCC_4.0.0)(armv7hl)", - "libc.so.6()(armv7hl)", - "libc.so.6(GLIBC_2.11)(armv7hl)", - "libc.so.6(GLIBC_2.2.5)(armv7hl)", - "libc.so.6(GLIBC_2.3)(armv7hl)", - "libc.so.6(GLIBC_2.3.2)(armv7hl)", - "libc.so.6(GLIBC_2.3.4)(armv7hl)", - "libc.so.6(GLIBC_2.4)(armv7hl)", - "libc.so.6(GLIBC_2.6)(armv7hl)", - "libc.so.6(GLIBC_2.7)(armv7hl)", - "libc.so.6(GLIBC_2.9)(armv7hl)", - "libxcb.so.1()(armv7hl)", - "libxkbfile.so.1()(armv7hl)", - "libsecret-1.so.0()(armv7hl)" + "libpthread.so.0()", + "libpthread.so.0(GLIBC_2.4)", + "libpthread.so.0(GLIBC_2.11)", + "libpthread.so.0(GLIBC_2.12)", + "libgtk-3.so.0()", + "libgdk-x11-2.0.so.0()", + "libatk-1.0.so.0()", + "libgio-2.0.so.0()", + "libpangocairo-1.0.so.0()", + "libgdk_pixbuf-2.0.so.0()", + "libcairo.so.2()", + "libpango-1.0.so.0()", + "libfreetype.so.6()", + "libfontconfig.so.1()", + "libgobject-2.0.so.0()", + "libdbus-1.so.3()", + "libXi.so.6()", + "libXcursor.so.1()", + "libXdamage.so.1()", + "libXrandr.so.2()", + "libXcomposite.so.1()", + "libXext.so.6()", + "libXfixes.so.3()", + "libXrender.so.1()", + "libX11.so.6()", + "libXss.so.1()", + "libXtst.so.6()", + "libgmodule-2.0.so.0()", + "librt.so.1()", + "libglib-2.0.so.0()", + "libnss3.so()", + "libnssutil3.so()", + "libsmime3.so()", + "libnspr4.so()", + "libasound.so.2()", + "libcups.so.2()", + "libdl.so.2()", + "libexpat.so.1()", + "libstdc++.so.6()", + "libstdc++.so.6(GLIBCXX_3.4)", + "libstdc++.so.6(GLIBCXX_3.4.10)", + "libstdc++.so.6(GLIBCXX_3.4.11)", + "libstdc++.so.6(GLIBCXX_3.4.14)", + "libstdc++.so.6(GLIBCXX_3.4.15)", + "libstdc++.so.6(GLIBCXX_3.4.9)", + "libm.so.6()", + "libm.so.6(GLIBC_2.4)", + "libm.so.6(GLIBC_2.15)", + "libgcc_s.so.1()", + "libgcc_s.so.1(GCC_3.0)", + "libgcc_s.so.1(GCC_4.0.0)", + "libc.so.6()", + "libc.so.6(GLIBC_2.11)", + "libc.so.6(GLIBC_2.4)", + "libc.so.6(GLIBC_2.6)", + "libc.so.6(GLIBC_2.7)", + "libc.so.6(GLIBC_2.9)", + "libxcb.so.1()", + "libxkbfile.so.1()", + "libsecret-1.so.0()" ] } diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index 046158e8..c24d0af3 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -6,6 +6,10 @@ description: | simplicity of a code editor with what developers need for the core edit-build-debug cycle. +architectures: + - build-on: amd64 + run-on: @@ARCHITECTURE@@ + grade: stable confinement: classic diff --git a/resources/web/code-web.js b/resources/web/code-web.js index 8658b737..9be3bc2d 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -386,6 +386,10 @@ async function handleRoot(req, res) { if (args['wrap-iframe']) { webConfigJSON._wrapWebWorkerExtHostInIframe = true; } + if (req.headers['x-forwarded-host']) { + // support for running in codespace => no iframe wrapping + delete webConfigJSON.webWorkerExtensionHostIframeSrc; + } const authSessionInfo = args['github-auth'] ? { id: uuid.v4(), diff --git a/scripts/code.bat b/scripts/code.bat index 7ef1fd33..a05bea92 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -22,7 +22,6 @@ set VSCODE_DEV=1 set VSCODE_CLI=1 set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 -set VSCODE_LOGS= :: Launch Code diff --git a/scripts/code.sh b/scripts/code.sh index 7ab367c1..3095f389 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -41,7 +41,6 @@ function code() { export VSCODE_CLI=1 export ELECTRON_ENABLE_STACK_DUMPING=1 export ELECTRON_ENABLE_LOGGING=1 - export VSCODE_LOGS= # Launch Code exec "$CODE" . --no-sandbox "$@" diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index b302ce28..b82dbec1 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -46,21 +46,46 @@ else echo "Running integration tests with '$INTEGRATION_TEST_ELECTRON_PATH' as build." fi +if [ -z "$INTEGRATION_TEST_APP_NAME" ]; then + after_suite() { true; } +else + after_suite() { killall $INTEGRATION_TEST_APP_NAME || true; } +fi + # Integration tests in AMD ./scripts/test.sh --runGlob **/*.integrationTest.js "$@" +after_suite # Tests in the extension host "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + #"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +# after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite + "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite # Tests in commonJS (CSS, HTML) cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js +after_suite + cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js +after_suite rm -rf $VSCODEUSERDATADIR diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 752e4ab6..58355aeb 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -18,7 +18,9 @@ loader.config({ catchError: true, nodeRequire: require, nodeMain: __filename, - 'vs/nls': nlsConfig + 'vs/nls': nlsConfig, + amdModulesPattern: /^vs\//, + recordStats: true }); // Running in Electron @@ -29,7 +31,7 @@ if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { } // Pseudo NLS support -if (nlsConfig.pseudo) { +if (nlsConfig && nlsConfig.pseudo) { loader(['vs/nls'], function (nlsPlugin) { nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); }); diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 97a4d993..68e2219b 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -13,7 +13,7 @@ const bootstrapNode = require('./bootstrap-node'); bootstrapNode.removeGlobalNodeModuleLookupPaths(); // Enable ASAR in our forked processes -bootstrap.enableASARSupport(); +bootstrap.enableASARSupport(undefined); if (process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']) { bootstrapNode.injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']); @@ -81,7 +81,9 @@ function pipeLoggingToParent() { // to start the stacktrace where the console message was being written if (process.env.VSCODE_LOG_STACK === 'true') { const stack = new Error().stack; - argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); + if (stack) { + argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); + } } try { @@ -114,7 +116,9 @@ function pipeLoggingToParent() { */ function safeSend(arg) { try { - process.send(arg); + if (process.send) { + process.send(arg); + } } catch (error) { // Can happen if the parent channel is closed meanwhile } diff --git a/src/bootstrap-node.js b/src/bootstrap-node.js index 26862499..161d68d5 100644 --- a/src/bootstrap-node.js +++ b/src/bootstrap-node.js @@ -58,3 +58,73 @@ exports.removeGlobalNodeModuleLookupPaths = function () { return paths.slice(0, paths.length - commonSuffixLength); }; }; + +/** + * Helper to enable portable mode. + * + * @param {{ portable?: string; applicationName: string; }} product + * @returns {{ portableDataPath: string; isPortable: boolean; }} + */ +exports.configurePortable = function (product) { + const fs = require('fs'); + const path = require('path'); + + const appRoot = path.dirname(__dirname); + + /** + * @param {import('path')} path + */ + function getApplicationPath(path) { + if (process.env['VSCODE_DEV']) { + return appRoot; + } + + if (process.platform === 'darwin') { + return path.dirname(path.dirname(path.dirname(appRoot))); + } + + return path.dirname(path.dirname(appRoot)); + } + + /** + * @param {import('path')} path + */ + function getPortableDataPath(path) { + if (process.env['VSCODE_PORTABLE']) { + return process.env['VSCODE_PORTABLE']; + } + + if (process.platform === 'win32' || process.platform === 'linux') { + return path.join(getApplicationPath(path), 'data'); + } + + // @ts-ignore + const portableDataName = product.portable || `${product.applicationName}-portable-data`; + return path.join(path.dirname(getApplicationPath(path)), portableDataName); + } + + const portableDataPath = getPortableDataPath(path); + const isPortable = !('target' in product) && fs.existsSync(portableDataPath); + const portableTempPath = path.join(portableDataPath, 'tmp'); + const isTempPortable = isPortable && fs.existsSync(portableTempPath); + + if (isPortable) { + process.env['VSCODE_PORTABLE'] = portableDataPath; + } else { + delete process.env['VSCODE_PORTABLE']; + } + + if (isTempPortable) { + if (process.platform === 'win32') { + process.env['TMP'] = portableTempPath; + process.env['TEMP'] = portableTempPath; + } else { + process.env['TMPDIR'] = portableTempPath; + } + } + + return { + portableDataPath, + isPortable + }; +}; diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 3fc0f419..d7cc72d3 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -25,7 +25,12 @@ const preloadGlobals = globals(); const sandbox = preloadGlobals.context.sandbox; const webFrame = preloadGlobals.webFrame; - const safeProcess = sandbox ? preloadGlobals.process : process; + const safeProcess = preloadGlobals.process; + const configuration = parseWindowConfiguration(); + + // Start to resolve process.env before anything gets load + // so that we can run loading and resolving in parallel + const whenEnvResolved = safeProcess.resolveEnv(configuration.userEnv); /** * @param {string[]} modulePaths @@ -33,18 +38,6 @@ * @param {{ forceEnableDeveloperKeybindings?: boolean, disallowReloadKeybinding?: boolean, removeDeveloperKeybindingsAfterLoad?: boolean, canModifyDOM?: (config: object) => void, beforeLoaderConfig?: (config: object, loaderConfig: object) => void, beforeRequire?: () => void }=} options */ function load(modulePaths, resultCallback, options) { - const args = parseURLQueryArgs(); - /** - * // configuration: INativeWindowConfiguration - * @type {{ - * zoomLevel?: number, - * extensionDevelopmentPath?: string[], - * extensionTestsPath?: string, - * userEnv?: { [key: string]: string | undefined }, - * appRoot?: string, - * nodeCachedDataDir?: string - * }} */ - const configuration = JSON.parse(args['config'] || '{}') || {}; // Apply zoom level early to avoid glitches const zoomLevel = configuration.zoomLevel; @@ -64,22 +57,15 @@ developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); } - // Correctly inherit the parent's environment (TODO@sandbox non-sandboxed only) - if (!sandbox) { - Object.assign(safeProcess.env, configuration.userEnv); - } - - // Enable ASAR support (TODO@sandbox non-sandboxed only) - if (!sandbox) { - globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); - } + // Enable ASAR support + globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); if (options && typeof options.canModifyDOM === 'function') { options.canModifyDOM(configuration); } - // Get the nls configuration into the process.env as early as possible (TODO@sandbox non-sandboxed only) - const nlsConfig = sandbox ? { availableLanguages: {} } : globalThis.MonacoBootstrap.setupNLS(); + // Get the nls configuration into the process.env as early as possible + const nlsConfig = globalThis.MonacoBootstrap.setupNLS(); let locale = nlsConfig.availableLanguages['*'] || 'en'; if (locale === 'zh-tw') { @@ -102,9 +88,14 @@ window['MonacoEnvironment'] = {}; + const baseUrl = sandbox ? + `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out` : + `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`; + const loaderConfig = { - baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`, - 'vs/nls': nlsConfig + baseUrl, + 'vs/nls': nlsConfig, + preferScriptTags: sandbox }; // Enable loading of node modules: @@ -150,17 +141,23 @@ options.beforeRequire(); } - require(modulePaths, result => { + require(modulePaths, async result => { try { + + // Wait for process environment being fully resolved + const perf = perfLib(); + perf.mark('willWaitForShellEnv'); + await whenEnvResolved; + perf.mark('didWaitForShellEnv'); + + // Callback only after process environment is resolved const callbackResult = resultCallback(result, configuration); - if (callbackResult && typeof callbackResult.then === 'function') { - callbackResult.then(() => { - if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { - developerToolsUnbind(); - } - }, error => { - onUnexpectedError(error, enableDeveloperTools); - }); + if (callbackResult instanceof Promise) { + await callbackResult; + + if (developerToolsUnbind && options && options.removeDeveloperKeybindingsAfterLoad) { + developerToolsUnbind(); + } } } catch (error) { onUnexpectedError(error, enableDeveloperTools); @@ -169,20 +166,30 @@ } /** - * @returns {{[param: string]: string }} + * Parses the contents of the `INativeWindowConfiguration` that + * is passed into the URL from the `electron-main` side. + * + * @returns {{ + * zoomLevel?: number, + * extensionDevelopmentPath?: string[], + * extensionTestsPath?: string, + * userEnv?: { [key: string]: string | undefined }, + * appRoot: string, + * nodeCachedDataDir?: string + * }} */ - function parseURLQueryArgs() { - const search = window.location.search || ''; - - return search.split(/[?&]/) + function parseWindowConfiguration() { + const rawConfiguration = (window.location.search || '').split(/[?&]/) .filter(function (param) { return !!param; }) .map(function (param) { return param.split('='); }) .filter(function (param) { return param.length === 2; }) .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); + + return JSON.parse(rawConfiguration['config'] || '{}') || {}; } /** - * @param {boolean} disallowReloadKeybinding + * @param {boolean | undefined} disallowReloadKeybinding * @returns {() => void} */ function registerDeveloperKeybindings(disallowReloadKeybinding) { @@ -203,6 +210,7 @@ const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R + /** @type {((e: any) => void) | undefined} */ let listener = function (e) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { @@ -255,8 +263,26 @@ return window.vscode; } + /** + * @return {{ mark: (name: string) => void }} + */ + function perfLib() { + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; + + return { + /** + * @param {string} name + */ + mark(name) { + globalThis.MonacoPerformanceMarks.push(name, Date.now()); + performance.mark(name); + } + }; + } + return { load, - globals + globals, + perfLib }; })); diff --git a/src/bootstrap.js b/src/bootstrap.js index 0cb6466a..5392a560 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -42,11 +42,11 @@ //#region Add support for using node_modules.asar /** - * @param {string} appRoot + * @param {string | undefined} appRoot */ function enableASARSupport(appRoot) { - if (!path || !Module) { - console.warn('enableASARSupport() is only available in node.js environments'); + if (!path || !Module || typeof process === 'undefined') { + console.warn('enableASARSupport() is only available in node.js environments'); // TODO@sandbox ASAR is currently non-sandboxed only return; } @@ -124,17 +124,14 @@ //#region NLS helpers /** - * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} + * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean } | undefined} */ function setupNLS() { - if (!path || !fs) { - console.warn('setupNLS() is only available in node.js environments'); - return; - } - // Get the nls configuration into the process.env as early as possible. + // Get the nls configuration as early as possible. + const process = safeProcess(); let nlsConfig = { availableLanguages: {} }; - if (process.env['VSCODE_NLS_CONFIG']) { + if (process && process.env['VSCODE_NLS_CONFIG']) { try { nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); } catch (e) { @@ -153,8 +150,7 @@ return; } - const bundleFile = path.join(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`); - fs.promises.readFile(bundleFile, 'utf8').then(function (content) { + safeReadNlsFile(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`).then(function (content) { const json = JSON.parse(content); bundles[bundle] = json; @@ -162,7 +158,7 @@ }).catch((error) => { try { if (nlsConfig._corruptedFile) { - fs.promises.writeFile(nlsConfig._corruptedFile, 'corrupted', 'utf8').catch(function (error) { console.error(error); }); + safeWriteNlsFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); }); } } finally { cb(error, undefined); @@ -174,77 +170,73 @@ return nlsConfig; } - //#endregion + function safeGlobals() { + const globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {}); - - //#region Portable helpers + return globals.vscode; + } /** - * @param {{ portable: string; applicationName: string; }} product - * @returns {{ portableDataPath: string; isPortable: boolean; }} + * @returns {NodeJS.Process | undefined} */ - function configurePortable(product) { - if (!path || !fs) { - console.warn('configurePortable() is only available in node.js environments'); - return; + function safeProcess() { + if (typeof process !== 'undefined') { + return process; // Native environment (non-sandboxed) } - const appRoot = path.dirname(__dirname); + const globals = safeGlobals(); + if (globals) { + return globals.process; // Native environment (sandboxed) + } + } - function getApplicationPath() { - if (process.env['VSCODE_DEV']) { - return appRoot; - } + /** + * @returns {Electron.IpcRenderer | undefined} + */ + function safeIpcRenderer() { + const globals = safeGlobals(); + if (globals) { + return globals.ipcRenderer; + } + } - if (process.platform === 'darwin') { - return path.dirname(path.dirname(path.dirname(appRoot))); - } - - return path.dirname(path.dirname(appRoot)); + /** + * @param {string[]} pathSegments + * @returns {Promise} + */ + async function safeReadNlsFile(...pathSegments) { + const ipcRenderer = safeIpcRenderer(); + if (ipcRenderer) { + return ipcRenderer.invoke('vscode:readNlsFile', ...pathSegments); } - function getPortableDataPath() { - if (process.env['VSCODE_PORTABLE']) { - return process.env['VSCODE_PORTABLE']; - } - - if (process.platform === 'win32' || process.platform === 'linux') { - return path.join(getApplicationPath(), 'data'); - } - - // @ts-ignore - const portableDataName = product.portable || `${product.applicationName}-portable-data`; - return path.join(path.dirname(getApplicationPath()), portableDataName); + if (fs && path) { + return (await fs.promises.readFile(path.join(...pathSegments))).toString(); } - const portableDataPath = getPortableDataPath(); - const isPortable = !('target' in product) && fs.existsSync(portableDataPath); - const portableTempPath = path.join(portableDataPath, 'tmp'); - const isTempPortable = isPortable && fs.existsSync(portableTempPath); + throw new Error('Unsupported operation (read NLS files)'); + } - if (isPortable) { - process.env['VSCODE_PORTABLE'] = portableDataPath; - } else { - delete process.env['VSCODE_PORTABLE']; + /** + * @param {string} path + * @param {string} content + * @returns {Promise} + */ + function safeWriteNlsFile(path, content) { + const ipcRenderer = safeIpcRenderer(); + if (ipcRenderer) { + return ipcRenderer.invoke('vscode:writeNlsFile', path, content); } - if (isTempPortable) { - if (process.platform === 'win32') { - process.env['TMP'] = portableTempPath; - process.env['TEMP'] = portableTempPath; - } else { - process.env['TMPDIR'] = portableTempPath; - } + if (fs) { + return fs.promises.writeFile(path, content); } - return { - portableDataPath, - isPortable - }; + throw new Error('Unsupported operation (write NLS files)'); } //#endregion - + //#region ApplicationInsights @@ -267,7 +259,6 @@ return { enableASARSupport, avoidMonkeyPatchFromAppInsights, - configurePortable, setupNLS, fileUriFromPath }; diff --git a/src/buildfile.js b/src/buildfile.js index f6d1c647..c0400de7 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -10,7 +10,7 @@ function entrypoint(name) { exports.base = [{ name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], - prepend: ['vs/loader.js'], + prepend: ['vs/loader.js', 'vs/nls.js'], append: ['vs/base/worker/workerMain'], dest: 'vs/base/worker/workerMain.js' }]; diff --git a/src/cli.js b/src/cli.js index 872cd667..5624cb93 100644 --- a/src/cli.js +++ b/src/cli.js @@ -7,16 +7,17 @@ 'use strict'; const bootstrap = require('./bootstrap'); +const bootstrapNode = require('./bootstrap-node'); const product = require('../product.json'); // Avoid Monkey Patches from Application Insights bootstrap.avoidMonkeyPatchFromAppInsights(); // Enable portable support -bootstrap.configurePortable(product); +bootstrapNode.configurePortable(product); // Enable ASAR support -bootstrap.enableASARSupport(); +bootstrap.enableASARSupport(undefined); // Load CLI through AMD loader require('./bootstrap-amd').load('vs/code/node/cli'); diff --git a/src/main.js b/src/main.js index 2a9756ab..0bc56609 100644 --- a/src/main.js +++ b/src/main.js @@ -15,6 +15,7 @@ const path = require('path'); const fs = require('fs'); const os = require('os'); const bootstrap = require('./bootstrap'); +const bootstrapNode = require('./bootstrap-node'); const paths = require('./paths'); /** @type {any} */ const product = require('../product.json'); @@ -25,10 +26,10 @@ const { app, protocol, crashReporter } = require('electron'); app.allowRendererProcessReuse = false; // Enable portable support -const portable = bootstrap.configurePortable(product); +const portable = bootstrapNode.configurePortable(product); // Enable ASAR support -bootstrap.enableASARSupport(); +bootstrap.enableASARSupport(undefined); // Set userData path before app 'ready' event const args = parseCLIArgs(); @@ -107,7 +108,7 @@ crashReporter.start({ // to ensure that no 'logs' folder is created on disk at a // location outside of the portable directory // (https://github.com/microsoft/vscode/issues/56651) -if (portable.isPortable) { +if (portable && portable.isPortable) { app.setAppLogsPath(path.join(userDataPath, 'logs')); } @@ -133,6 +134,15 @@ protocol.registerSchemesAsPrivileged([ corsEnabled: true, } }, + { + scheme: 'vscode-file', + privileges: { + secure: true, + standard: true, + supportFetchAPI: true, + corsEnabled: true + } + } ]); // Global app listeners @@ -145,7 +155,7 @@ const nodeCachedDataDir = getNodeCachedDir(); * Support user defined locale: load it early before app('ready') * to have more things running in parallel. * - * @type {Promise} nlsConfig | undefined + * @type {Promise | undefined} */ let nlsConfigurationPromise = undefined; @@ -359,7 +369,7 @@ function getArgvConfigPath() { /** * @param {NativeParsedArgs} cliArgs - * @returns {string} + * @returns {string | null} */ function getJSFlags(cliArgs) { const jsFlags = []; @@ -387,7 +397,7 @@ function getUserDataPath(cliArgs) { return path.join(portable.portableDataPath, 'user-data'); } - return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath(process.platform)); + return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath()); } /** @@ -468,12 +478,14 @@ function getNodeCachedDir() { } async ensureExists() { - try { - await mkdirp(this.value); + if (typeof this.value === 'string') { + try { + await mkdirp(this.value); - return this.value; - } catch (error) { - // ignore + return this.value; + } catch (error) { + // ignore + } } } diff --git a/src/paths.js b/src/paths.js index a88927d2..2042123d 100644 --- a/src/paths.js +++ b/src/paths.js @@ -11,25 +11,38 @@ const path = require('path'); const os = require('os'); /** - * @param {string} platform * @returns {string} */ -function getAppDataPath(platform) { - switch (platform) { - case 'win32': return process.env['VSCODE_APPDATA'] || process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming'); - case 'darwin': return process.env['VSCODE_APPDATA'] || path.join(os.homedir(), 'Library', 'Application Support'); - case 'linux': return process.env['VSCODE_APPDATA'] || process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); - default: throw new Error('Platform not supported'); +function getDefaultUserDataPath() { + + // Support global VSCODE_APPDATA environment variable + let appDataPath = process.env['VSCODE_APPDATA']; + + // Otherwise check per platform + if (!appDataPath) { + switch (process.platform) { + case 'win32': + appDataPath = process.env['APPDATA']; + if (!appDataPath) { + const userProfile = process.env['USERPROFILE']; + if (typeof userProfile !== 'string') { + throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable'); + } + appDataPath = path.join(userProfile, 'AppData', 'Roaming'); + } + break; + case 'darwin': + appDataPath = path.join(os.homedir(), 'Library', 'Application Support'); + break; + case 'linux': + appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); + break; + default: + throw new Error('Platform not supported'); + } } + + return path.join(appDataPath, pkg.name); } -/** - * @param {string} platform - * @returns {string} - */ -function getDefaultUserDataPath(platform) { - return path.join(getAppDataPath(platform), pkg.name); -} - -exports.getAppDataPath = getAppDataPath; exports.getDefaultUserDataPath = getDefaultUserDataPath; diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 2119700f..fc2ee032 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -5,7 +5,7 @@ import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview'; export interface IContextMenuEvent { readonly shiftKey?: boolean; @@ -26,6 +26,7 @@ export interface IContextMenuDelegate { actionRunner?: IActionRunner; autoSelectFirstItem?: boolean; anchorAlignment?: AnchorAlignment; + anchorAxisAlignment?: AnchorAxisAlignment; domForShadowRoot?: HTMLElement; } diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 430ef809..b4740dad 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -661,6 +661,48 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b return false; } +const parentFlowToDataKey = 'parentFlowToElementId'; + +/** + * Set an explicit parent to use for nodes that are not part of the + * regular dom structure. + */ +export function setParentFlowTo(fromChildElement: HTMLElement, toParentElement: Element): void { + fromChildElement.dataset[parentFlowToDataKey] = toParentElement.id; +} + +function getParentFlowToElement(node: HTMLElement): HTMLElement | null { + const flowToParentId = node.dataset[parentFlowToDataKey]; + if (typeof flowToParentId === 'string') { + return document.getElementById(flowToParentId); + } + return null; +} + +/** + * Check if `testAncestor` is an ancessor of `testChild`, observing the explicit + * parents set by `setParentFlowTo`. + */ +export function isAncestorUsingFlowTo(testChild: Node, testAncestor: Node): boolean { + let node: Node | null = testChild; + while (node) { + if (node === testAncestor) { + return true; + } + + if (node instanceof HTMLElement) { + const flowToParentElement = getParentFlowToElement(node); + if (flowToParentElement) { + node = flowToParentElement; + continue; + } + } + node = node.parentNode; + } + + return false; +} + export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null { while (node && node.nodeType === node.ELEMENT_NODE) { if (node.classList.contains(clazz)) { @@ -1331,8 +1373,8 @@ export function safeInnerHtml(node: HTMLElement, value: string): void { const options = _extInsaneOptions({ allowedTags: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'], allowedAttributes: { - 'a': ['href'], - 'button': ['data-href'], + 'a': ['href', 'x-dispatch'], + 'button': ['data-href', 'x-dispatch'], 'input': ['type', 'placeholder', 'checked', 'required'], 'label': ['for'], 'select': ['required'], diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index 328ecaee..4911b59e 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import * as platform from 'vs/base/common/platform'; import { IframeUtils } from 'vs/base/browser/iframe'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; export interface IStandardMouseMoveEventData { leftButton: boolean; @@ -26,7 +24,7 @@ export interface IMouseMoveCallback { } export interface IOnStopCallback { - (): void; + (browserEvent?: MouseEvent | KeyboardEvent): void; } export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData | null, currentEvent: MouseEvent): IStandardMouseMoveEventData { @@ -52,7 +50,7 @@ export class GlobalMouseMoveMonitor implements I this._hooks.dispose(); } - public stopMonitoring(invokeStopCallback: boolean): void { + public stopMonitoring(invokeStopCallback: boolean, browserEvent?: MouseEvent | KeyboardEvent): void { if (!this.isMonitoring()) { // Not monitoring return; @@ -66,7 +64,7 @@ export class GlobalMouseMoveMonitor implements I this._onStopCallback = null; if (invokeStopCallback && onStopCallback) { - onStopCallback(); + onStopCallback(browserEvent); } } @@ -90,8 +88,8 @@ export class GlobalMouseMoveMonitor implements I this._onStopCallback = onStopCallback; const windowChain = IframeUtils.getSameOriginWindowChain(); - const mouseMove = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove'; - const mouseUp = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup'; + const mouseMove = 'mousemove'; + const mouseUp = 'mouseup'; const listenTo: (Document | ShadowRoot)[] = windowChain.map(element => element.window.document); const shadowRoot = dom.getShadowRoot(initialElement); diff --git a/src/vs/base/browser/hash.ts b/src/vs/base/browser/hash.ts new file mode 100644 index 00000000..bf48b614 --- /dev/null +++ b/src/vs/base/browser/hash.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { StringSHA1, toHexString } from 'vs/base/common/hash'; + +export async function sha1Hex(str: string): Promise { + + // Prefer to use browser's crypto module + if (globalThis?.crypto?.subtle) { + const hash = await globalThis.crypto.subtle.digest({ name: 'sha-1' }, VSBuffer.fromString(str).buffer); + + return toHexString(hash); + } + + // Otherwise fallback to `StringSHA1` + else { + const computer = new StringSHA1(); + computer.update(str); + + return computer.digest(); + } +} diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 2d601e97..a5cd4c5e 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -28,7 +28,7 @@ export interface MarkedOptions extends marked.MarkedOptions { export interface MarkdownRenderOptions extends FormattedTextRenderOptions { codeBlockRenderer?: (modeId: string, value: string) => Promise; - codeBlockRenderCallback?: () => void; + asyncRenderCallback?: () => void; baseUrl?: URI; } @@ -177,8 +177,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // ignore }); - if (options.codeBlockRenderCallback) { - promise.then(options.codeBlockRenderCallback); + if (options.asyncRenderCallback) { + promise.then(options.asyncRenderCallback); } return `
${escape(code)}
`; @@ -215,11 +215,15 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // Use our own sanitizer so that we can let through only spans. // Otherwise, we'd be letting all html be rendered. // If we want to allow markdown permitted tags, then we can delete sanitizer and sanitize. + // We always pass the output through insane after this so that we don't rely on + // marked for sanitization. markedOptions.sanitizer = (html: string): string => { const match = markdown.isTrusted ? html.match(/^()|(<\/\s*span>)$/) : undefined; return match ? html : ''; }; markedOptions.sanitize = true; + markedOptions.silent = true; + markedOptions.renderer = renderer; // values that are too long will freeze the UI @@ -240,6 +244,17 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // signal that async code blocks can be now be inserted signalInnerHTML!(); + // signal size changes for image tags + if (options.asyncRenderCallback) { + for (const img of element.getElementsByTagName('img')) { + const listener = DOM.addDisposableListener(img, 'load', () => { + listener.dispose(); + options.asyncRenderCallback!(); + }); + } + } + + return element; } @@ -301,3 +316,76 @@ function getInsaneOptions(options: { readonly isTrusted?: boolean }): InsaneOpti }; } +/** + * Strips all markdown from `markdown`. For example `# Header` would be output as `Header`. + */ +export function renderMarkdownAsPlaintext(markdown: IMarkdownString) { + const renderer = new marked.Renderer(); + + renderer.code = (code: string): string => { + return code; + }; + renderer.blockquote = (quote: string): string => { + return quote; + }; + renderer.html = (_html: string): string => { + return ''; + }; + renderer.heading = (text: string, _level: 1 | 2 | 3 | 4 | 5 | 6, _raw: string): string => { + return text + '\n'; + }; + renderer.hr = (): string => { + return ''; + }; + renderer.list = (body: string, _ordered: boolean): string => { + return body; + }; + renderer.listitem = (text: string): string => { + return text + '\n'; + }; + renderer.paragraph = (text: string): string => { + return text + '\n'; + }; + renderer.table = (header: string, body: string): string => { + return header + body + '\n'; + }; + renderer.tablerow = (content: string): string => { + return content; + }; + renderer.tablecell = (content: string, _flags: { + header: boolean; + align: 'center' | 'left' | 'right' | null; + }): string => { + return content + ' '; + }; + renderer.strong = (text: string): string => { + return text; + }; + renderer.em = (text: string): string => { + return text; + }; + renderer.codespan = (code: string): string => { + return code; + }; + renderer.br = (): string => { + return '\n'; + }; + renderer.del = (text: string): string => { + return text; + }; + renderer.image = (_href: string, _title: string, _text: string): string => { + return ''; + }; + renderer.text = (text: string): string => { + return text; + }; + renderer.link = (_href: string, _title: string, text: string): string => { + return text; + }; + // values that are too long will freeze the UI + let value = markdown.value ?? ''; + if (value.length > 100_000) { + value = `${value.substr(0, 100_000)}â€Ļ`; + } + return sanitizeRenderedMarkdown({ isTrusted: false }, marked.parse(value, { renderer })).toString(); +} diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 9d206dab..788a83d5 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -60,6 +60,9 @@ export class ActionBar extends Disposable implements IActionRunner { protected focusedItem?: number; private focusTracker: DOM.IFocusTracker; + // Trigger Key Tracking + private triggerKeyDown: boolean = false; + // Elements domNode: HTMLElement; protected actionsList: HTMLElement; @@ -74,8 +77,8 @@ export class ActionBar extends Disposable implements IActionRunner { private _onDidRun = this._register(new Emitter()); readonly onDidRun = this._onDidRun.event; - private _onDidBeforeRun = this._register(new Emitter()); - readonly onDidBeforeRun = this._onDidBeforeRun.event; + private _onBeforeRun = this._register(new Emitter()); + readonly onBeforeRun = this._onBeforeRun.event; constructor(container: HTMLElement, options: IActionBarOptions = {}) { super(); @@ -96,7 +99,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); - this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); + this._register(this._actionRunner.onBeforeRun(e => this._onBeforeRun.fire(e))); this._actionIds = []; this.viewItems = []; @@ -148,6 +151,8 @@ export class ActionBar extends Disposable implements IActionRunner { // Staying out of the else branch even if not triggered if (this._triggerKeys.keyDown) { this.doTrigger(event); + } else { + this.triggerKeyDown = true; } } else { eventHandled = false; @@ -164,7 +169,8 @@ export class ActionBar extends Disposable implements IActionRunner { // Run action on Enter/Space if (this.isTriggerKeyEvent(event)) { - if (!this._triggerKeys.keyDown) { + if (!this._triggerKeys.keyDown && this.triggerKeyDown) { + this.triggerKeyDown = false; this.doTrigger(event); } @@ -183,6 +189,7 @@ export class ActionBar extends Disposable implements IActionRunner { if (DOM.getActiveElement() === this.domNode || !DOM.isAncestor(DOM.getActiveElement(), this.domNode)) { this._onDidBlur.fire(); this.focusedItem = undefined; + this.triggerKeyDown = false; } })); diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 27981af2..37b6b03f 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -11,7 +11,7 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; import 'vs/css!./breadcrumbsWidget'; export abstract class BreadcrumbsItem { @@ -56,7 +56,7 @@ export interface IBreadcrumbsItemEvent { payload: any; } -const breadcrumbSeparatorIcon = registerIcon('breadcrumb-separator', Codicon.chevronRight); +const breadcrumbSeparatorIcon = registerCodicon('breadcrumb-separator', Codicon.chevronRight); export class BreadcrumbsWidget { diff --git a/src/vs/base/browser/ui/button/button.css b/src/vs/base/browser/ui/button/button.css index 6d682352..217c1656 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css @@ -10,7 +10,6 @@ padding: 4px; text-align: center; cursor: pointer; - outline-offset: 2px !important; justify-content: center; align-items: center; } @@ -24,7 +23,15 @@ cursor: default; } -.monaco-button > .codicon { +.monaco-text-button > .codicon { margin: 0 0.2em; color: inherit !important; } + +.monaco-button-dropdown { + display: flex; +} + +.monaco-button-dropdown > .monaco-dropdown-button { + margin-left: 1px; +} diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index a3ca3ac9..b3697f9b 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -9,10 +9,13 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; import { renderCodicons } from 'vs/base/browser/codicons'; import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; +import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { CSSIcon, Codicon } from 'vs/base/common/codicons'; export interface IButtonOptions extends IButtonStyles { readonly title?: boolean | string; @@ -36,7 +39,18 @@ const defaultOptions: IButtonStyles = { buttonForeground: Color.white }; -export class Button extends Disposable { +export interface IButton extends IDisposable { + readonly element: HTMLElement; + readonly onDidClick: BaseEvent; + label: string; + icon: CSSIcon; + enabled: boolean; + style(styles: IButtonStyles): void; + focus(): void; + hasFocus(): boolean; +} + +export class Button extends Disposable implements IButton { private _element: HTMLElement; private options: IButtonOptions; @@ -188,8 +202,8 @@ export class Button extends Disposable { } } - set icon(iconClassName: string) { - this._element.classList.add(iconClassName); + set icon(icon: CSSIcon) { + this._element.classList.add(...icon.classNames.split(' ')); } set enabled(value: boolean) { @@ -217,47 +231,124 @@ export class Button extends Disposable { } } -export class ButtonGroup extends Disposable { - private _buttons: Button[] = []; +export interface IButtonWithDropdownOptions extends IButtonOptions { + readonly contextMenuProvider: IContextMenuProvider; + readonly actions: IAction[]; + readonly actionRunner?: IActionRunner; +} - constructor(container: HTMLElement, count: number, options?: IButtonOptions) { +export class ButtonWithDropdown extends Disposable implements IButton { + + private readonly button: Button; + private readonly dropdownButton: Button; + + readonly element: HTMLElement; + readonly onDidClick: BaseEvent; + + constructor(container: HTMLElement, options: IButtonWithDropdownOptions) { super(); - this.create(container, count, options); + this.element = document.createElement('div'); + this.element.classList.add('monaco-button-dropdown'); + container.appendChild(this.element); + + this.button = this._register(new Button(this.element, options)); + this.onDidClick = this.button.onDidClick; + + this.dropdownButton = this._register(new Button(this.element, { ...options, title: false, supportCodicons: true })); + this.dropdownButton.element.classList.add('monaco-dropdown-button'); + this.dropdownButton.icon = Codicon.dropDownButton; + this._register(this.dropdownButton.onDidClick(() => { + options.contextMenuProvider.showContextMenu({ + getAnchor: () => this.dropdownButton.element, + getActions: () => options.actions, + actionRunner: options.actionRunner, + onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false') + }); + this.dropdownButton.element.setAttribute('aria-expanded', 'true'); + })); } - get buttons(): Button[] { + set label(value: string) { + this.button.label = value; + } + + set icon(icon: CSSIcon) { + this.button.icon = icon; + } + + set enabled(enabled: boolean) { + this.button.enabled = enabled; + this.dropdownButton.enabled = enabled; + } + + get enabled(): boolean { + return this.button.enabled; + } + + style(styles: IButtonStyles): void { + this.button.style(styles); + this.dropdownButton.style(styles); + } + + focus(): void { + this.button.focus(); + } + + hasFocus(): boolean { + return this.button.hasFocus() || this.dropdownButton.hasFocus(); + } +} + +export class ButtonBar extends Disposable { + + private _buttons: IButton[] = []; + + constructor(private readonly container: HTMLElement) { + super(); + } + + get buttons(): IButton[] { return this._buttons; } - private create(container: HTMLElement, count: number, options?: IButtonOptions): void { - for (let index = 0; index < count; index++) { - const button = this._register(new Button(container, options)); - this._buttons.push(button); - - // Implement keyboard access in buttons if there are multiple - if (count > 1) { - this._register(addDisposableListener(button.element, EventType.KEY_DOWN, e => { - const event = new StandardKeyboardEvent(e); - let eventHandled = true; - - // Next / Previous Button - let buttonIndexToFocus: number | undefined; - if (event.equals(KeyCode.LeftArrow)) { - buttonIndexToFocus = index > 0 ? index - 1 : this._buttons.length - 1; - } else if (event.equals(KeyCode.RightArrow)) { - buttonIndexToFocus = index === this._buttons.length - 1 ? 0 : index + 1; - } else { - eventHandled = false; - } - - if (eventHandled && typeof buttonIndexToFocus === 'number') { - this._buttons[buttonIndexToFocus].focus(); - EventHelper.stop(e, true); - } - - })); - } - } + addButton(options?: IButtonOptions): IButton { + const button = this._register(new Button(this.container, options)); + this.pushButton(button); + return button; } + + addButtonWithDropdown(options: IButtonWithDropdownOptions): IButton { + const button = this._register(new ButtonWithDropdown(this.container, options)); + this.pushButton(button); + return button; + } + + private pushButton(button: IButton): void { + this._buttons.push(button); + + const index = this._buttons.length - 1; + this._register(addDisposableListener(button.element, EventType.KEY_DOWN, e => { + const event = new StandardKeyboardEvent(e); + let eventHandled = true; + + // Next / Previous Button + let buttonIndexToFocus: number | undefined; + if (event.equals(KeyCode.LeftArrow)) { + buttonIndexToFocus = index > 0 ? index - 1 : this._buttons.length - 1; + } else if (event.equals(KeyCode.RightArrow)) { + buttonIndexToFocus = index === this._buttons.length - 1 ? 0 : index + 1; + } else { + eventHandled = false; + } + + if (eventHandled && typeof buttonIndexToFocus === 'number') { + this._buttons[buttonIndexToFocus].focus(); + EventHelper.stop(e, true); + } + + })); + + } + } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index c3b0e243..932a2860 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -11,12 +11,12 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Codicon } from 'vs/base/common/codicons'; +import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export interface ICheckboxOpts extends ICheckboxStyles { readonly actionClassName?: string; - readonly icon?: Codicon; + readonly icon?: CSSIcon; readonly title: string; readonly isChecked: boolean; } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 4c16209d..7fdf6ed7 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/codicons/codiconStyles.ts b/src/vs/base/browser/ui/codicons/codiconStyles.ts index 8229f2f1..44281196 100644 --- a/src/vs/base/browser/ui/codicons/codiconStyles.ts +++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts @@ -7,18 +7,7 @@ import 'vs/css!./codicon/codicon'; import 'vs/css!./codicon/codicon-modifications'; import 'vs/css!./codicon/codicon-animations'; -import { Codicon, iconRegistry } from 'vs/base/common/codicons'; - -export const CodiconStyles = new class { - onDidChange = iconRegistry.onDidRegister; - public getCSS(): string { - const rules = []; - for (let c of iconRegistry.all) { - rules.push(formatRule(c)); - } - return rules.join('\n'); - } -}; +import { Codicon } from 'vs/base/common/codicons'; export function formatRule(c: Codicon) { let def = c.definition; diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 97e78cef..ef695b6a 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -31,6 +31,10 @@ export const enum AnchorPosition { BELOW, ABOVE } +export const enum AnchorAxisAlignment { + VERTICAL, HORIZONTAL +} + export interface IDelegate { getAnchor(): HTMLElement | IAnchor; render(container: HTMLElement): IDisposable | null; @@ -38,6 +42,7 @@ export interface IDelegate { layout?(): void; anchorAlignment?: AnchorAlignment; // default: left anchorPosition?: AnchorPosition; // default: below + anchorAxisAlignment?: AnchorAxisAlignment; // default: vertical canRelayout?: boolean; // default: true onDOMEvent?(e: Event, activeElement: HTMLElement): void; onHide?(data?: any): void; @@ -66,9 +71,15 @@ export const enum LayoutAnchorPosition { After } +export enum LayoutAnchorMode { + AVOID, + ALIGN +} + export interface ILayoutAnchor { offset: number; size: number; + mode?: LayoutAnchorMode; // default: AVOID position: LayoutAnchorPosition; } @@ -78,25 +89,26 @@ export interface ILayoutAnchor { * @returns The view offset within the viewport. */ export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAnchor): number { - const anchorEnd = anchor.offset + anchor.size; + const layoutAfterAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset : anchor.offset + anchor.size; + const layoutBeforeAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset + anchor.size : anchor.offset; if (anchor.position === LayoutAnchorPosition.Before) { - if (viewSize <= viewportSize - anchorEnd) { - return anchorEnd; // happy case, lay it out after the anchor + if (viewSize <= viewportSize - layoutAfterAnchorBoundary) { + return layoutAfterAnchorBoundary; // happy case, lay it out after the anchor } - if (viewSize <= anchor.offset) { - return anchor.offset - viewSize; // ok case, lay it out before the anchor + if (viewSize <= layoutBeforeAnchorBoundary) { + return layoutBeforeAnchorBoundary - viewSize; // ok case, lay it out before the anchor } return Math.max(viewportSize - viewSize, 0); // sad case, lay it over the anchor } else { - if (viewSize <= anchor.offset) { - return anchor.offset - viewSize; // happy case, lay it out before the anchor + if (viewSize <= layoutBeforeAnchorBoundary) { + return layoutBeforeAnchorBoundary - viewSize; // happy case, lay it out before the anchor } - if (viewSize <= viewportSize - anchorEnd) { - return anchorEnd; // ok case, lay it out after the anchor + if (viewSize <= viewportSize - layoutAfterAnchorBoundary) { + return layoutAfterAnchorBoundary; // ok case, lay it out after the anchor } return 0; // sad case, lay it over the anchor @@ -270,28 +282,36 @@ export class ContextView extends Disposable { const anchorPosition = this.delegate!.anchorPosition || AnchorPosition.BELOW; const anchorAlignment = this.delegate!.anchorAlignment || AnchorAlignment.LEFT; + const anchorAxisAlignment = this.delegate!.anchorAxisAlignment || AnchorAxisAlignment.VERTICAL; - const verticalAnchor: ILayoutAnchor = { offset: around.top - window.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + let top: number; + let left: number; - let horizontalAnchor: ILayoutAnchor; + if (anchorAxisAlignment === AnchorAxisAlignment.VERTICAL) { + const verticalAnchor: ILayoutAnchor = { offset: around.top - window.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + const horizontalAnchor: ILayoutAnchor = { offset: around.left, size: around.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN }; - if (anchorAlignment === AnchorAlignment.LEFT) { - horizontalAnchor = { offset: around.left, size: 0, position: LayoutAnchorPosition.Before }; - } else { - horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After }; - } + top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; - const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; - - // if view intersects vertically with anchor, shift it horizontally - if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) { - horizontalAnchor.size = around.width; - if (anchorAlignment === AnchorAlignment.RIGHT) { - horizontalAnchor.offset = around.left; + // if view intersects vertically with anchor, we must avoid the anchor + if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) { + horizontalAnchor.mode = LayoutAnchorMode.AVOID; } - } - const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor); + left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor); + } else { + const horizontalAnchor: ILayoutAnchor = { offset: around.left, size: around.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN }; + + left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor); + + // if view intersects horizontally with anchor, we must avoid the anchor + if (Range.intersects({ start: left, end: left + viewSizeWidth }, { start: horizontalAnchor.offset, end: horizontalAnchor.offset + horizontalAnchor.size })) { + verticalAnchor.mode = LayoutAnchorMode.AVOID; + } + + top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; + } this.view.classList.remove('top', 'bottom', 'left', 'right'); this.view.classList.add(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 1f1180b8..7c5ffc1c 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -148,7 +148,8 @@ width: fit-content; width: -moz-fit-content; padding: 5px 10px; - margin: 4px 5px; /* allows button focus outline to be visible */ overflow: hidden; text-overflow: ellipsis; + margin: 4px 5px; /* allows button focus outline to be visible */ + outline-offset: 2px !important; } diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 097e879e..4d8a075d 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -11,13 +11,13 @@ import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; -import { ButtonGroup, IButtonStyles } from 'vs/base/browser/ui/button/button'; +import { ButtonBar, IButtonStyles } from 'vs/base/browser/ui/button/button'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { SimpleCheckbox, ISimpleCheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; export interface IDialogInputOptions { @@ -60,10 +60,10 @@ interface ButtonMapEntry { readonly index: number; } -const dialogErrorIcon = registerIcon('dialog-error', Codicon.error); -const dialogWarningIcon = registerIcon('dialog-warning', Codicon.warning); -const dialogInfoIcon = registerIcon('dialog-info', Codicon.info); -const dialogCloseIcon = registerIcon('dialog-close', Codicon.close); +const dialogErrorIcon = registerCodicon('dialog-error', Codicon.error); +const dialogWarningIcon = registerCodicon('dialog-warning', Codicon.warning); +const dialogInfoIcon = registerCodicon('dialog-info', Codicon.info); +const dialogCloseIcon = registerCodicon('dialog-close', Codicon.close); export class Dialog extends Disposable { private readonly element: HTMLElement; @@ -74,7 +74,7 @@ export class Dialog extends Disposable { private readonly iconElement: HTMLElement; private readonly checkbox: SimpleCheckbox | undefined; private readonly toolbarContainer: HTMLElement; - private buttonGroup: ButtonGroup | undefined; + private buttonBar: ButtonBar | undefined; private styles: IDialogStyles | undefined; private focusToReturn: HTMLElement | undefined; private readonly inputs: InputBox[]; @@ -173,11 +173,12 @@ export class Dialog extends Disposable { return new Promise((resolve) => { clearNode(this.buttonsContainer); - const buttonGroup = this.buttonGroup = this._register(new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true })); + const buttonBar = this.buttonBar = this._register(new ButtonBar(this.buttonsContainer)); const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId); // Handle button clicks - buttonGroup.buttons.forEach((button, index) => { + buttonMap.forEach((entry, index) => { + const button = this._register(buttonBar.addButton({ title: true })); button.label = mnemonicButtonLabel(buttonMap[index].label, true); this._register(button.onDidClick(e => { @@ -237,8 +238,8 @@ export class Dialog extends Disposable { } } - if (this.buttonGroup) { - for (const button of this.buttonGroup.buttons) { + if (this.buttonBar) { + for (const button of this.buttonBar.buttons) { focusableElements.push(button); if (button.hasFocus()) { focusedIndex = focusableElements.length - 1; @@ -349,7 +350,7 @@ export class Dialog extends Disposable { } else { buttonMap.forEach((value, index) => { if (value.index === 0) { - buttonGroup.buttons[index].focus(); + buttonBar.buttons[index].focus(); } }); } @@ -371,8 +372,8 @@ export class Dialog extends Disposable { this.element.style.backgroundColor = bgColor?.toString() ?? ''; this.element.style.border = border; - if (this.buttonGroup) { - this.buttonGroup.buttons.forEach(button => button.style(style)); + if (this.buttonBar) { + this.buttonBar.buttons.forEach(button => button.style(style)); } if (this.checkbox) { diff --git a/src/vs/base/browser/ui/dropdown/dropdown.css b/src/vs/base/browser/ui/dropdown/dropdown.css index fb87b8c5..77060ee2 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.css +++ b/src/vs/base/browser/ui/dropdown/dropdown.css @@ -12,3 +12,7 @@ cursor: pointer; height: 100%; } + +.monaco-dropdown > .dropdown-label > .action-label.disabled { + cursor: default; +} diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 5713a8a9..ea32bdc0 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -13,6 +13,7 @@ import { Emitter } from 'vs/base/common/event'; import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IActionProvider, DropdownMenu, IDropdownMenuOptions, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; +import { Codicon } from 'vs/base/common/codicons'; export interface IKeybindingProvider { (action: IAction): ResolvedKeybinding | undefined; @@ -35,6 +36,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { private menuActionsOrProvider: readonly IAction[] | IActionProvider; private dropdownMenu: DropdownMenu | undefined; private contextMenuProvider: IContextMenuProvider; + private actionItem: HTMLElement | null = null; private _onDidChangeVisibility = this._register(new Emitter()); readonly onDidChangeVisibility = this._onDidChangeVisibility.event; @@ -56,6 +58,8 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { } render(container: HTMLElement): void { + this.actionItem = container; + const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { this.element = append(el, $('a.action-label')); @@ -115,6 +119,8 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { } }; } + + this.updateEnabled(); } setActionContext(newContext: unknown): void { @@ -134,6 +140,12 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { this.dropdownMenu.show(); } } + + protected updateEnabled(): void { + const disabled = !this.getAction().enabled; + this.actionItem?.classList.toggle('disabled', disabled); + this.element?.classList.toggle('disabled', disabled); + } } export interface IActionWithDropdownActionViewItemOptions extends IActionViewItemOptions { @@ -158,7 +170,7 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem { super.render(container); if (this.element) { this.element.classList.add('action-dropdown-item'); - this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(new Action('dropdownAction', undefined), (this.options).menuActionsOrProvider, this.contextMenuProvider, { classNames: ['dropdown', 'codicon-chevron-down', ...(this.options).menuActionClassNames || []] }); + this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(new Action('dropdownAction', undefined), (this.options).menuActionsOrProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...(this.options).menuActionClassNames || []] }); this.dropdownMenuActionViewItem.render(this.element); } } diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index db736d5d..22d8a75e 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -175,7 +175,7 @@ function getGridLocation(element: HTMLElement): number[] { } const index = indexInParent(parentElement); - const ancestor = parentElement.parentElement!.parentElement!.parentElement!; + const ancestor = parentElement.parentElement!.parentElement!.parentElement!.parentElement!; return [...getGridLocation(ancestor), index]; } @@ -215,6 +215,8 @@ export class Grid extends Disposable { get boundarySashes(): IBoundarySashes { return this.gridview.boundarySashes; } set boundarySashes(boundarySashes: IBoundarySashes) { this.gridview.boundarySashes = boundarySashes; } + set edgeSnapping(edgeSnapping: boolean) { this.gridview.edgeSnapping = edgeSnapping; } + get element(): HTMLElement { return this.gridview.element; } private didLayout = false; diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index b08b72a9..323f71fd 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -170,6 +170,7 @@ class BranchNode implements ISplitView, IDisposable { private absoluteOffset: number = 0; private absoluteOrthogonalOffset: number = 0; + private absoluteOrthogonalSize: number = 0; private _styles: IGridViewStyles; get styles(): IGridViewStyles { return this._styles; } @@ -270,6 +271,24 @@ class BranchNode implements ISplitView, IDisposable { } } + private _edgeSnapping = false; + get edgeSnapping(): boolean { return this._edgeSnapping; } + set edgeSnapping(edgeSnapping: boolean) { + if (this._edgeSnapping === edgeSnapping) { + return; + } + + this._edgeSnapping = edgeSnapping; + + for (const child of this.children) { + if (child instanceof BranchNode) { + child.edgeSnapping = edgeSnapping; + } + } + + this.updateSplitviewEdgeSnappingEnablement(); + } + constructor( readonly orientation: Orientation, readonly layoutController: ILayoutController, @@ -277,6 +296,7 @@ class BranchNode implements ISplitView, IDisposable { readonly proportionalLayout: boolean, size: number = 0, orthogonalSize: number = 0, + edgeSnapping: boolean = false, childDescriptors?: INodeDescriptor[] ) { this._styles = styles; @@ -355,6 +375,7 @@ class BranchNode implements ISplitView, IDisposable { this._orthogonalSize = size; this.absoluteOffset = ctx.absoluteOffset + offset; this.absoluteOrthogonalOffset = ctx.absoluteOrthogonalOffset; + this.absoluteOrthogonalSize = ctx.absoluteOrthogonalSize; this.splitview.layout(ctx.orthogonalSize, { orthogonalSize: size, @@ -364,9 +385,7 @@ class BranchNode implements ISplitView, IDisposable { absoluteOrthogonalSize: ctx.absoluteSize }); - // Disable snapping on views which sit on the edges of the grid - this.splitview.startSnappingEnabled = this.absoluteOrthogonalOffset > 0; - this.splitview.endSnappingEnabled = this.absoluteOrthogonalOffset + ctx.orthogonalSize < ctx.absoluteOrthogonalSize; + this.updateSplitviewEdgeSnappingEnablement(); } setVisible(visible: boolean): void { @@ -607,6 +626,11 @@ class BranchNode implements ISplitView, IDisposable { }); } + private updateSplitviewEdgeSnappingEnablement(): void { + this.splitview.startSnappingEnabled = this._edgeSnapping || this.absoluteOrthogonalOffset > 0; + this.splitview.endSnappingEnabled = this._edgeSnapping || this.absoluteOrthogonalOffset + this._size < this.absoluteOrthogonalSize; + } + dispose(): void { for (const child of this.children) { child.dispose(); @@ -775,7 +799,7 @@ export interface INodeDescriptor { function flipNode(node: T, size: number, orthogonalSize: number): T { if (node instanceof BranchNode) { - const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize); + const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize, node.edgeSnapping); let totalSize = 0; @@ -863,6 +887,10 @@ export class GridView implements IDisposable { this.root.boundarySashes = fromAbsoluteBoundarySashes(boundarySashes, this.orientation); } + set edgeSnapping(edgeSnapping: boolean) { + this.root.edgeSnapping = edgeSnapping; + } + /** * The first layout controller makes sure layout only propagates * to the views after the very first call to gridview.layout() @@ -932,7 +960,7 @@ export class GridView implements IDisposable { grandParent.removeChild(parentIndex); - const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); + const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize, grandParent.edgeSnapping); grandParent.addChild(newParent, parent.size, parentIndex); const newSibling = new LeafNode(parent.view, grandParent.orientation, this.layoutController, parent.size); @@ -1205,7 +1233,7 @@ export class GridView implements IDisposable { } as INodeDescriptor; }); - result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, children); + result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, undefined, children); } else { result = new LeafNode(deserializer.fromJSON(node.data), orientation, this.layoutController, orthogonalSize, node.size); } diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css index a5390be4..c2e11fa8 100644 --- a/src/vs/base/browser/ui/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -87,7 +87,6 @@ .monaco-hover .monaco-tokenized-source { white-space: pre-wrap; - word-break: break-all; } .monaco-hover .hover-row.status-bar { diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 18a32bf9..44c56834 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -14,7 +14,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { isString } from 'vs/base/common/types'; +import { isFunction, isString } from 'vs/base/common/types'; import { domEvent } from 'vs/base/browser/event'; export interface IIconLabelCreationOptions { @@ -24,8 +24,13 @@ export interface IIconLabelCreationOptions { hoverDelegate?: IHoverDelegate; } +export interface IIconLabelMarkdownString { + markdown: IMarkdownString | string | undefined | (() => Promise); + markdownNotSupportedFallback: string | undefined; +} + export interface IIconLabelValueOptions { - title?: string | IMarkdownString | Promise; + title?: string | IIconLabelMarkdownString; descriptionTitle?: string; hideIcon?: boolean; extraClasses?: string[]; @@ -93,6 +98,8 @@ export class IconLabel extends Disposable { private descriptionNode: FastLabelNode | HighlightedLabel | undefined; private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel; + private labelContainer: HTMLElement; + private hoverDelegate: IHoverDelegate | undefined = undefined; private readonly customHovers: Map = new Map(); @@ -101,10 +108,10 @@ export class IconLabel extends Disposable { this.domNode = this._register(new FastLabelNode(dom.append(container, dom.$('.monaco-icon-label')))); - const labelContainer = dom.append(this.domNode.element, dom.$('.monaco-icon-label-container')); + this.labelContainer = dom.append(this.domNode.element, dom.$('.monaco-icon-label-container')); - const nameContainer = dom.append(labelContainer, dom.$('span.monaco-icon-name-container')); - this.descriptionContainer = this._register(new FastLabelNode(dom.append(labelContainer, dom.$('span.monaco-icon-description-container')))); + const nameContainer = dom.append(this.labelContainer, dom.$('span.monaco-icon-name-container')); + this.descriptionContainer = this._register(new FastLabelNode(dom.append(this.labelContainer, dom.$('span.monaco-icon-description-container')))); if (options?.supportHighlights) { this.nameNode = new LabelWithHighlights(nameContainer, !!options.supportCodicons); @@ -144,7 +151,7 @@ export class IconLabel extends Disposable { } this.domNode.className = classes.join(' '); - this.setupHover(this.domNode.element, options?.title); + this.setupHover(this.labelContainer, options?.title); this.nameNode.setLabel(label, options); @@ -164,7 +171,7 @@ export class IconLabel extends Disposable { } } - private setupHover(htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { + private setupHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void { const previousCustomHover = this.customHovers.get(htmlElement); if (previousCustomHover) { previousCustomHover.dispose(); @@ -183,22 +190,39 @@ export class IconLabel extends Disposable { } } - private setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { + private setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, markdownTooltip: string | IIconLabelMarkdownString): void { htmlElement.removeAttribute('title'); + let tooltip: () => Promise; + if (isString(markdownTooltip)) { + tooltip = async () => markdownTooltip; + } else if (isFunction(markdownTooltip.markdown)) { + tooltip = markdownTooltip.markdown; + } else { + const markdown = markdownTooltip.markdown; + tooltip = async () => markdown; + } // Testing has indicated that on Windows and Linux 500 ms matches the native hovers most closely. // On Mac, the delay is 1500. const hoverDelay = isMacintosh ? 1500 : 500; let hoverOptions: IHoverDelegateOptions | undefined; let mouseX: number | undefined; + let isHovering = false; function mouseOver(this: HTMLElement, e: MouseEvent): any { - let isHovering = true; + if (isHovering) { + return; + } + function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): any { + isHovering = false; + mouseLeaveDisposable.dispose(); + mouseDownDisposable.dispose(); + } + const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeaveOrDown.bind(htmlElement)); + const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement)); + isHovering = true; + function mouseMove(this: HTMLElement, e: MouseEvent): any { mouseX = e.x; } - function mouseLeave(this: HTMLElement, e: MouseEvent): any { - isHovering = false; - } - const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeave.bind(htmlElement)); const mouseMoveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_MOVE, true)(mouseMove.bind(htmlElement)); setTimeout(async () => { if (isHovering && tooltip) { @@ -208,7 +232,7 @@ export class IconLabel extends Disposable { targetElements: [this], dispose: () => { } }; - const resolvedTooltip = await tooltip; + const resolvedTooltip = await tooltip(); if (resolvedTooltip) { hoverOptions = { text: resolvedTooltip, @@ -217,7 +241,8 @@ export class IconLabel extends Disposable { }; } } - if (hoverOptions) { + // awaiting the tooltip could take a while. Make sure we're still hovering. + if (hoverOptions && isHovering) { if (mouseX !== undefined) { (hoverOptions.target).x = mouseX + 10; } @@ -225,15 +250,20 @@ export class IconLabel extends Disposable { } } mouseMoveDisposable.dispose(); - mouseLeaveDisposable.dispose(); }, hoverDelay); } const mouseOverDisposable = this._register(domEvent(htmlElement, dom.EventType.MOUSE_OVER, true)(mouseOver.bind(htmlElement))); this.customHovers.set(htmlElement, mouseOverDisposable); } - private setupNativeHover(htmlElement: HTMLElement, tooltip: string | IMarkdownString | Promise | undefined): void { - htmlElement.title = isString(tooltip) ? tooltip : ''; + private setupNativeHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void { + let stringTooltip: string = ''; + if (isString(tooltip)) { + stringTooltip = tooltip; + } else if (tooltip?.markdownNotSupportedFallback) { + stringTooltip = tooltip.markdownNotSupportedFallback; + } + htmlElement.title = stringTooltip; } } diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index 9341feba..06224874 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -60,12 +60,12 @@ } .monaco-icon-label.italic > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, -.monaco-icon-label.italic > .monaco-icon-description-container > .label-description { +.monaco-icon-label.italic > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { font-style: italic; } .monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, -.monaco-icon-label.strikethrough > .monaco-icon-description-container > .label-description { +.monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { text-decoration: line-through; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 03db9d2a..e2e8e849 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -258,6 +258,10 @@ export class PagedList implements IThemable, IDisposable { return this.list.getSelection(); } + getSelectedElements(): T[] { + return this.getSelection().map(i => this.model.get(i)); + } + layout(height?: number, width?: number): void { this.list.layout(height, width); } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index c527ca29..26b19843 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -327,7 +327,6 @@ export class ListView implements ISpliceable, IDisposable { this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => scheduleAtNextAnimationFrame(cb)); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { - alwaysConsumeMouseWheel: true, horizontal: ScrollbarVisibility.Auto, vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), @@ -1323,6 +1322,9 @@ export class ListView implements ISpliceable, IDisposable { if (item.row) { const renderer = this.renderers.get(item.row.templateId); if (renderer) { + if (renderer.disposeElement) { + renderer.disposeElement(item.element, -1, item.row.templateData, undefined); + } renderer.disposeTemplate(item.row.templateData); } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 39d1bbed..e530b43d 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -318,10 +318,12 @@ class KeyboardController implements IDisposable { } private onEscape(e: StandardKeyboardEvent): void { - e.preventDefault(); - e.stopPropagation(); - this.list.setSelection([], e.browserEvent); - this.view.domNode.focus(); + if (this.list.getSelection().length) { + e.preventDefault(); + e.stopPropagation(); + this.list.setSelection([], e.browserEvent); + this.view.domNode.focus(); + } } dispose() { @@ -1460,6 +1462,8 @@ export class List implements ISpliceable, IThemable, IDisposable { this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); if (this.view.getScrollTop() !== previousScrollTop) { + this.setFocus([]); + // Let the scroll event listener run setTimeout(() => this.focusNextPage(browserEvent, filter), 0); } @@ -1492,6 +1496,8 @@ export class List implements ISpliceable, IThemable, IDisposable { this.view.setScrollTop(scrollTop - this.view.renderHeight); if (this.view.getScrollTop() !== previousScrollTop) { + this.setFocus([]); + // Let the scroll event listener run setTimeout(() => this.focusPreviousPage(browserEvent, filter), 0); } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 8b3da5df..fc129fef 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -18,7 +18,7 @@ import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { Event } from 'vs/base/common/event'; import { AnchorAlignment, layout, LayoutAnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon, stripCodicons } from 'vs/base/common/codicons'; import { BaseActionViewItem, ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { formatRule } from 'vs/base/browser/ui/codicons/codiconStyles'; import { isFirefox } from 'vs/base/browser/browser'; @@ -27,8 +27,8 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/; export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\s&])/g; -const menuSelectionIcon = registerIcon('menu-selection', Codicon.check); -const menuSubmenuIcon = registerIcon('menu-submenu', Codicon.chevronRight); +const menuSelectionIcon = registerCodicon('menu-selection', Codicon.check); +const menuSubmenuIcon = registerCodicon('menu-submenu', Codicon.chevronRight); export enum Direction { Right, @@ -728,6 +728,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { if (this.item) { this.item.classList.add('monaco-submenu-item'); + this.item.tabIndex = 0; this.item.setAttribute('aria-haspopup', 'true'); this.updateAriaExpanded('false'); this.submenuIndicator = append(this.item, $('span.submenu-indicator' + menuSubmenuIcon.cssSelector)); @@ -777,6 +778,12 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { })); } + updateEnabled(): void { + // override on submenu entry + // native menus do not observe enablement on sumbenus + // we mimic that behavior + } + open(selectFirst?: boolean): void { this.cleanupExistingSubmenu(false); this.createSubmenu(selectFirst); @@ -1189,6 +1196,7 @@ ${formatRule(menuSubmenuIcon)} outline: 0; border: none; animation: fadeIn 0.083s linear; + -webkit-app-region: no-drag; } .context-view.monaco-menu-container :focus, diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index cb2b3fc0..55858a58 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -21,11 +21,11 @@ import { asArray } from 'vs/base/common/arrays'; import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode'; import { isMacintosh } from 'vs/base/common/platform'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; const $ = DOM.$; -const menuBarMoreIcon = registerIcon('menubar-more', Codicon.more); +const menuBarMoreIcon = registerCodicon('menubar-more', Codicon.more); export interface IMenuBarOptions { enableMnemonics?: boolean; @@ -115,7 +115,7 @@ export class MenuBar extends Disposable { this.menuUpdater = this._register(new RunOnceScheduler(() => this.update(), 200)); this.actionRunner = this._register(new ActionRunner()); - this._register(this.actionRunner.onDidBeforeRun(() => { + this._register(this.actionRunner.onBeforeRun(() => { this.setUnfocusedState(); })); diff --git a/src/vs/base/browser/ui/progressbar/progressbar.css b/src/vs/base/browser/ui/progressbar/progressbar.css index 64368d69..55e6a1a6 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.css +++ b/src/vs/base/browser/ui/progressbar/progressbar.css @@ -42,7 +42,10 @@ * The progress bit has a width: 2% (1/50) of the parent container. The animation moves it from 0% to 100% of * that container. Since translateX is relative to the progress bit size, we have to multiple it with * its relative size to the parent container: - * 50%: 50 * 50 = 2500% - * 100%: 50 * 100 - 50 (do not overflow): 4950% + * parent width: 5000% + * bit width: 100% + * translateX should be as follow: + * 50%: 5000% * 50% - 50% (set to center) = 2450% + * 100%: 5000% * 100% - 100% (do not overflow) = 4900% */ -@keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4950%) scaleX(1) } } +@keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4900%) scaleX(1) } } diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts index 5e47bcb1..a8aa8b61 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.ts +++ b/src/vs/base/browser/ui/progressbar/progressbar.ts @@ -58,6 +58,7 @@ export class ProgressBar extends Disposable { this.element = document.createElement('div'); this.element.classList.add('monaco-progress-container'); this.element.setAttribute('role', 'progressbar'); + this.element.setAttribute('aria-valuemin', '0'); container.appendChild(this.element); this.bit = document.createElement('div'); diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index ac9e628c..db8977d6 100644 --- a/src/vs/base/browser/ui/sash/sash.css +++ b/src/vs/base/browser/ui/sash/sash.css @@ -60,19 +60,48 @@ height: var(--sash-size); } -.monaco-sash:not(.disabled).orthogonal-start::before, .monaco-sash:not(.disabled).orthogonal-end::after { - content: ' '; +.monaco-sash:not(.disabled).orthogonal-start::before, +.monaco-sash:not(.disabled).orthogonal-end::after { + content: " "; height: calc(var(--sash-size) * 2); width: calc(var(--sash-size) * 2); z-index: 100; display: block; - cursor: all-scroll; position: absolute; + cursor: all-scroll; + position: absolute; } -.monaco-sash.orthogonal-start.vertical::before { left: -calc(var(--sash-size) / 2); top: calc(var(--sash-size) * -1); } -.monaco-sash.orthogonal-end.vertical::after { left: -calc(var(--sash-size) / 2); bottom: calc(var(--sash-size) * -1); } -.monaco-sash.orthogonal-start.horizontal::before { top: -calc(var(--sash-size) / 2); left: calc(var(--sash-size) * -1); } -.monaco-sash.orthogonal-end.horizontal::after { top: -calc(var(--sash-size) / 2); right: calc(var(--sash-size) * -1); } +.monaco-sash.horizontal.orthogonal-edge-north:not(.disabled).orthogonal-start::before, +.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled).orthogonal-end::after { + cursor: nwse-resize; +} + +.monaco-sash.horizontal.orthogonal-edge-north:not(.disabled).orthogonal-end::after, +.monaco-sash.horizontal.orthogonal-edge-south:not(.disabled).orthogonal-start::before { + cursor: nesw-resize; +} + +.monaco-sash.orthogonal-start.vertical::before { + left: -calc(var(--sash-size) / 2); + top: calc(var(--sash-size) * -1); +} +.monaco-sash.orthogonal-end.vertical::after { + left: -calc(var(--sash-size) / 2); + bottom: calc(var(--sash-size) * -1); +} +.monaco-sash.orthogonal-start.horizontal::before { + top: -calc(var(--sash-size) / 2); + left: calc(var(--sash-size) * -1); +} +.monaco-sash.orthogonal-end.horizontal::after { + top: -calc(var(--sash-size) / 2); + right: calc(var(--sash-size) * -1); +} + +.monaco-sash { + transition: background-color 0.1s ease-out; + background: transparent; +} /** Debug **/ diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 3f7801dd..fa0aa709 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -37,11 +37,19 @@ export interface ISashEvent { altKey: boolean; } +export enum OrthogonalEdge { + North = 'north', + South = 'south', + East = 'east', + West = 'west' +} + export interface ISashOptions { readonly orientation: Orientation; readonly orthogonalStartSash?: Sash; readonly orthogonalEndSash?: Sash; readonly size?: number; + readonly orthogonalEdge?: OrthogonalEdge; } export interface IVerticalSashOptions extends ISashOptions { @@ -150,6 +158,10 @@ export class Sash extends Disposable { this.el = append(container, $('.monaco-sash')); + if (options.orthogonalEdge) { + this.el.classList.add(`orthogonal-edge-${options.orthogonalEdge}`); + } + if (isMacintosh) { this.el.classList.add('mac'); } diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 23bfdb2c..133ec935 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -38,12 +38,14 @@ export interface AbstractScrollbarOptions { visibility: ScrollbarVisibility; extraScrollbarClassName: string; scrollable: Scrollable; + scrollByPage: boolean; } export abstract class AbstractScrollbar extends Widget { protected _host: ScrollbarHost; protected _scrollable: Scrollable; + protected _scrollByPage: boolean; private _lazyRender: boolean; protected _scrollbarState: ScrollbarState; private _visibilityController: ScrollbarVisibilityController; @@ -59,6 +61,7 @@ export abstract class AbstractScrollbar extends Widget { this._lazyRender = opts.lazyRender; this._host = opts.host; this._scrollable = opts.scrollable; + this._scrollByPage = opts.scrollByPage; this._scrollbarState = opts.scrollbarState; this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName)); this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); @@ -210,7 +213,14 @@ export abstract class AbstractScrollbar extends Widget { offsetX = e.posx - domNodePosition.left; offsetY = e.posy - domNodePosition.top; } - this._setDesiredScrollPositionNow(this._scrollbarState.getDesiredScrollPositionFromOffset(this._mouseDownRelativePosition(offsetX, offsetY))); + + const offset = this._mouseDownRelativePosition(offsetX, offsetY); + this._setDesiredScrollPositionNow( + this._scrollByPage + ? this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset) + : this._scrollbarState.getDesiredScrollPositionFromOffset(offset) + ); + if (e.leftButton) { e.preventDefault(); this._sliderMouseDown(e, () => { /*nothing to do*/ }); diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 6e7f132e..9d9a2268 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -9,11 +9,11 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; -const scrollbarButtonLeftIcon = registerIcon('scrollbar-button-left', Codicon.triangleLeft); -const scrollbarButtonRightIcon = registerIcon('scrollbar-button-right', Codicon.triangleRight); +const scrollbarButtonLeftIcon = registerCodicon('scrollbar-button-left', Codicon.triangleLeft); +const scrollbarButtonRightIcon = registerCodicon('scrollbar-button-right', Codicon.triangleRight); export class HorizontalScrollbar extends AbstractScrollbar { @@ -33,7 +33,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { ), visibility: options.horizontal, extraScrollbarClassName: 'horizontal', - scrollable: scrollable + scrollable: scrollable, + scrollByPage: options.scrollByPage }); if (options.horizontalHasArrows) { diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 00bb9830..115864f3 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -361,6 +361,8 @@ export abstract class AbstractScrollableElement extends Widget { // console.log(`${Date.now()}, ${e.deltaY}, ${e.deltaX}`); + let didScroll = false; + if (e.deltaY || e.deltaX) { let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity; let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity; @@ -419,11 +421,12 @@ export abstract class AbstractScrollableElement extends Widget { } else { this._scrollable.setScrollPositionNow(desiredScrollPosition); } - this._shouldRender = true; + + didScroll = true; } } - if (this._options.alwaysConsumeMouseWheel || this._shouldRender) { + if (this._options.alwaysConsumeMouseWheel || didScroll) { e.preventDefault(); e.stopPropagation(); } @@ -614,7 +617,9 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto), verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10), verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false), - verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0) + verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0), + + scrollByPage: (typeof opts.scrollByPage !== 'undefined' ? opts.scrollByPage : false) }; result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts index afb227be..556d2083 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -114,6 +114,11 @@ export interface ScrollableElementCreationOptions { * Defaults to false. */ verticalHasArrows?: boolean; + /** + * Scroll gutter clicks move by page vs. jump to position. + * Defaults to false. + */ + scrollByPage?: boolean; } export interface ScrollableElementChangeOptions { @@ -146,4 +151,5 @@ export interface ScrollableElementResolvedOptions { verticalScrollbarSize: number; verticalSliderSize: number; verticalHasArrows: boolean; + scrollByPage: boolean; } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index 48e20a5a..d0d98b9a 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts @@ -202,6 +202,28 @@ export class ScrollbarState { return Math.round(desiredSliderPosition / this._computedSliderRatio); } + /** + * Compute a desired `scrollPosition` from if offset is before or after the slider position. + * If offset is before slider, treat as a page up (or left). If after, page down (or right). + * `offset` and `_computedSliderPosition` are based on the same coordinate system. + * `_visibleSize` corresponds to a "page" of lines in the returned coordinate system. + */ + public getDesiredScrollPositionFromOffsetPaged(offset: number): number { + if (!this._computedIsNeeded) { + // no need for a slider + return 0; + } + + let correctedOffset = offset - this._arrowSize; // compensate if has arrows + let desiredScrollPosition = this._scrollPosition; + if (correctedOffset < this._computedSliderPosition) { + desiredScrollPosition -= this._visibleSize; // page up/left + } else { + desiredScrollPosition += this._visibleSize; // page down/right + } + return desiredScrollPosition; + } + /** * Compute a desired `scrollPosition` such that the slider moves by `delta`. */ diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index 296913a3..12d2887b 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -9,10 +9,10 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; -const scrollbarButtonUpIcon = registerIcon('scrollbar-button-up', Codicon.triangleUp); -const scrollbarButtonDownIcon = registerIcon('scrollbar-button-down', Codicon.triangleDown); +const scrollbarButtonUpIcon = registerCodicon('scrollbar-button-up', Codicon.triangleUp); +const scrollbarButtonDownIcon = registerCodicon('scrollbar-button-down', Codicon.triangleDown); export class VerticalScrollbar extends AbstractScrollbar { @@ -33,7 +33,8 @@ export class VerticalScrollbar extends AbstractScrollbar { ), visibility: options.vertical, extraScrollbarClassName: 'vertical', - scrollable: scrollable + scrollable: scrollable, + scrollByPage: options.scrollByPage }); if (options.verticalHasArrows) { diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 79f0da0e..4315b8e8 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -46,6 +46,7 @@ export interface ISelectBoxOptions { // Utilize optionItem interface to capture all option parameters export interface ISelectOptionItem { text: string; + detail?: string; decoratorRight?: string; description?: string; descriptionIsMarkdown?: boolean; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css index 536a9422..f385bf29 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css @@ -75,6 +75,15 @@ float: left; } +.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-detail { + text-overflow: ellipsis; + overflow: hidden; + padding-left: 3.5px; + white-space: nowrap; + float: left; + opacity: 0.7; +} + .monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-decorator-right { text-overflow: ellipsis; overflow: hidden; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 62438e1b..841f2ee4 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -29,6 +29,7 @@ const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'selectOption.entry.template'; interface ISelectListTemplateData { root: HTMLElement; text: HTMLElement; + detail: HTMLElement; decoratorRight: HTMLElement; disposables: IDisposable[]; } @@ -42,6 +43,7 @@ class SelectListRenderer implements IListRenderer { - const len = option.text.length + (!!option.decoratorRight ? option.decoratorRight.length : 0); + const detailLength = !!option.detail ? option.detail.length : 0; + const rightDecoratorLength = !!option.decoratorRight ? option.decoratorRight.length : 0; + + const len = option.text.length + detailLength + rightDecoratorLength; if (len > longestLength) { longest = index; longestLength = len; @@ -697,6 +706,10 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi accessibilityProvider: { getAriaLabel: element => { let label = element.text; + if (element.detail) { + label += `. ${element.detail}`; + } + if (element.decoratorRight) { label += `. ${element.decoratorRight}`; } diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css index 06743287..3ffad073 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -38,23 +38,12 @@ width: 22px; } -.monaco-pane-view .pane > .pane-header > .twisties { - width: 20px; - display: flex; - align-items: center; - justify-content: center; - transform-origin: center; - color: inherit; - flex-shrink: 0; +.monaco-pane-view .pane > .pane-header > .codicon:first-of-type { + margin: 0 2px; } -.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .twisties { - margin-top: 2px; - margin-bottom: 2px; -} - -.monaco-pane-view .pane > .pane-header.expanded > .twisties::before { - transform: rotate(90deg); +.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .codicon:first-of-type { + margin: 2px; } /* TODO: actions should be part of the pane, but they aren't yet */ diff --git a/src/vs/base/browser/ui/splitview/splitview.css b/src/vs/base/browser/ui/splitview/splitview.css index e5baba1f..3af3e906 100644 --- a/src/vs/base/browser/ui/splitview/splitview.css +++ b/src/vs/base/browser/ui/splitview/splitview.css @@ -20,31 +20,36 @@ pointer-events: initial; } -.monaco-split-view2 > .split-view-container { +.monaco-split-view2 > .monaco-scrollable-element { + width: 100%; + height: 100%; +} + +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container { width: 100%; height: 100%; white-space: nowrap; position: relative; } -.monaco-split-view2 > .split-view-container > .split-view-view { +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view { white-space: initial; position: absolute; } -.monaco-split-view2 > .split-view-container > .split-view-view:not(.visible) { +.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view:not(.visible) { display: none; } -.monaco-split-view2.vertical > .split-view-container > .split-view-view { +.monaco-split-view2.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view { width: 100%; } -.monaco-split-view2.horizontal > .split-view-container > .split-view-view { +.monaco-split-view2.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view { height: 100%; } -.monaco-split-view2.separator-border > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { content: ' '; position: absolute; top: 0; @@ -54,12 +59,12 @@ background-color: var(--separator-border); } -.monaco-split-view2.separator-border.horizontal > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border.horizontal > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { height: 100%; width: 1px; } -.monaco-split-view2.separator-border.vertical > .split-view-container > .split-view-view:not(:first-child)::before { +.monaco-split-view2.separator-border.vertical > .monaco-scrollable-element > .split-view-container > .split-view-view:not(:first-child)::before { height: 1px; width: 100%; } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 10c15797..8bb89391 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -12,7 +12,9 @@ import { range, pushToStart, pushToEnd } from 'vs/base/common/arrays'; import { Sash, Orientation, ISashEvent as IBaseSashEvent, SashState } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { domEvent } from 'vs/base/browser/event'; -import { $, append } from 'vs/base/browser/dom'; +import { $, append, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export interface ISplitViewStyles { @@ -213,6 +215,8 @@ export class SplitView extends Disposable { readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; + private scrollable: Scrollable; + private scrollableElement: SmoothScrollableElement; private size = 0; private layoutContext: TLayoutContext | undefined; private contentSize = 0; @@ -301,7 +305,20 @@ export class SplitView extends Disposable { container.appendChild(this.el); this.sashContainer = append(this.el, $('.sash-container')); - this.viewContainer = append(this.el, $('.split-view-container')); + this.viewContainer = $('.split-view-container'); + + this.scrollable = new Scrollable(125, scheduleAtNextAnimationFrame); + this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, { + vertical: this.orientation === Orientation.VERTICAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, + horizontal: this.orientation === Orientation.HORIZONTAL ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden + }, this.scrollable)); + + this._register(this.scrollableElement.onScroll(e => { + this.viewContainer.scrollTop = e.scrollTop; + this.viewContainer.scrollLeft = e.scrollLeft; + })); + + append(this.el, this.scrollableElement.getDomNode()); this.style(options.styles || defaultStyles); @@ -897,6 +914,21 @@ export class SplitView extends Disposable { // Layout sashes this.sashItems.forEach(item => item.sash.layout()); this.updateSashEnablement(); + this.updateScrollableElement(); + } + + private updateScrollableElement(): void { + if (this.orientation === Orientation.VERTICAL) { + this.scrollableElement.setScrollDimensions({ + height: this.size, + scrollHeight: this.contentSize + }); + } else { + this.scrollableElement.setScrollDimensions({ + width: this.size, + scrollWidth: this.contentSize + }); + } } private updateSashEnablement(): void { diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index d2b36144..d782e1bc 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -11,12 +11,12 @@ import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, CSSIcon, registerCodicon } from 'vs/base/common/codicons'; import { EventMultiplexer } from 'vs/base/common/event'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; -const toolBarMoreIcon = registerIcon('toolbar-more', Codicon.more); +const toolBarMoreIcon = registerCodicon('toolbar-more', Codicon.more); export interface IToolBarOptions { orientation?: ActionsOrientation; @@ -27,6 +27,7 @@ export interface IToolBarOptions { toggleMenuTitle?: string; anchorAlignmentProvider?: () => AnchorAlignment; renderDropdownAsChildElement?: boolean; + moreIcon?: CSSIcon; } /** @@ -72,7 +73,7 @@ export class ToolBar extends Disposable { actionViewItemProvider: this.options.actionViewItemProvider, actionRunner: this.actionRunner, keybindingProvider: this.options.getKeyBinding, - classNames: toolBarMoreIcon.classNamesArray, + classNames: (options.moreIcon ?? toolBarMoreIcon).classNames, anchorAlignmentProvider: this.options.anchorAlignmentProvider, menuAsChild: !!this.options.renderDropdownAsChildElement } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index fa8ac3e8..4df19745 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -400,15 +400,23 @@ class TreeRenderer implements IListRenderer } private renderTwistie(node: ITreeNode, templateData: ITreeListTemplateData) { + templateData.twistie.classList.remove(...treeItemExpandedIcon.classNamesArray); + + let twistieRendered = false; + if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(node.element, templateData.twistie); + twistieRendered = this.renderer.renderTwistie(node.element, templateData.twistie); } if (node.collapsible && (!this.hideTwistiesOfChildlessElements || node.visibleChildrenCount > 0)) { - templateData.twistie.classList.add(...treeItemExpandedIcon.classNamesArray, 'collapsible'); + if (!twistieRendered) { + templateData.twistie.classList.add(...treeItemExpandedIcon.classNamesArray); + } + + templateData.twistie.classList.add('collapsible'); templateData.twistie.classList.toggle('collapsed', node.collapsed); } else { - templateData.twistie.classList.remove(...treeItemExpandedIcon.classNamesArray, 'collapsible', 'collapsed'); + templateData.twistie.classList.remove('collapsible', 'collapsed'); } if (node.collapsible) { @@ -811,7 +819,7 @@ class TypeFilterController implements IDisposable { const onDragOver = (event: DragEvent) => { event.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - const x = event.screenX - left; + const x = event.clientX - left; if (event.dataTransfer) { event.dataTransfer.dropEffect = 'none'; } @@ -954,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; readonly expandOnlyOnDoubleClick?: boolean; + readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T } export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { @@ -961,7 +970,6 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; - readonly expandOnlyOnTwistieClick?: boolean | ((e: T) => boolean); readonly additionalScrollHeight?: number; } @@ -1109,7 +1117,7 @@ class TreeNodeListMouseController extends MouseController< expandOnlyOnTwistieClick = !!this.tree.expandOnlyOnTwistieClick; } - if (expandOnlyOnTwistieClick && !onTwistie) { + if (expandOnlyOnTwistieClick && !onTwistie && e.browserEvent.detail !== 2) { return super.onViewPointer(e); } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 970eddf5..e4a93aef 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -110,10 +110,11 @@ class AsyncDataTreeRenderer implements IT renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + return true; } else { twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + return false; } - return false; } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { @@ -1053,10 +1054,11 @@ class CompressibleAsyncDataTreeRenderer i renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + return true; } else { twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + return false; } - return false; } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index 150f49ce..3dc8ab96 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -38,8 +38,8 @@ export function compress(element: ICompressedTreeElement): ITreeElement>; - let children: ITreeElement[]; + let childrenIterator: Iterable>; + let children: ICompressedTreeElement[]; while (true) { [children, childrenIterator] = Iterable.consume(Iterable.from(element.children), 2); @@ -48,12 +48,11 @@ export function compress(element: ICompressedTreeElement): ITreeElement, TFilterData, TTemplateDat this.renderer.disposeTemplate(templateData.data); } - renderTwistie?(element: T, twistieElement: HTMLElement): void { + renderTwistie?(element: T, twistieElement: HTMLElement): boolean { if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(element, twistieElement); + return this.renderer.renderTwistie(element, twistieElement); } + return false; } } diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 085bb3d9..c17b41fb 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -129,7 +129,7 @@ export interface ITreeModel { } export interface ITreeRenderer extends IListRenderer, TTemplateData> { - renderTwistie?(element: T, twistieElement: HTMLElement): void; + renderTwistie?(element: T, twistieElement: HTMLElement): boolean; onDidChangeTwistieState?: Event; } diff --git a/src/vs/base/browser/ui/tree/treeIcons.ts b/src/vs/base/browser/ui/tree/treeIcons.ts index bccd5188..9755ae59 100644 --- a/src/vs/base/browser/ui/tree/treeIcons.ts +++ b/src/vs/base/browser/ui/tree/treeIcons.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon, registerCodicon } from 'vs/base/common/codicons'; -export const treeItemExpandedIcon = registerIcon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation +export const treeItemExpandedIcon = registerCodicon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation -export const treeFilterOnTypeOnIcon = registerIcon('tree-filter-on-type-on', Codicon.listFilter); -export const treeFilterOnTypeOffIcon = registerIcon('tree-filter-on-type-off', Codicon.listSelection); -export const treeFilterClearIcon = registerIcon('tree-filter-clear', Codicon.close); +export const treeFilterOnTypeOnIcon = registerCodicon('tree-filter-on-type-on', Codicon.listFilter); +export const treeFilterOnTypeOffIcon = registerCodicon('tree-filter-on-type-off', Codicon.listSelection); +export const treeFilterClearIcon = registerCodicon('tree-filter-clear', Codicon.close); -export const treeItemLoadingIcon = registerIcon('tree-item-loading', Codicon.loading); +export const treeItemLoadingIcon = registerCodicon('tree-item-loading', Codicon.loading); diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index cb4b12d5..8be6f19a 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -36,7 +36,7 @@ export interface IAction extends IDisposable { export interface IActionRunner extends IDisposable { run(action: IAction, context?: any): Promise; readonly onDidRun: Event; - readonly onDidBeforeRun: Event; + readonly onBeforeRun: Event; } export interface IActionViewItem extends IDisposable { @@ -178,8 +178,8 @@ export interface IRunEvent { export class ActionRunner extends Disposable implements IActionRunner { - private _onDidBeforeRun = this._register(new Emitter()); - readonly onDidBeforeRun: Event = this._onDidBeforeRun.event; + private _onBeforeRun = this._register(new Emitter()); + readonly onBeforeRun: Event = this._onBeforeRun.event; private _onDidRun = this._register(new Emitter()); readonly onDidRun: Event = this._onDidRun.event; @@ -189,7 +189,7 @@ export class ActionRunner extends Disposable implements IActionRunner { return Promise.resolve(null); } - this._onDidBeforeRun.fire({ action: action }); + this._onBeforeRun.fire({ action: action }); try { const result = await this.runAction(action, context); @@ -235,6 +235,17 @@ export class Separator extends Action { } } +export class ActionWithMenuAction extends Action { + + get actions(): IAction[] { + return this._actions; + } + + constructor(id: string, private _actions: IAction[], label?: string, cssClass?: string, enabled?: boolean, actionCallback?: (event?: any) => Promise) { + super(id, label, cssClass, enabled, actionCallback); + } +} + export class SubmenuAction extends Action { get actions(): IAction[] { @@ -242,7 +253,7 @@ export class SubmenuAction extends Action { } constructor(id: string, label: string, private _actions: IAction[], cssClass?: string) { - super(id, label, cssClass, true); + super(id, label, cssClass, !!_actions?.length); } } diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index d8ce68b5..a0ee6f54 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { mergeSort } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; /** @@ -18,3 +19,133 @@ export function getPathFromAmdModule(requirefn: typeof require, relativePath: st export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI { return URI.parse(requirefn.toUrl(relativePath)); } + +export abstract class LoaderStats { + abstract get amdLoad(): [string, number][]; + abstract get amdInvoke(): [string, number][]; + abstract get nodeRequire(): [string, number][]; + abstract get nodeEval(): [string, number][]; + abstract get nodeRequireTotal(): number; + + + static get(): LoaderStats { + + + const amdLoadScript = new Map(); + const amdInvokeFactory = new Map(); + const nodeRequire = new Map(); + const nodeEval = new Map(); + + function mark(map: Map, stat: LoaderEvent) { + if (map.has(stat.detail)) { + // console.warn('BAD events, DOUBLE start', stat); + // map.delete(stat.detail); + return; + } + map.set(stat.detail, -stat.timestamp); + } + + function diff(map: Map, stat: LoaderEvent) { + let duration = map.get(stat.detail); + if (!duration) { + // console.warn('BAD events, end WITHOUT start', stat); + // map.delete(stat.detail); + return; + } + if (duration >= 0) { + // console.warn('BAD events, DOUBLE end', stat); + // map.delete(stat.detail); + return; + } + map.set(stat.detail, duration + stat.timestamp); + } + + const stats = mergeSort(require.getStats().slice(0), (a, b) => a.timestamp - b.timestamp); + + for (const stat of stats) { + switch (stat.type) { + case LoaderEventType.BeginLoadingScript: + mark(amdLoadScript, stat); + break; + case LoaderEventType.EndLoadingScriptOK: + case LoaderEventType.EndLoadingScriptError: + diff(amdLoadScript, stat); + break; + + case LoaderEventType.BeginInvokeFactory: + mark(amdInvokeFactory, stat); + break; + case LoaderEventType.EndInvokeFactory: + diff(amdInvokeFactory, stat); + break; + + case LoaderEventType.NodeBeginNativeRequire: + mark(nodeRequire, stat); + break; + case LoaderEventType.NodeEndNativeRequire: + diff(nodeRequire, stat); + break; + + case LoaderEventType.NodeBeginEvaluatingScript: + mark(nodeEval, stat); + break; + case LoaderEventType.NodeEndEvaluatingScript: + diff(nodeEval, stat); + break; + } + } + + let nodeRequireTotal = 0; + nodeRequire.forEach(value => nodeRequireTotal += value); + + function to2dArray(map: Map): [string, number][] { + let res: [string, number][] = []; + map.forEach((value, index) => res.push([index, value])); + return res; + } + + return { + amdLoad: to2dArray(amdLoadScript), + amdInvoke: to2dArray(amdInvokeFactory), + nodeRequire: to2dArray(nodeRequire), + nodeEval: to2dArray(nodeEval), + nodeRequireTotal + }; + } + + static toMarkdownTable(header: string[], rows: Array>): string { + let result = ''; + + let lengths: number[] = []; + header.forEach((cell, ci) => { + lengths[ci] = cell.length; + }); + rows.forEach(row => { + row.forEach((cell, ci) => { + if (typeof cell === 'undefined') { + cell = row[ci] = '-'; + } + const len = cell.toString().length; + lengths[ci] = Math.max(len, lengths[ci]); + }); + }); + + // header + header.forEach((cell, ci) => { result += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; }); + result += '|\n'; + header.forEach((_cell, ci) => { result += `| ${'-'.repeat(lengths[ci])} `; }); + result += '|\n'; + + // cells + rows.forEach(row => { + row.forEach((cell, ci) => { + if (typeof cell !== 'undefined') { + result += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; + } + }); + result += '|\n'; + }); + + return result; + } +} diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index bc0aa40a..c4820d9c 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -588,3 +588,17 @@ export function asArray(x: T | T[]): T[] { export function getRandomElement(arr: T[]): T | undefined { return arr[Math.floor(Math.random() * arr.length)]; } + +/** + * Returns the first mapped value of the array which is not undefined. + */ +export function mapFind(array: Iterable, mapFn: (value: T) => R | undefined): R | undefined { + for (const value of array) { + const mapped = mapFn(value); + if (mapped !== undefined) { + return mapped; + } + } + + return undefined; +} diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 632c1989..167a47d1 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -445,6 +445,45 @@ export function first(promiseFactories: ITask>[], shouldStop: (t: return loop(); } +/** + * Returns the result of the first promise that matches the "shouldStop", + * running all promises in parallel. Supports cancelable promises. + */ +export function firstParallel(promiseList: Promise[], shouldStop?: (t: T) => boolean, defaultValue?: T | null): Promise; +export function firstParallel(promiseList: Promise[], shouldStop: (t: T) => t is R, defaultValue?: R | null): Promise; +export function firstParallel(promiseList: Promise[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null) { + if (promiseList.length === 0) { + return Promise.resolve(defaultValue); + } + + let todo = promiseList.length; + const finish = () => { + todo = -1; + for (const promise of promiseList) { + (promise as Partial>).cancel?.(); + } + }; + + return new Promise((resolve, reject) => { + for (const promise of promiseList) { + promise.then(result => { + if (--todo >= 0 && shouldStop(result)) { + finish(); + resolve(result); + } else if (todo === 0) { + resolve(defaultValue); + } + }) + .catch(err => { + if (--todo >= 0) { + finish(); + reject(err); + } + }); + } + }); +} + interface ILimitedTaskFactory { factory: ITask>; c: (value: T | Promise) => void; diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 3c4fffd3..8681df32 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -5,6 +5,7 @@ import { codiconStartMarker } from 'vs/base/common/codicon'; import { Emitter, Event } from 'vs/base/common/event'; +import { localize } from 'vs/nls'; export interface IIconRegistry { readonly all: IterableIterator; @@ -18,9 +19,12 @@ class Registry implements IIconRegistry { private readonly _onDidRegister = new Emitter(); public add(icon: Codicon) { - if (!this._icons.has(icon.id)) { + const existing = this._icons.get(icon.id); + if (!existing) { this._icons.set(icon.id, icon); this._onDidRegister.fire(icon); + } else if (icon.description) { + existing.description = icon.description; } else { console.error(`Duplicate registration of codicon ${icon.id}`); } @@ -43,11 +47,11 @@ const _registry = new Registry(); export const iconRegistry: IIconRegistry = _registry; -export function registerIcon(id: string, def: Codicon, description?: string) { - return new Codicon(id, def); +export function registerCodicon(id: string, def: Codicon, description?: string): Codicon { + return new Codicon(id, def, description); } -export class Codicon { +export class Codicon implements CSSIcon { constructor(public readonly id: string, public readonly definition: Codicon | IconDefinition, public description?: string) { _registry.add(this); } @@ -57,6 +61,12 @@ export class Codicon { public get cssSelector() { return '.codicon.codicon-' + this.id; } } +export interface CSSIcon { + readonly classNames: string; +} + + + interface IconDefinition { character: string; } @@ -484,8 +494,15 @@ export namespace Codicon { export const redo = new Codicon('redo', { character: '\\ebb0' }); export const checkAll = new Codicon('check-all', { character: '\\ebb1' }); export const pinnedDirty = new Codicon('pinned-dirty', { character: '\\ebb2' }); + export const passFilled = new Codicon('pass-filled', { character: '\\ebb3' }); + export const circleLargeFilled = new Codicon('circle-large-filled', { character: '\\ebb4' }); + export const circleLargeOutline = new Codicon('circle-large-outline', { character: '\\ebb5' }); + + export const dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition, localize('dropDownButton', 'Icon for drop down buttons.')); } +// common icons + diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index 8729b730..f97dc23f 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -76,7 +76,37 @@ export function fromMap(original: Map): IStringDictionary { return result; } +export function diffSets(before: Set, after: Set): { removed: T[], added: T[] } { + const removed: T[] = []; + const added: T[] = []; + for (let element of before) { + if (!after.has(element)) { + removed.push(element); + } + } + for (let element of after) { + if (!before.has(element)) { + added.push(element); + } + } + return { removed, added }; +} +export function diffMaps(before: Map, after: Map): { removed: V[], added: V[] } { + const removed: V[] = []; + const added: V[] = []; + for (let [index, value] of before) { + if (!after.has(index)) { + removed.push(value); + } + } + for (let [index, value] of after) { + if (!before.has(index)) { + added.push(value); + } + } + return { removed, added }; +} export class SetMap { private map = new Map>(); diff --git a/src/vs/base/common/diff/diff.ts b/src/vs/base/common/diff/diff.ts index d81c79ea..97fdcbc0 100644 --- a/src/vs/base/common/diff/diff.ts +++ b/src/vs/base/common/diff/diff.ts @@ -880,9 +880,81 @@ export class LcsDiff { change.modifiedStart -= bestDelta; } + // There could be multiple longest common substrings. + // Give preference to the ones containing longer lines + if (this._hasStrings) { + for (let i = 1, len = changes.length; i < len; i++) { + const aChange = changes[i - 1]; + const bChange = changes[i]; + const matchedLength = bChange.originalStart - aChange.originalStart - aChange.originalLength; + const aOriginalStart = aChange.originalStart; + const bOriginalEnd = bChange.originalStart + bChange.originalLength; + const abOriginalLength = bOriginalEnd - aOriginalStart; + const aModifiedStart = aChange.modifiedStart; + const bModifiedEnd = bChange.modifiedStart + bChange.modifiedLength; + const abModifiedLength = bModifiedEnd - aModifiedStart; + // Avoid wasting a lot of time with these searches + if (matchedLength < 5 && abOriginalLength < 20 && abModifiedLength < 20) { + const t = this._findBetterContiguousSequence( + aOriginalStart, abOriginalLength, + aModifiedStart, abModifiedLength, + matchedLength + ); + if (t) { + const [originalMatchStart, modifiedMatchStart] = t; + if (originalMatchStart !== aChange.originalStart + aChange.originalLength || modifiedMatchStart !== aChange.modifiedStart + aChange.modifiedLength) { + // switch to another sequence that has a better score + aChange.originalLength = originalMatchStart - aChange.originalStart; + aChange.modifiedLength = modifiedMatchStart - aChange.modifiedStart; + bChange.originalStart = originalMatchStart + matchedLength; + bChange.modifiedStart = modifiedMatchStart + matchedLength; + bChange.originalLength = bOriginalEnd - bChange.originalStart; + bChange.modifiedLength = bModifiedEnd - bChange.modifiedStart; + } + } + } + } + } + return changes; } + private _findBetterContiguousSequence(originalStart: number, originalLength: number, modifiedStart: number, modifiedLength: number, desiredLength: number): [number, number] | null { + if (originalLength < desiredLength || modifiedLength < desiredLength) { + return null; + } + const originalMax = originalStart + originalLength - desiredLength + 1; + const modifiedMax = modifiedStart + modifiedLength - desiredLength + 1; + let bestScore = 0; + let bestOriginalStart = 0; + let bestModifiedStart = 0; + for (let i = originalStart; i < originalMax; i++) { + for (let j = modifiedStart; j < modifiedMax; j++) { + const score = this._contiguousSequenceScore(i, j, desiredLength); + if (score > 0 && score > bestScore) { + bestScore = score; + bestOriginalStart = i; + bestModifiedStart = j; + } + } + } + if (bestScore > 0) { + return [bestOriginalStart, bestModifiedStart]; + } + return null; + } + + private _contiguousSequenceScore(originalStart: number, modifiedStart: number, length: number): number { + let score = 0; + for (let l = 0; l < length; l++) { + if (!this.ElementsAreEqual(originalStart + l, modifiedStart + l)) { + return 0; + } + score += this._originalStringElements[originalStart + l].length; + } + return score; + } + private _OriginalIsBoundary(index: number): boolean { if (index <= 0 || index >= this._originalElementsOrHash.length - 1) { return true; diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index f70aa9d8..6a69c0e6 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -629,7 +629,7 @@ export class PauseableEmitter extends Emitter { if (this._mergeFn) { // use the merge function to create a single composite // event. make a copy in case firing pauses this emitter - const events = this._eventQueue.toArray(); + const events = Array.from(this._eventQueue); this._eventQueue.clear(); super.fire(this._mergeFn(events)); diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 13a1847e..b39581a8 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -106,8 +106,14 @@ function leftPad(value: string, length: number, char: string = '0'): string { return value; } -function toHexString(value: number, bitsize: number = 32): string { - return leftPad((value >>> 0).toString(16), bitsize / 4); +export function toHexString(buffer: ArrayBuffer): string; +export function toHexString(value: number, bitsize?: number): string; +export function toHexString(bufferOrValue: ArrayBuffer | number, bitsize: number = 32): string { + if (bufferOrValue instanceof ArrayBuffer) { + return Array.from(new Uint8Array(bufferOrValue)).map(b => b.toString(16).padStart(2, '0')).join(''); + } + + return leftPad((bufferOrValue >>> 0).toString(16), bitsize / 4); } /** diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 32f463ff..829a3480 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -21,52 +21,52 @@ export const enum MarkdownStringTextNewlineStyle { } export class MarkdownString implements IMarkdownString { - private readonly _isTrusted: boolean; - private readonly _supportThemeIcons: boolean; + + public value: string; + public isTrusted?: boolean; + public supportThemeIcons?: boolean; constructor( - private _value: string = '', + value: string = '', isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, ) { - if (typeof this._value !== 'string') { + this.value = value; + if (typeof this.value !== 'string') { throw illegalArgument('value'); } if (typeof isTrustedOrOptions === 'boolean') { - this._isTrusted = isTrustedOrOptions; - this._supportThemeIcons = false; + this.isTrusted = isTrustedOrOptions; + this.supportThemeIcons = false; } else { - this._isTrusted = isTrustedOrOptions.isTrusted ?? false; - this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; + this.isTrusted = isTrustedOrOptions.isTrusted ?? undefined; + this.supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; } } - get value() { return this._value; } - get isTrusted() { return this._isTrusted; } - get supportThemeIcons() { return this._supportThemeIcons; } - appendText(value: string, newlineStyle: MarkdownStringTextNewlineStyle = MarkdownStringTextNewlineStyle.Paragraph): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this._value += (this._supportThemeIcons ? escapeCodicons(value) : value) + this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') + .replace(/([ \t]+)/g, (_match, g1) => ' '.repeat(g1.length)) + .replace(/^>/gm, '\\>') .replace(/\n/g, newlineStyle === MarkdownStringTextNewlineStyle.Break ? '\\\n' : '\n\n'); return this; } appendMarkdown(value: string): MarkdownString { - this._value += value; - + this.value += value; return this; } appendCodeblock(langId: string, code: string): MarkdownString { - this._value += '\n```'; - this._value += langId; - this._value += '\n'; - this._value += code; - this._value += '\n```\n'; + this.value += '\n```'; + this.value += langId; + this.value += '\n'; + this.value += code; + this.value += '\n```\n'; return this; } } diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 933e370f..8b464edb 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -126,7 +126,7 @@ export interface Location { /** * Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices). * '*' will match a single segment, of any property name or index. - * '**' will match a sequece of segments or no segment, of any property name or index. + * '**' will match a sequence of segments or no segment, of any property name or index. */ matches: (patterns: JSONPath) => boolean; /** diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 5f3fab86..5c1aac3b 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -95,6 +95,10 @@ function hasDriveLetter(path: string): boolean { return !!(isWindows && path && path[1] === ':'); } +export function extractDriveLetter(path: string): string | undefined { + return hasDriveLetter(path) ? path[0] : undefined; +} + export function normalizeDriveLetter(path: string): string { if (hasDriveLetter(path)) { return path.charAt(0).toUpperCase() + path.slice(1); diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index 8ca17bc7..ddeb494c 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -131,12 +131,4 @@ export class LinkedList { node = node.next; } } - - toArray(): E[] { - const result: E[] = []; - for (let node = this._first; node !== Node.Undefined; node = node.next) { - result.push(node.element); - } - return result; - } } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index deaf5695..0e08876e 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -6,8 +6,6 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; import { compareSubstringIgnoreCase, compare, compareSubstring, compareIgnoreCase } from 'vs/base/common/strings'; -import { isLinux } from 'vs/base/common/platform'; -import { Schemas } from 'vs/base/common/network'; export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); @@ -77,6 +75,57 @@ export class StringIterator implements IKeyIterator { } } +export class ConfigKeysIterator implements IKeyIterator { + + private _value!: string; + private _from!: number; + private _to!: number; + + constructor( + private readonly _caseSensitive: boolean = true + ) { } + + reset(key: string): this { + this._value = key; + this._from = 0; + this._to = 0; + return this.next(); + } + + hasNext(): boolean { + return this._to < this._value.length; + } + + next(): this { + // this._data = key.split(/[\\/]/).filter(s => !!s); + this._from = this._to; + let justSeps = true; + for (; this._to < this._value.length; this._to++) { + const ch = this._value.charCodeAt(this._to); + if (ch === CharCode.Period) { + if (justSeps) { + this._from++; + } else { + break; + } + } else { + justSeps = false; + } + } + return this; + } + + cmp(a: string): number { + return this._caseSensitive + ? compareSubstring(a, this._value, 0, a.length, this._from, this._to) + : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to); + } + + value(): string { + return this._value.substring(this._from, this._to); + } +} + export class PathIterator implements IKeyIterator { private _value!: string; @@ -140,7 +189,7 @@ export class UriIterator implements IKeyIterator { private _states: UriIteratorState[] = []; private _stateIdx: number = 0; - constructor(private readonly _ignorePathCasing: boolean | undefined) { } + constructor(private readonly _ignorePathCasing: (uri: URI) => boolean) { } reset(key: URI): this { this._value = key; @@ -152,10 +201,7 @@ export class UriIterator implements IKeyIterator { this._states.push(UriIteratorState.Authority); } if (this._value.path) { - this._pathIterator = new PathIterator(false, this._ignorePathCasing === undefined - ? key.scheme === Schemas.file && isLinux - : !this._ignorePathCasing - ); + this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key)); this._pathIterator.reset(key.path); if (this._pathIterator.value()) { this._states.push(UriIteratorState.Path); @@ -231,7 +277,7 @@ class TernarySearchTreeNode { export class TernarySearchTree { - static forUris(ignorePathCasing?: boolean): TernarySearchTree { + static forUris(ignorePathCasing: (key: URI) => boolean = () => false): TernarySearchTree { return new TernarySearchTree(new UriIterator(ignorePathCasing)); } @@ -243,6 +289,10 @@ export class TernarySearchTree { return new TernarySearchTree(new StringIterator()); } + static forConfigKeys(): TernarySearchTree { + return new TernarySearchTree(new ConfigKeysIterator()); + } + private _iter: IKeyIterator; private _root: TernarySearchTreeNode | undefined; @@ -301,6 +351,10 @@ export class TernarySearchTree { } get(key: K): V | undefined { + return this._getNode(key)?.value; + } + + private _getNode(key: K) { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -319,7 +373,12 @@ export class TernarySearchTree { break; } } - return node ? node.value : undefined; + return node; + } + + has(key: K): boolean { + const node = this._getNode(key); + return !(node?.value === undefined && node?.mid === undefined); } delete(key: K): void { @@ -352,11 +411,18 @@ export class TernarySearchTree { stack.push([0, node]); node = node.mid; } else { - // remove element - node.value = undefined; + if (superStr) { + // remove children + node.left = undefined; + node.mid = undefined; + node.right = undefined; + } else { + // remove element + node.value = undefined; + } // clean up empty nodes - while (stack.length > 0 && (node.isEmpty() || superStr)) { + while (stack.length > 0 && node.isEmpty()) { let [dir, parent] = stack.pop()!; switch (dir) { case 1: parent.left = undefined; break; @@ -394,7 +460,7 @@ export class TernarySearchTree { return node && node.value || candidate; } - findSuperstr(key: K): Iterator | undefined { + findSuperstr(key: K): IterableIterator<[K, V]> | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -414,7 +480,7 @@ export class TernarySearchTree { if (!node.mid) { return undefined; } else { - return this._values(node.mid); + return this._entries(node.mid); } } } @@ -431,12 +497,6 @@ export class TernarySearchTree { yield* this._entries(this._root); } - private *_values(node: TernarySearchTreeNode): IterableIterator { - for (const [, value] of this._entries(node)) { - yield value; - } - } - private *_entries(node: TernarySearchTreeNode | undefined): IterableIterator<[K, V]> { if (node) { // left diff --git a/src/vs/base/common/marked/cgmanifest.json b/src/vs/base/common/marked/cgmanifest.json index f3477931..15e641cb 100644 --- a/src/vs/base/common/marked/cgmanifest.json +++ b/src/vs/base/common/marked/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "marked", "repositoryUrl": "https://github.com/markedjs/marked", - "commitHash": "529a8d4e185a8aa561e4d8d2891f8556b5717cd4" + "commitHash": "8cfa29ccd2a2759e8e60fe0d8d6df8c022beda4e" } }, "license": "MIT", - "version": "0.6.2" + "version": "1.1.0" } ], "version": 1 diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index fb133b90..ce8d1641 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -12,7 +12,7 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (global = global || self, global.marked = factory()); + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.marked = factory()); }(this, (function () { 'use strict'; function _defineProperties(target, props) { @@ -368,11 +368,28 @@ function checkSanitizeDeprecation(opt) { if (opt && opt.sanitize && !opt.silent) { - // VS CODE CHANGE - // Disable logging about sanitize options. We already use insane after running the sanitizer - - // console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); + console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); } + } // copied from https://stackoverflow.com/a/5450113/806777 + + + function repeatString(pattern, count) { + if (count < 1) { + return ''; + } + + var result = ''; + + while (count > 1) { + if (count & 1) { + result += pattern; + } + + count >>= 1; + pattern += pattern; + } + + return result + pattern; } var helpers = { @@ -386,7 +403,8 @@ splitCells: splitCells, rtrim: rtrim, findClosingBracket: findClosingBracket, - checkSanitizeDeprecation: checkSanitizeDeprecation + checkSanitizeDeprecation: checkSanitizeDeprecation, + repeatString: repeatString }; var defaults$1 = defaults.defaults; @@ -620,7 +638,7 @@ // so it is seen as the next token. space = item.length; - item = item.replace(/^ *([*+-]|\d+[.)]) */, ''); // Outdent whatever the + item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the // list item contains. Hacky. if (~item.indexOf('\n ')) { @@ -1138,11 +1156,11 @@ block.gfm = merge$1({}, block.normal, { nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header - + ' *([-:]+ *\\|[-| :]*)' // Align + + ' {0,3}([-:]+ *\\|[-| :]*)' // Align + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells table: '^ *\\|(.+)\\n' // Header - + ' *\\|?( *[-:]+[-| :]*)' // Align + + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells }); @@ -1187,24 +1205,24 @@ start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/, // (1) returns if starts w/ punctuation middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/, - endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation\s]|$))/, + endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/, // last char can't be punct, or final * must also be followed by punct (or endline) - endUnd: /[^\s]__(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) + endUnd: /[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) }, em: { start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/, // (1) returns if starts w/ punctuation middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/, - endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation\s]|$))/, + endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/, // last char can't be punct, or final * must also be followed by punct (or endline) - endUnd: /[^\s]_(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) + endUnd: /[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline) }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, del: noopTest$1, - text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\ 0) { while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); } } } @@ -1660,7 +1688,7 @@ while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { - maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); } while (src) { @@ -2073,6 +2101,15 @@ var parser = new Parser(options); return parser.parse(tokens); } + /** + * Static Parse Inline Method + */ + ; + + Parser.parseInline = function parseInline(tokens, options) { + var parser = new Parser(options); + return parser.parseInline(tokens); + } /** * Parse Loop */ @@ -2606,6 +2643,42 @@ } } }; + /** + * Parse Inline + */ + + + marked.parseInline = function (src, opt) { + // throw error in case of non string input + if (typeof src === 'undefined' || src === null) { + throw new Error('marked.parseInline(): input parameter is undefined or null'); + } + + if (typeof src !== 'string') { + throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); + } + + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); + + try { + var tokens = Lexer_1.lexInline(src, opt); + + if (opt.walkTokens) { + marked.walkTokens(tokens, opt.walkTokens); + } + + return Parser_1.parseInline(tokens, opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/markedjs/marked.'; + + if (opt.silent) { + return '

An error occurred:

' + escape$2(e.message + '', true) + '
'; + } + + throw e; + } + }; /** * Expose */ diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index f475b10e..4e12b577 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -78,6 +78,12 @@ export namespace Schemas { * Scheme used for extension pages */ export const extension = 'extension'; + + /** + * Scheme used as a replacement of `file` scheme to load + * files with our custom protocol handler (desktop only). + */ + export const vscodeFileResource = 'vscode-file'; } class RemoteAuthoritiesImpl { @@ -132,6 +138,8 @@ export const RemoteAuthorities = new RemoteAuthoritiesImpl(); class FileAccessImpl { + private readonly FALLBACK_AUTHORITY = 'vscode-app'; + /** * Returns a URI to use in contexts where the browser is responsible * for loading (e.g. fetch()) or when used within the DOM. @@ -143,13 +151,43 @@ class FileAccessImpl { asBrowserUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { const uri = this.toUri(uriOrModule, moduleIdToUrl); + // Handle remote URIs via `RemoteAuthorities` if (uri.scheme === Schemas.vscodeRemote) { return RemoteAuthorities.rewrite(uri); } + // Only convert the URI if we are in a native context and it has `file:` scheme + if (platform.isElectronSandboxed && platform.isNative && uri.scheme === Schemas.file) { + return this.toCodeFileUri(uri); + } + return uri; } + /** + * TODO@bpasero remove me eventually when vscode-file is adopted everywhere + */ + _asCodeFileUri(uri: URI): URI; + _asCodeFileUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI; + _asCodeFileUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + const uri = this.toUri(uriOrModule, moduleIdToUrl); + + return this.toCodeFileUri(uri); + } + + private toCodeFileUri(uri: URI): URI { + return uri.with({ + scheme: Schemas.vscodeFileResource, + // We need to provide an authority here so that it can serve + // as origin for network and loading matters in chromium. + // If the URI is not coming with an authority already, we + // add our own + authority: uri.authority || this.FALLBACK_AUTHORITY, + query: null, + fragment: null + }); + } + /** * Returns the `file` URI to use in contexts where node.js * is responsible for loading. @@ -159,6 +197,19 @@ class FileAccessImpl { asFileUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { const uri = this.toUri(uriOrModule, moduleIdToUrl); + // Only convert the URI if it is `vscode-file:` scheme + if (uri.scheme === Schemas.vscodeFileResource) { + return uri.with({ + scheme: Schemas.file, + // Only preserve the `authority` if it is different from + // our fallback authority. This ensures we properly preserve + // Windows UNC paths that come with their own authority. + authority: uri.authority !== this.FALLBACK_AUTHORITY ? uri.authority : null, + query: null, + fragment: null + }); + } + return uri; } diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index 5893db44..b10f9b1d 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -12,7 +12,7 @@ function _factory(sharedObj) { sharedObj.MonacoPerformanceMarks = sharedObj.MonacoPerformanceMarks || []; const _dataLen = 2; - const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; + const _nativeMark = typeof performance === 'object' && typeof performance.mark === 'function' ? performance.mark.bind(performance) : () => { }; function importEntries(entries) { sharedObj.MonacoPerformanceMarks.splice(0, 0, ...entries); @@ -55,7 +55,7 @@ function _factory(sharedObj) { function mark(name) { sharedObj.MonacoPerformanceMarks.push(name, Date.now()); - _timeStamp(name); + _nativeMark(name); } const exports = { diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 3361d83b..61fa4d71 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -33,8 +33,8 @@ export interface INodeProcess { versions?: { electron?: string; }; + sandboxed?: boolean; // Electron type?: string; - getuid(): number; cwd(): string; } declare const process: INodeProcess; @@ -55,11 +55,12 @@ if (typeof process !== 'undefined') { // Native environment (non-sandboxed) nodeProcess = process; } else if (typeof _globals.vscode !== 'undefined') { - // Native envionment (sandboxed) + // Native environment (sandboxed) nodeProcess = _globals.vscode.process; } const isElectronRenderer = typeof nodeProcess?.versions?.electron === 'string' && nodeProcess.type === 'renderer'; +export const isElectronSandboxed = isElectronRenderer && nodeProcess?.sandboxed; // Web environment if (typeof navigator === 'object' && !isElectronRenderer) { @@ -133,10 +134,6 @@ export const isIOS = _isIOS; export const platform = _platform; export const userAgent = _userAgent; -export function isRootUser(): boolean { - return !!nodeProcess && !_isWindows && (nodeProcess.getuid() === 0); -} - /** * The language used for the user interface. The format of * the string is all lower case (e.g. zh-tw for Traditional diff --git a/src/vs/base/common/process.ts b/src/vs/base/common/process.ts index 5e85d672..4b9dde87 100644 --- a/src/vs/base/common/process.ts +++ b/src/vs/base/common/process.ts @@ -16,7 +16,16 @@ if (typeof process !== 'undefined') { // Native sandbox environment else if (typeof globals.vscode !== 'undefined') { - safeProcess = globals.vscode.process; + safeProcess = { + + // Supported + get platform(): 'win32' | 'linux' | 'darwin' { return globals.vscode.process.platform; }, + get env() { return globals.vscode.process.env; }, + nextTick(callback: (...args: any[]) => void): void { return setImmediate(callback); }, + + // Unsupported + cwd(): string { return globals.vscode.process.env['VSCODE_CWD'] || globals.vscode.process.execPath.substr(0, globals.vscode.process.execPath.lastIndexOf(globals.vscode.process.platform === 'win32' ? '\\' : '/')); } + }; } // Web environment @@ -29,8 +38,7 @@ else { // Unsupported get env() { return Object.create(null); }, - cwd(): string { return '/'; }, - getuid(): number { return -1; } + cwd(): string { return '/'; } }; } diff --git a/src/vs/base/common/resourceTree.ts b/src/vs/base/common/resourceTree.ts index 23f661e1..96cdee54 100644 --- a/src/vs/base/common/resourceTree.ts +++ b/src/vs/base/common/resourceTree.ts @@ -5,7 +5,7 @@ import { memoize } from 'vs/base/common/decorators'; import * as paths from 'vs/base/common/path'; -import { relativePath, joinPath } from 'vs/base/common/resources'; +import { IExtUri, extUri as defaultExtUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { PathIterator } from 'vs/base/common/map'; @@ -95,12 +95,12 @@ export class ResourceTree, C> { return obj instanceof Node; } - constructor(context: C, rootURI: URI = URI.file('/')) { + constructor(context: C, rootURI: URI = URI.file('/'), private extUri: IExtUri = defaultExtUri) { this.root = new Node(rootURI, '', context); } add(uri: URI, element: T): void { - const key = relativePath(this.root.uri, uri) || uri.fsPath; + const key = this.extUri.relativePath(this.root.uri, uri) || uri.path; const iterator = new PathIterator(false).reset(key); let node = this.root; let path = ''; @@ -113,7 +113,7 @@ export class ResourceTree, C> { if (!child) { child = new Node( - joinPath(this.root.uri, path), + this.extUri.joinPath(this.root.uri, path), path, this.root.context, iterator.hasNext() ? undefined : element, @@ -136,7 +136,7 @@ export class ResourceTree, C> { } delete(uri: URI): T | undefined { - const key = relativePath(this.root.uri, uri) || uri.fsPath; + const key = this.extUri.relativePath(this.root.uri, uri) || uri.path; const iterator = new PathIterator(false).reset(key); return this._delete(this.root, iterator); } @@ -168,7 +168,7 @@ export class ResourceTree, C> { } getNode(uri: URI): IResourceNode | undefined { - const key = relativePath(this.root.uri, uri) || uri.fsPath; + const key = this.extUri.relativePath(this.root.uri, uri) || uri.path; const iterator = new PathIterator(false).reset(key); let node = this.root; diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 621c9736..82619c4b 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -10,8 +10,6 @@ import { equalsIgnoreCase, compare as strCompare } from 'vs/base/common/strings' import { Schemas } from 'vs/base/common/network'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; -import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; -import { TernarySearchTree } from 'vs/base/common/map'; export function originalFSPath(uri: URI): string { return uriToFsPath(uri, true); @@ -58,6 +56,11 @@ export interface IExtUri { */ getComparisonKey(uri: URI, ignoreFragment?: boolean): string; + /** + * Whether the casing of the path-component of the uri should be ignored. + */ + ignorePathCasing(uri: URI): boolean; + // --- path math basenameOrAuthority(resource: URI): string; @@ -161,6 +164,10 @@ export class ExtUri implements IExtUri { }).toString(); } + ignorePathCasing(uri: URI): boolean { + return this._ignorePathCasing(uri); + } + isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean { if (base.scheme === parentCandidate.scheme) { if (base.scheme === Schemas.file) { @@ -427,33 +434,6 @@ export namespace DataUri { } } -export class ResourceGlobMatcher { - - private readonly globalExpression: ParsedExpression; - private readonly expressionsByRoot: TernarySearchTree = TernarySearchTree.forUris<{ root: URI, expression: ParsedExpression }>(); - - constructor( - globalExpression: IExpression, - rootExpressions: { root: URI, expression: IExpression }[] - ) { - this.globalExpression = parse(globalExpression); - for (const expression of rootExpressions) { - this.expressionsByRoot.set(expression.root, { root: expression.root, expression: parse(expression.expression) }); - } - } - - matches(resource: URI): boolean { - const rootExpression = this.expressionsByRoot.findSubstr(resource); - if (rootExpression) { - const path = relativePath(rootExpression.root, resource); - if (path && !!rootExpression.expression(path)) { - return true; - } - } - return !!this.globalExpression(resource.path); - } -} - export function toLocalResource(resource: URI, authority: string | undefined, localScheme: string): URI { if (authority) { let path = resource.path; diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 9e35fd6c..84e9c52f 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -69,6 +69,14 @@ export function count(value: string, character: string): number { return result; } +export function truncate(value: string, maxLength: number, suffix = 'â€Ļ'): string { + if (value.length <= maxLength) { + return value; + } + + return `${value.substr(0, maxLength)}${suffix}`; +} + /** * Removes all occurrences of needle from the beginning and end of haystack. * @param haystack string to trim @@ -208,6 +216,10 @@ export function regExpFlags(regexp: RegExp): string { + ((regexp as any /* standalone editor compilation */).unicode ? 'u' : ''); } +export function splitLines(str: string): string[] { + return str.split(/\r\n|\r|\n/); +} + /** * Returns first index of the string that is not whitespace. * If string is empty or contains only whitespaces, returns -1 @@ -879,9 +891,15 @@ export function getNLines(str: string, n = 1): string { n--; } while (n > 0 && idx >= 0); - return idx >= 0 ? - str.substr(0, idx) : - str; + if (idx === -1) { + return str; + } + + if (str[idx - 1] === '\r') { + idx--; + } + + return str.substr(0, idx); } /** diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 5ac44dc8..8111602e 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -345,7 +345,7 @@ export class URI implements UriComponents { */ static joinPath(uri: URI, ...pathFragment: string[]): URI { if (!uri.path) { - throw new Error(`[UriError]: cannot call joinPaths on URI without path`); + throw new Error(`[UriError]: cannot call joinPath on URI without path`); } let newPath: string; if (isWindows && uri.scheme === 'file') { diff --git a/src/vs/base/common/uuid.ts b/src/vs/base/common/uuid.ts index 57d9db69..141fd83c 100644 --- a/src/vs/base/common/uuid.ts +++ b/src/vs/base/common/uuid.ts @@ -17,8 +17,9 @@ for (let i = 0; i < 256; i++) { _hex.push(i.toString(16).padStart(2, '0')); } -// todo@joh node nodejs use `crypto#randomBytes`, see: https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randombytes_size_callback -// todo@joh use browser-crypto +// todo@jrieken +// 1. node nodejs use`crypto#randomBytes`, see: https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randombytes_size_callback +// 2. use browser-crypto const _fillRandomValues = function (bucket: Uint8Array): Uint8Array { for (let i = 0; i < bucket.length; i++) { bucket[i] = Math.floor(Math.random() * 256); diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 19f154a7..1b81394a 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -358,6 +358,10 @@ export class SimpleWorkerServer { delete loaderConfig.paths['vs']; } } + if (typeof loaderConfig.trustedTypesPolicy !== undefined) { + // don't use, it has been destroyed during serialize + delete loaderConfig['trustedTypesPolicy']; + } // Since this is in a web worker, enable catching errors loaderConfig.catchError = true; diff --git a/src/vs/base/node/paths.ts b/src/vs/base/node/paths.ts index 977eaf88..eaf03e6e 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/node/paths.ts @@ -5,12 +5,7 @@ import { FileAccess } from 'vs/base/common/network'; -interface IPaths { - getAppDataPath(platform: string): string; - getDefaultUserDataPath(platform: string): string; -} - const pathsPath = FileAccess.asFileUri('paths', require).fsPath; -const paths = require.__$__nodeRequire(pathsPath); -export const getAppDataPath = paths.getAppDataPath; +const paths = require.__$__nodeRequire<{ getDefaultUserDataPath(): string }>(pathsPath); + export const getDefaultUserDataPath = paths.getDefaultUserDataPath; diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index bc8e97c2..b27c8734 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -126,7 +126,8 @@ const enum ProtocolMessageType { Control = 2, Ack = 3, KeepAlive = 4, - Disconnect = 5 + Disconnect = 5, + ReplayRequest = 6 } export const enum ProtocolConstants { @@ -274,7 +275,11 @@ class ProtocolWriter { } public dispose(): void { - this.flush(); + try { + this.flush(); + } catch (err) { + // ignore error, since the socket could be already closed + } this._isDisposed = true; } @@ -601,6 +606,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { private _outgoingKeepAliveTimeout: any | null; private _incomingKeepAliveTimeout: any | null; + private _lastReplayRequestTime: number; + private _socket: ISocket; private _socketWriter: ProtocolWriter; private _socketReader: ProtocolReader; @@ -642,6 +649,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._outgoingKeepAliveTimeout = null; this._incomingKeepAliveTimeout = null; + this._lastReplayRequestTime = 0; + this._socketDisposables = []; this._socket = socket; this._socketWriter = new ProtocolWriter(this._socket); @@ -747,6 +756,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._onSocketTimeout.flushBuffer(); this._socket.dispose(); + this._lastReplayRequestTime = 0; + this._socket = socket; this._socketWriter = new ProtocolWriter(this._socket); this._socketDisposables.push(this._socketWriter); @@ -792,17 +803,31 @@ export class PersistentProtocol implements IMessagePassingProtocol { if (msg.type === ProtocolMessageType.Regular) { if (msg.id > this._incomingMsgId) { if (msg.id !== this._incomingMsgId + 1) { - console.error(`PROTOCOL CORRUPTION, LAST SAW MSG ${this._incomingMsgId} AND HAVE NOW RECEIVED MSG ${msg.id}`); + // in case we missed some messages we ask the other party to resend them + const now = Date.now(); + if (now - this._lastReplayRequestTime > 10000) { + // send a replay request at most once every 10s + this._lastReplayRequestTime = now; + this._socketWriter.write(new ProtocolMessage(ProtocolMessageType.ReplayRequest, 0, 0, getEmptyBuffer())); + } + } else { + this._incomingMsgId = msg.id; + this._incomingMsgLastTime = Date.now(); + this._sendAckCheck(); + this._onMessage.fire(msg.data); } - this._incomingMsgId = msg.id; - this._incomingMsgLastTime = Date.now(); - this._sendAckCheck(); - this._onMessage.fire(msg.data); } } else if (msg.type === ProtocolMessageType.Control) { this._onControlMessage.fire(msg.data); } else if (msg.type === ProtocolMessageType.Disconnect) { this._onClose.fire(); + } else if (msg.type === ProtocolMessageType.ReplayRequest) { + // Send again all unacknowledged messages + const toSend = this._outgoingUnackMsg.toArray(); + for (let i = 0, len = toSend.length; i < len; i++) { + this._socketWriter.write(toSend[i]); + } + this._recvAckCheck(); } } diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index 29a3f02b..4489a73a 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -130,6 +130,11 @@ padding: 5px 5px 2px 5px; } +.quick-input-message > .codicon { + margin: 0 0.2em; + vertical-align: text-bottom; +} + .quick-input-progress.monaco-progress-container { position: relative; } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 0a4c9f1b..bddbd8a6 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -27,8 +27,10 @@ import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/lis import { List, IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox'; import { Color } from 'vs/base/common/color'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { registerCodicon, Codicon } from 'vs/base/common/codicons'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { escape } from 'vs/base/common/strings'; +import { renderCodicons } from 'vs/base/browser/codicons'; export interface IQuickInputOptions { idPrefix: string; @@ -70,7 +72,7 @@ const $ = dom.$; type Writeable = { -readonly [P in keyof T]: T[P] }; -const backButtonIcon = registerIcon('quick-input-back', Codicon.arrowLeft); +const backButtonIcon = registerCodicon('quick-input-back', Codicon.arrowLeft, localize('backButtonIcon', 'Icon for the back button in the quick input dialog.')); const backButton = { iconClass: backButtonIcon.classNames, @@ -413,6 +415,7 @@ class QuickPick extends QuickInput implements IQuickPi private _valueSelection: Readonly<[number, number]> | undefined; private valueSelectionUpdated = true; private _validationMessage: string | undefined; + private _lastValidationMessage: string | undefined; private _ok: boolean | 'default' = 'default'; private _customButton = false; private _customButtonLabel: string | undefined; @@ -961,12 +964,11 @@ class QuickPick extends QuickInput implements IQuickPi this.selectedItemsToConfirm = null; } } - if (this.validationMessage) { - this.ui.message.textContent = this.validationMessage; - this.showMessageDecoration(Severity.Error); - } else { - this.ui.message.textContent = null; - this.showMessageDecoration(Severity.Ignore); + const validationMessage = this.validationMessage || ''; + if (this._lastValidationMessage !== validationMessage) { + this._lastValidationMessage = validationMessage; + dom.reset(this.ui.message, ...renderCodicons(escape(validationMessage))); + this.showMessageDecoration(this.validationMessage ? Severity.Error : Severity.Ignore); } this.ui.customButton.label = this.customLabel || ''; this.ui.customButton.element.title = this.customHover || ''; @@ -996,6 +998,7 @@ class InputBox extends QuickInput implements IInputBox { private _prompt: string | undefined; private noValidationMessage = InputBox.noPromptMessage; private _validationMessage: string | undefined; + private _lastValidationMessage: string | undefined; private readonly onDidValueChangeEmitter = this._register(new Emitter()); private readonly onDidAcceptEmitter = this._register(new Emitter()); @@ -1097,13 +1100,11 @@ class InputBox extends QuickInput implements IInputBox { if (this.ui.inputBox.password !== this.password) { this.ui.inputBox.password = this.password; } - if (!this.validationMessage && this.ui.message.textContent !== this.noValidationMessage) { - this.ui.message.textContent = this.noValidationMessage; - this.showMessageDecoration(Severity.Ignore); - } - if (this.validationMessage && this.ui.message.textContent !== this.validationMessage) { - this.ui.message.textContent = this.validationMessage; - this.showMessageDecoration(Severity.Error); + const validationMessage = this.validationMessage || this.noValidationMessage; + if (this._lastValidationMessage !== validationMessage) { + this._lastValidationMessage = validationMessage; + dom.reset(this.ui.message, ...renderCodicons(validationMessage)); + this.showMessageDecoration(this.validationMessage ? Severity.Error : Severity.Ignore); } } } @@ -1528,7 +1529,7 @@ export class QuickInputController extends Disposable { ui.inputBox.showDecoration(Severity.Ignore); ui.visibleCount.setCount(0); ui.count.setCount(0); - ui.message.textContent = ''; + dom.reset(ui.message); ui.progressBar.stop(); ui.list.setElements([]); ui.list.matchOnDescription = false; @@ -1696,7 +1697,7 @@ export class QuickInputController extends Disposable { this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : ''; this.ui.container.style.color = quickInputForeground ? quickInputForeground.toString() : ''; this.ui.container.style.border = contrastBorder ? `1px solid ${contrastBorder}` : ''; - this.ui.container.style.boxShadow = widgetShadow ? `0 5px 8px ${widgetShadow}` : ''; + this.ui.container.style.boxShadow = widgetShadow ? `0 0 8px 2px ${widgetShadow}` : ''; this.ui.inputBox.style(this.styles.inputBox); this.ui.count.style(this.styles.countBadge); this.ui.ok.style(this.styles.button); diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index d0cb6d42..a1aa638f 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -35,6 +35,17 @@ } }, + /** + * @param {string} channel + * @param {any[]} args + * @returns {Promise | undefined} + */ + invoke(channel, ...args) { + if (validateIPC(channel)) { + return ipcRenderer.invoke(channel, ...args); + } + }, + /** * @param {string} channel * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener @@ -97,80 +108,48 @@ /** * Support for a subset of access to node.js global `process`. + * + * Note: when `sandbox` is enabled, the only properties available + * are https://github.com/electron/electron/blob/master/docs/api/process.md#sandbox */ process: { get platform() { return process.platform; }, get env() { return process.env; }, get versions() { return process.versions; }, get type() { return 'renderer'; }, + get execPath() { return process.execPath; }, - _whenEnvResolved: undefined, - whenEnvResolved: - /** - * @returns when the shell environment has been resolved. - */ - function () { - if (!this._whenEnvResolved) { - this._whenEnvResolved = resolveEnv(); - } + /** + * @param {{[key: string]: string}} userEnv + * @returns {Promise} + */ + resolveEnv(userEnv) { + return resolveEnv(userEnv); + }, - return this._whenEnvResolved; - }, + /** + * @returns {Promise} + */ + getProcessMemoryInfo() { + return process.getProcessMemoryInfo(); + }, - nextTick: - /** - * Adds callback to the "next tick queue". This queue is fully drained - * after the current operation on the JavaScript stack runs to completion - * and before the event loop is allowed to continue. - * - * @param {Function} callback - * @param {any[]} args - */ - function nextTick(callback, ...args) { - return process.nextTick(callback, ...args); - }, - - cwd: - /** - * @returns the current working directory. - */ - function () { - return process.cwd(); - }, - - getuid: - /** - * @returns the numeric user identity of the process - */ - function () { - return process.getuid(); - }, - - getProcessMemoryInfo: - /** - * @returns {Promise} - */ - function () { - return process.getProcessMemoryInfo(); - }, - - on: - /** - * @param {string} type - * @param {() => void} callback - */ - function (type, callback) { - if (validateProcessEventType(type)) { - process.on(type, callback); - } + /** + * @param {string} type + * @param {() => void} callback + */ + on(type, callback) { + if (validateProcessEventType(type)) { + process.on(type, callback); } + } }, /** * Some information about the context we are running in. */ context: { - get sandbox() { return process.argv.includes('--enable-sandbox'); } + get sandbox() { return process.sandboxed; } } }; @@ -197,6 +176,7 @@ /** * @param {string} channel + * @returns {true | never} */ function validateIPC(channel) { if (!channel || !channel.startsWith('vscode:')) { @@ -218,32 +198,40 @@ return true; } + /** @type {Promise | undefined} */ + let resolvedEnv = undefined; + /** * If VSCode is not run from a terminal, we should resolve additional * shell specific environment from the OS shell to ensure we are seeing * all development related environment variables. We do this from the * main process because it may involve spawning a shell. + * + * @param {{[key: string]: string}} userEnv + * @returns {Promise} */ - function resolveEnv() { - return new Promise(function (resolve) { - const handle = setTimeout(function () { - console.warn('Preload: Unable to resolve shell environment in a reasonable time'); + function resolveEnv(userEnv) { + if (!resolvedEnv) { - // It took too long to fetch the shell environment, return - resolve(); - }, 3000); + // Apply `userEnv` directly + Object.assign(process.env, userEnv); - ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { - clearTimeout(handle); + // Resolve `shellEnv` from the main side + resolvedEnv = new Promise(function (resolve) { + ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { - // Assign all keys of the shell environment to our process environment - Object.assign(process.env, shellEnv); + // Assign all keys of the shell environment to our process environment + // But make sure that the user environment wins in the end + Object.assign(process.env, shellEnv, userEnv); - resolve(); + resolve(); + }); + + ipcRenderer.send('vscode:fetchShellEnv'); }); + } - ipcRenderer.send('vscode:fetchShellEnv'); - }); + return resolvedEnv; } //#endregion diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index 923383b4..69f3a2a8 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -12,45 +12,45 @@ export interface ISandboxNodeProcess extends INodeProcess { * The process.platform property returns a string identifying the operating system platform * on which the Node.js process is running. */ - platform: 'win32' | 'linux' | 'darwin'; + readonly platform: 'win32' | 'linux' | 'darwin'; /** * The type will always be Electron renderer. */ - type: 'renderer'; + readonly type: 'renderer'; /** * A list of versions for the current node.js/electron configuration. */ - versions: { [key: string]: string | undefined }; + readonly versions: { [key: string]: string | undefined }; /** * The process.env property returns an object containing the user environment. */ - env: IProcessEnvironment; + readonly env: IProcessEnvironment; /** - * The current working directory. + * The `execPath` will be the location of the executable of this application. */ - cwd(): string; + readonly execPath: string; /** - * Returns the numeric user identity of the process. + * Resolve the true process environment to use and apply it to `process.env`. + * + * There are different layers of environment that will apply: + * - `process.env`: this is the actual environment of the process before this method + * - `shellEnv` : if the program was not started from a terminal, we resolve all shell + * variables to get the same experience as if the program was started from + * a terminal (Linux, macOS) + * - `userEnv` : this is instance specific environment, e.g. if the user started the program + * from a terminal and changed certain variables + * + * The order of overwrites is `process.env` < `shellEnv` < `userEnv`. + * + * It is critical that every process awaits this method early on startup to get the right + * set of environment in `process.env`. */ - getuid(): number; - - /** - * Allows to await resolving the full process environment by checking for the shell environment - * of the OS in certain cases (e.g. when the app is started from the Dock on macOS). - */ - whenEnvResolved(): Promise; - - /** - * Adds callback to the "next tick queue". This queue is fully drained - * after the current operation on the JavaScript stack runs to completion - * and before the event loop is allowed to continue. - */ - nextTick(callback: (...args: any[]) => void, ...args: any[]): void; + resolveEnv(userEnv: IProcessEnvironment): Promise; /** * A listener on the process. Only a small subset of listener types are allowed. diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index b6602b6a..8b55819c 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -43,9 +43,10 @@ export interface IStorageDatabase { export interface IStorage extends IDisposable { + readonly onDidChangeStorage: Event; + readonly items: Map; readonly size: number; - readonly onDidChangeStorage: Event; init(): Promise; @@ -61,6 +62,8 @@ export interface IStorage extends IDisposable { set(key: string, value: string | boolean | number | undefined | null): Promise; delete(key: string): Promise; + whenFlushed(): Promise; + close(): Promise; } @@ -86,6 +89,8 @@ export class Storage extends Disposable implements IStorage { private pendingDeletes = new Set(); private pendingInserts = new Map(); + private readonly whenFlushedCallbacks: Function[] = []; + constructor( protected readonly database: IStorageDatabase, private readonly options: IStorageOptions = Object.create(null) @@ -273,8 +278,12 @@ export class Storage extends Disposable implements IStorage { await this.database.close(() => this.cache); } + private get hasPending() { + return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0; + } + private flushPending(): Promise { - if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) { + if (!this.hasPending) { return Promise.resolve(); // return early if nothing to do } @@ -285,8 +294,23 @@ export class Storage extends Disposable implements IStorage { this.pendingDeletes = new Set(); this.pendingInserts = new Map(); - // Update in storage - return this.database.updateItems(updateRequest); + // Update in storage and release any + // waiters we have once done + return this.database.updateItems(updateRequest).finally(() => { + if (!this.hasPending) { + while (this.whenFlushedCallbacks.length) { + this.whenFlushedCallbacks.pop()?.(); + } + } + }); + } + + whenFlushed(): Promise { + if (!this.hasPending) { + return Promise.resolve(); // return early if nothing to do + } + + return new Promise(resolve => this.whenFlushedCallbacks.push(resolve)); } } diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index 3c351039..e735634c 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -45,11 +45,16 @@ suite('Storage Library', function () { changes.add(key); }); + await storage.whenFlushed(); // returns immediately when no pending updates + // Simple updates const set1Promise = storage.set('bar', 'foo'); const set2Promise = storage.set('barNumber', 55); const set3Promise = storage.set('barBoolean', true); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('bar'), 'foo'); equal(storage.getNumber('barNumber'), 55); equal(storage.getBoolean('barBoolean'), true); @@ -62,6 +67,7 @@ suite('Storage Library', function () { let setPromiseResolved = false; await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true); equal(setPromiseResolved, true); + equal(flushPromiseResolved, true); changes = new Set(); @@ -166,6 +172,9 @@ suite('Storage Library', function () { const set1Promise = storage.set('foo', 'bar'); const set2Promise = storage.set('bar', 'foo'); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('foo'), 'bar'); equal(storage.get('bar'), 'foo'); @@ -175,6 +184,7 @@ suite('Storage Library', function () { await storage.close(); equal(setPromiseResolved, true); + equal(flushPromiseResolved, true); storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); @@ -226,6 +236,9 @@ suite('Storage Library', function () { const set2Promise = storage.set('foo', 'bar2'); const set3Promise = storage.set('foo', 'bar3'); + let flushPromiseResolved = false; + storage.whenFlushed().then(() => flushPromiseResolved = true); + equal(storage.get('foo'), 'bar3'); equal(changes.size, 1); ok(changes.has('foo')); @@ -233,6 +246,7 @@ suite('Storage Library', function () { let setPromiseResolved = false; await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true); ok(setPromiseResolved); + ok(flushPromiseResolved); changes = new Set(); diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts index 4c40ea2a..77ec3adf 100644 --- a/src/vs/base/test/browser/comparers.test.ts +++ b/src/vs/base/test/browser/comparers.test.ts @@ -197,7 +197,7 @@ suite('Comparers', () => { // name-only comparisons assert(compareFileNamesDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter sorts by locale'); assert(compareFileNamesDefault('Ãĸ', 'Â') === compareLocale('Ãĸ', 'Â'), 'the same accented letter sorts by locale'); - assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + // assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); assert.deepEqual(['email', 'Email', 'Êmail', 'Émail'].sort(compareFileNamesDefault), ['email', 'Email', 'Êmail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents sort in locale order'); // numeric comparisons @@ -259,7 +259,7 @@ suite('Comparers', () => { // name-only comparisons assert(compareFileExtensionsDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter of different case sorts by locale'); assert(compareFileExtensionsDefault('Ãĸ', 'Â') === compareLocale('Ãĸ', 'Â'), 'the same accented letter of different case sorts by locale'); - assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); + // assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order'); assert.deepEqual(['email', 'Email', 'Êmail', 'Émail'].sort(compareFileExtensionsDefault), ['email', 'Email', 'Êmail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents sort in locale order'); // name plus extension comparisons diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/browser/hash.test.ts similarity index 85% rename from src/vs/base/test/common/hash.test.ts rename to src/vs/base/test/browser/hash.test.ts index b5074f4f..68ee17ee 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/browser/hash.test.ts @@ -2,8 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { hash, StringSHA1 } from 'vs/base/common/hash'; +import { sha1Hex } from 'vs/base/browser/hash'; suite('Hash', () => { test('string', () => { @@ -71,28 +73,32 @@ suite('Hash', () => { }); - function checkSHA1(strings: string[], expected: string) { + async function checkSHA1(str: string, expected: string) { + + // Test with StringSHA1 const hash = new StringSHA1(); - for (const str of strings) { - hash.update(str); - } - const actual = hash.digest(); + hash.update(str); + let actual = hash.digest(); + assert.equal(actual, expected); + + // Test with crypto.subtle + actual = await sha1Hex(str); assert.equal(actual, expected); } test('sha1-1', () => { - checkSHA1(['\udd56'], '9bdb77276c1852e1fb067820472812fcf6084024'); + return checkSHA1('\udd56', '9bdb77276c1852e1fb067820472812fcf6084024'); }); test('sha1-2', () => { - checkSHA1(['\udb52'], '9bdb77276c1852e1fb067820472812fcf6084024'); + return checkSHA1('\udb52', '9bdb77276c1852e1fb067820472812fcf6084024'); }); test('sha1-3', () => { - checkSHA1(['\uda02ꑍ'], '9b483a471f22fe7e09d83f221871a987244bbd3f'); + return checkSHA1('\uda02ꑍ', '9b483a471f22fe7e09d83f221871a987244bbd3f'); }); test('sha1-4', () => { - checkSHA1(['hello'], 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'); + return checkSHA1('hello', 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'); }); }); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 78f82030..f4bfa18d 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; -import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; +import { renderMarkdown, renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { MarkdownString, IMarkdownString } from 'vs/base/common/htmlContent'; import { URI } from 'vs/base/common/uri'; import { parse } from 'vs/base/common/marshalling'; @@ -57,7 +57,7 @@ suite('MarkdownRenderer', () => { mds.appendText('$(zap) $(not a theme icon) $(add)'); let result: HTMLElement = renderMarkdown(mds); - assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); + assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); }); test('render appendMarkdown', () => { @@ -85,7 +85,7 @@ suite('MarkdownRenderer', () => { mds.appendText('$(zap) $(not a theme icon) $(add)'); let result: HTMLElement = renderMarkdown(mds); - assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); + assert.strictEqual(result.innerHTML, `

$(zap) $(not a theme icon) $(add)

`); }); test('render appendMarkdown with escaped icon', () => { @@ -115,4 +115,20 @@ suite('MarkdownRenderer', () => { assert.ok(data.documentUri.toString().startsWith('file:///c%3A/')); }); + suite('PlaintextMarkdownRender', () => { + + test('test code, blockquote, heading, list, listitem, paragraph, table, tablerow, tablecell, strong, em, br, del, text are rendered plaintext', () => { + const markdown = { value: '`code`\n>quote\n# heading\n- list\n\n\ntable | table2\n--- | --- \none | two\n\n\nbo**ld**\n_italic_\n~~del~~\nsome text' }; + const expected = 'code\nquote\nheading\nlist\ntable table2 one two \nbold\nitalic\ndel\nsome text\n'; + const result: string = renderMarkdownAsPlaintext(markdown); + assert.strictEqual(result, expected); + }); + + test('test html, hr, image, link are rendered plaintext', () => { + const markdown = { value: '
html
\n\n---\n![image](imageLink)\n[text](textLink)' }; + const expected = '\ntext\n'; + const result: string = renderMarkdownAsPlaintext(markdown); + assert.strictEqual(result, expected); + }); + }); }); diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts index 5ab03d9c..4d3c0944 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -18,8 +18,11 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 249); - assert.equal(actual.getDesiredScrollPositionFromOffset(259), 32849); + + // 259 is greater than 230 so page down, 32787 + 339 = 33126 + assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(259), 33126); + actual.setScrollPosition(32849); assert.equal(actual.getArrowSize(), 0); assert.equal(actual.getScrollPosition(), 32849); @@ -41,8 +44,11 @@ suite('ScrollbarState', () => { assert.equal(actual.getSliderSize(), 20); assert.equal(actual.getSliderPosition(), 230); - assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 32811); + + // 240 + 12 = 252; greater than 230 so page down, 32787 + 339 = 33126 + assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(240 + 12), 33126); + actual.setScrollPosition(32811); assert.equal(actual.getArrowSize(), 12); assert.equal(actual.getScrollPosition(), 32811); diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 2620db0b..c36a51fd 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -91,7 +91,7 @@ suite('Splitview', () => { splitview.addView(view2, 20); splitview.addView(view3, 20); - let viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + let viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 3, 'split view should have 3 views'); let sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -99,7 +99,7 @@ suite('Splitview', () => { splitview.removeView(2); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 2, 'split view should have 2 views'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -107,7 +107,7 @@ suite('Splitview', () => { splitview.removeView(0); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 1, 'split view should have 1 view'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); @@ -115,7 +115,7 @@ suite('Splitview', () => { splitview.removeView(0); - viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view'); + viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view'); assert.equal(viewQuery.length, 0, 'split view should have no views'); sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash'); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index a7d505b2..d1baf9b5 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as async from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; suite('Async', () => { @@ -651,41 +651,39 @@ suite('Async', () => { test('raceCancellation', async () => { const cts = new CancellationTokenSource(); - const now = Date.now(); - - const p = async.raceCancellation(async.timeout(100), cts.token); + let triggered = false; + const p = async.raceCancellation(async.timeout(100).then(() => triggered = true), cts.token); cts.cancel(); await p; - assert.ok(Date.now() - now < 100); + assert.ok(!triggered); }); test('raceTimeout', async () => { const cts = new CancellationTokenSource(); // timeout wins - let now = Date.now(); let timedout = false; + let triggered = false; - const p1 = async.raceTimeout(async.timeout(100), 1, () => timedout = true); + const p1 = async.raceTimeout(async.timeout(100).then(() => triggered = true), 1, () => timedout = true); cts.cancel(); await p1; - assert.ok(Date.now() - now < 100); + assert.ok(!triggered); assert.equal(timedout, true); // promise wins - now = Date.now(); timedout = false; - const p2 = async.raceTimeout(async.timeout(1), 100, () => timedout = true); + const p2 = async.raceTimeout(async.timeout(1).then(() => triggered = true), 100, () => timedout = true); cts.cancel(); await p2; - assert.ok(Date.now() - now < 100); + assert.ok(triggered); assert.equal(timedout, false); }); @@ -719,4 +717,63 @@ suite('Async', () => { assert.equal(counter.increment(), 2); assert.equal(counter.increment(), 3); }); + + test('firstParallel - simple', async () => { + const a = await async.firstParallel([ + Promise.resolve(1), + Promise.resolve(2), + Promise.resolve(3), + ], v => v === 2); + assert.equal(a, 2); + }); + + test('firstParallel - uses null default', async () => { + assert.equal(await async.firstParallel([Promise.resolve(1)], v => v === 2), null); + }); + + test('firstParallel - uses value default', async () => { + assert.equal(await async.firstParallel([Promise.resolve(1)], v => v === 2, 4), 4); + }); + + test('firstParallel - empty', async () => { + assert.equal(await async.firstParallel([], v => v === 2, 4), 4); + }); + + test('firstParallel - cancels', async () => { + let ct1: CancellationToken; + const p1 = async.createCancelablePromise(async (ct) => { + ct1 = ct; + await async.timeout(200, ct); + return 1; + }); + let ct2: CancellationToken; + const p2 = async.createCancelablePromise(async (ct) => { + ct2 = ct; + await async.timeout(2, ct); + return 2; + }); + + assert.equal(await async.firstParallel([p1, p2], v => v === 2, 4), 2); + assert.equal(ct1!.isCancellationRequested, true, 'should cancel a'); + assert.equal(ct2!.isCancellationRequested, true, 'should cancel b'); + }); + + test('firstParallel - rejection handling', async () => { + let ct1: CancellationToken; + const p1 = async.createCancelablePromise(async (ct) => { + ct1 = ct; + await async.timeout(200, ct); + return 1; + }); + let ct2: CancellationToken; + const p2 = async.createCancelablePromise(async (ct) => { + ct2 = ct; + await async.timeout(2, ct); + throw new Error('oh no'); + }); + + assert.equal(await async.firstParallel([p1, p2], v => v === 2, 4).catch(() => 'ok'), 'ok'); + assert.equal(ct1!.isCancellationRequested, true, 'should cancel a'); + assert.equal(ct2!.isCancellationRequested, true, 'should cancel b'); + }); }); diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts index e63fda85..dbb057a2 100644 --- a/src/vs/base/test/common/linkedList.test.ts +++ b/src/vs/base/test/common/linkedList.test.ts @@ -14,7 +14,7 @@ suite('LinkedList', function () { assert.equal(list.size, elements.length); // assert toArray - assert.deepEqual(list.toArray(), elements); + assert.deepEqual(Array.from(list), elements); // assert Symbol.iterator (1) assert.deepEqual([...list], elements); diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 2615f098..5781de52 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator } from 'vs/base/common/map'; +import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator, ConfigKeysIterator } from 'vs/base/common/map'; import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { extUriIgnorePathCase } from 'vs/base/common/resources'; @@ -368,7 +368,7 @@ suite('Map', () => { }); test('URIIterator', function () { - const iter = new UriIterator(false); + const iter = new UriIterator(() => false); iter.reset(URI.parse('file:///usr/bin/file.txt')); assert.equal(iter.value(), 'file'); @@ -549,13 +549,15 @@ suite('Map', () => { trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); + trie.set('foobarbaz', 4); trie.deleteSuperstr('foo'); - assertTernarySearchTree(trie, ['bar', 3]); + assertTernarySearchTree(trie, ['foo', 1], ['bar', 3]); trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); + trie.set('foobarbaz', 4); trie.deleteSuperstr('fo'); assertTernarySearchTree(trie, ['bar', 3]); @@ -612,17 +614,17 @@ suite('Map', () => { map.set('/user/foo/flip/flop', 3); map.set('/usr/foo', 4); - let item: IteratorResult; + let item: IteratorResult<[string, number]>; let iter = map.findSuperstr('/user'); item = iter!.next(); - assert.equal(item.value, 2); + assert.equal(item.value[1], 2); assert.equal(item.done, false); item = iter!.next(); - assert.equal(item.value, 1); + assert.equal(item.value[1], 1); assert.equal(item.done, false); item = iter!.next(); - assert.equal(item.value, 3); + assert.equal(item.value[1], 3); assert.equal(item.done, false); item = iter!.next(); assert.equal(item.value, undefined); @@ -630,7 +632,7 @@ suite('Map', () => { iter = map.findSuperstr('/usr'); item = iter!.next(); - assert.equal(item.value, 4); + assert.equal(item.value[1], 4); assert.equal(item.done, false); item = iter!.next(); @@ -675,12 +677,12 @@ suite('Map', () => { map.set('/usr/foo', 4); map.deleteSuperstr('/user/foo'); assertTernarySearchTree(map, - ['/usr/foo', 4], + ['/user/foo', 2], ['/usr/foo', 4], ); }); test('TernarySearchTree (URI) - basics', function () { - let trie = new TernarySearchTree(new UriIterator(false)); + let trie = new TernarySearchTree(new UriIterator(() => false)); trie.set(URI.file('/user/foo/bar'), 1); trie.set(URI.file('/user/foo'), 2); @@ -700,7 +702,7 @@ suite('Map', () => { test('TernarySearchTree (URI) - lookup', function () { - const map = new TernarySearchTree(new UriIterator(false)); + const map = new TernarySearchTree(new UriIterator(() => false)); map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); map.set(URI.parse('http://foo.bar/user/foo?query'), 2); map.set(URI.parse('http://foo.bar/user/foo?QUERY'), 3); @@ -715,25 +717,35 @@ suite('Map', () => { assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar/boo')), undefined); }); - test('TernarySearchTree (PathSegments) - superstr', function () { + test('TernarySearchTree (URI) - lookup, casing', function () { - const map = new TernarySearchTree(new UriIterator(false)); + const map = new TernarySearchTree(new UriIterator(uri => /^https?$/.test(uri.scheme))); + map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); + assert.equal(map.get(URI.parse('http://foo.bar/USER/foo/bar')), 1); + + map.set(URI.parse('foo://foo.bar/user/foo/bar'), 1); + assert.equal(map.get(URI.parse('foo://foo.bar/USER/foo/bar')), undefined); + }); + + test('TernarySearchTree (URI) - superstr', function () { + + const map = new TernarySearchTree(new UriIterator(() => false)); map.set(URI.file('/user/foo/bar'), 1); map.set(URI.file('/user/foo'), 2); map.set(URI.file('/user/foo/flip/flop'), 3); map.set(URI.file('/usr/foo'), 4); - let item: IteratorResult; + let item: IteratorResult<[URI, number]>; let iter = map.findSuperstr(URI.file('/user'))!; item = iter.next(); - assert.equal(item.value, 2); + assert.equal(item.value[1], 2); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 1); + assert.equal(item.value[1], 1); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 3); + assert.equal(item.value[1], 3); assert.equal(item.done, false); item = iter.next(); assert.equal(item.value, undefined); @@ -741,7 +753,7 @@ suite('Map', () => { iter = map.findSuperstr(URI.file('/usr'))!; item = iter.next(); - assert.equal(item.value, 4); + assert.equal(item.value[1], 4); assert.equal(item.done, false); item = iter.next(); @@ -750,16 +762,16 @@ suite('Map', () => { iter = map.findSuperstr(URI.file('/'))!; item = iter.next(); - assert.equal(item.value, 2); + assert.equal(item.value[1], 2); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 1); + assert.equal(item.value[1], 1); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 3); + assert.equal(item.value[1], 3); assert.equal(item.done, false); item = iter.next(); - assert.equal(item.value, 4); + assert.equal(item.value[1], 4); assert.equal(item.done, false); item = iter.next(); assert.equal(item.value, undefined); @@ -771,6 +783,103 @@ suite('Map', () => { assert.equal(map.findSuperstr(URI.file('/userr')), undefined); }); + test('TernarySearchTree (ConfigKeySegments) - basics', function () { + let trie = new TernarySearchTree(new ConfigKeysIterator()); + + trie.set('config.foo.bar', 1); + trie.set('config.foo', 2); + trie.set('config.foo.flip.flop', 3); + + assert.equal(trie.get('config.foo.bar'), 1); + assert.equal(trie.get('config.foo'), 2); + assert.equal(trie.get('config.foo.flip.flop'), 3); + + assert.equal(trie.findSubstr('config.bar'), undefined); + assert.equal(trie.findSubstr('config.foo'), 2); + assert.equal(trie.findSubstr('config.foo.ba'), 2); + assert.equal(trie.findSubstr('config.foo.far.boo'), 2); + assert.equal(trie.findSubstr('config.foo.bar'), 1); + assert.equal(trie.findSubstr('config.foo.bar.far.boo'), 1); + }); + + test('TernarySearchTree (ConfigKeySegments) - lookup', function () { + + const map = new TernarySearchTree(new ConfigKeysIterator()); + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + + assert.equal(map.get('foo'), undefined); + assert.equal(map.get('config'), undefined); + assert.equal(map.get('config.foo'), 2); + assert.equal(map.get('config.foo.bar'), 1); + assert.equal(map.get('config.foo.bar.boo'), undefined); + }); + + test('TernarySearchTree (ConfigKeySegments) - superstr', function () { + + const map = new TernarySearchTree(new ConfigKeysIterator()); + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + map.set('boo', 4); + + let item: IteratorResult<[string, number]>; + let iter = map.findSuperstr('config'); + + item = iter!.next(); + assert.equal(item.value[1], 2); + assert.equal(item.done, false); + item = iter!.next(); + assert.equal(item.value[1], 1); + assert.equal(item.done, false); + item = iter!.next(); + assert.equal(item.value[1], 3); + assert.equal(item.done, false); + item = iter!.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + assert.equal(map.findSuperstr('foo'), undefined); + assert.equal(map.findSuperstr('config.foo.no'), undefined); + assert.equal(map.findSuperstr('config.foop'), undefined); + }); + + + test('TernarySearchTree (ConfigKeySegments) - delete_superstr', function () { + + const map = new TernarySearchTree(new ConfigKeysIterator()); + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + map.set('boo', 4); + + assertTernarySearchTree(map, + ['config.foo.bar', 1], + ['config.foo', 2], + ['config.foo.flip.flop', 3], + ['boo', 4], + ); + + // not a segment + map.deleteSuperstr('config.fo'); + assertTernarySearchTree(map, + ['config.foo.bar', 1], + ['config.foo', 2], + ['config.foo.flip.flop', 3], + ['boo', 4], + ); + + // delete a segment + map.set('config.foo.bar', 1); + map.set('config.foo', 2); + map.set('config.foo.flip.flop', 3); + map.set('config.boo', 4); + map.deleteSuperstr('config.foo'); + assertTernarySearchTree(map, + ['config.foo', 2], ['boo', 4], + ); + }); test('ResourceMap - basics', function () { const map = new ResourceMap(); diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts index f33f741f..424c2bd4 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -8,12 +8,24 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; suite('MarkdownString', () => { + test('Escape leading whitespace', function () { + const mds = new MarkdownString(); + mds.appendText('Hello\n Not a code block'); + assert.equal(mds.value, 'Hello\n\n    Not a code block'); + }); + + test('MarkdownString.appendText doesn\'t escape quote #109040', function () { + const mds = new MarkdownString(); + mds.appendText('> Text\n>More'); + assert.equal(mds.value, '\\> Text\n\n\\>More'); + }); + test('appendText', () => { const mds = new MarkdownString(); mds.appendText('# foo\n*bar*'); - assert.equal(mds.value, '\\# foo\n\n\\*bar\\*'); + assert.equal(mds.value, '\\# foo\n\n\\*bar\\*'); }); suite('ThemeIcons', () => { @@ -24,7 +36,7 @@ suite('MarkdownString', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: true }); mds.appendText('$(zap) $(not a theme icon) $(add)'); - assert.equal(mds.value, '\\\\$\\(zap\\) $\\(not a theme icon\\) \\\\$\\(add\\)'); + assert.equal(mds.value, '\\\\$\\(zap\\) $\\(not a theme icon\\) \\\\$\\(add\\)'); }); test('appendMarkdown', () => { @@ -49,7 +61,7 @@ suite('MarkdownString', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: false }); mds.appendText('$(zap) $(not a theme icon) $(add)'); - assert.equal(mds.value, '$\\(zap\\) $\\(not a theme icon\\) $\\(add\\)'); + assert.equal(mds.value, '$\\(zap\\) $\\(not a theme icon\\) $\\(add\\)'); }); test('appendMarkdown', () => { diff --git a/src/vs/base/test/common/network.test.ts b/src/vs/base/test/common/network.test.ts new file mode 100644 index 00000000..1729d4b1 --- /dev/null +++ b/src/vs/base/test/common/network.test.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { URI } from 'vs/base/common/uri'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { isEqual } from 'vs/base/common/resources'; +import { isElectronSandboxed } from 'vs/base/common/platform'; + +suite('network', () => { + const enableTest = isElectronSandboxed; + + (!enableTest ? test.skip : test)('FileAccess: URI (native)', () => { + + // asCodeUri() & asFileUri(): simple, without authority + let originalFileUri = URI.file('network.test.ts'); + let browserUri = FileAccess.asBrowserUri(originalFileUri); + assert.ok(browserUri.authority.length > 0); + let fileUri = FileAccess.asFileUri(browserUri); + assert.equal(fileUri.authority.length, 0); + assert(isEqual(originalFileUri, fileUri)); + + // asCodeUri() & asFileUri(): with authority + originalFileUri = URI.file('network.test.ts').with({ authority: 'test-authority' }); + browserUri = FileAccess.asBrowserUri(originalFileUri); + assert.equal(browserUri.authority, originalFileUri.authority); + fileUri = FileAccess.asFileUri(browserUri); + assert(isEqual(originalFileUri, fileUri)); + }); + + (!enableTest ? test.skip : test)('FileAccess: moduleId (native)', () => { + const browserUri = FileAccess.asBrowserUri('vs/base/test/node/network.test', require); + assert.equal(browserUri.scheme, Schemas.vscodeFileResource); + + const fileUri = FileAccess.asFileUri('vs/base/test/node/network.test', require); + assert.equal(fileUri.scheme, Schemas.file); + }); + + (!enableTest ? test.skip : test)('FileAccess: query and fragment is dropped (native)', () => { + let originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); + let browserUri = FileAccess.asBrowserUri(originalFileUri); + assert.equal(browserUri.query, ''); + assert.equal(browserUri.fragment, ''); + }); + + (!enableTest ? test.skip : test)('FileAccess: query and fragment is kept if URI is already of same scheme (native)', () => { + let originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); + let browserUri = FileAccess.asBrowserUri(originalFileUri.with({ scheme: Schemas.vscodeFileResource })); + assert.equal(browserUri.query, 'foo=bar'); + assert.equal(browserUri.fragment, 'something'); + + let fileUri = FileAccess.asFileUri(originalFileUri); + assert.equal(fileUri.query, 'foo=bar'); + assert.equal(fileUri.fragment, 'something'); + }); + + (!enableTest ? test.skip : test)('FileAccess: web', () => { + const originalHttpsUri = URI.file('network.test.ts').with({ scheme: 'https' }); + const browserUri = FileAccess.asBrowserUri(originalHttpsUri); + assert.equal(originalHttpsUri.toString(), browserUri.toString()); + }); + + test('FileAccess: remote URIs', () => { + const originalRemoteUri = URI.file('network.test.ts').with({ scheme: Schemas.vscodeRemote }); + const browserUri = FileAccess.asBrowserUri(originalRemoteUri); + assert.notEqual(originalRemoteUri.scheme, browserUri.scheme); + }); +}); diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts index 4b49a381..ee0d25c8 100644 --- a/src/vs/base/test/common/processes.test.ts +++ b/src/vs/base/test/common/processes.test.ts @@ -19,7 +19,6 @@ suite('Processes', () => { VSCODE_CLI: 'x', VSCODE_DEV: 'x', VSCODE_IPC_HOOK: 'x', - VSCODE_LOGS: 'x', VSCODE_NLS_CONFIG: 'x', VSCODE_PORTABLE: 'x', VSCODE_PID: 'x', diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 543cb460..d3366a1e 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -417,4 +417,9 @@ suite('Strings', () => { test('getGraphemeBreakType', () => { assert.equal(strings.getGraphemeBreakType(0xBC1), strings.GraphemeBreakType.SpacingMark); }); + + test('truncate', () => { + assert.equal('hello world', strings.truncate('hello world', 100)); + assert.equal('helloâ€Ļ', strings.truncate('hello world', 5)); + }); }); diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 71c6724e..c39dba9a 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -8,14 +8,20 @@ let MonacoEnvironment = (self).MonacoEnvironment; let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../'; + const trustedTypesPolicy = self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value }); + if (typeof (self).define !== 'function' || !(self).define.amd) { - importScripts(monacoBaseUrl + 'vs/loader.js'); + let loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js'; + if (trustedTypesPolicy) { + loaderSrc = trustedTypesPolicy.createScriptURL(loaderSrc); + } + importScripts(loaderSrc as string); } require.config({ baseUrl: monacoBaseUrl, catchError: true, - createTrustedScriptURL: (value: string) => value, + trustedTypesPolicy, }); let loadCode = function (moduleId: string) { diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index c3c259cf..8b186929 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -33,7 +33,14 @@ self.require = { baseUrl: `${window.location.origin}/static/out`, recordStats: true, - createTrustedScriptURL: value => value, + trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value) { + if(value.startsWith(window.location.origin)) { + return value; + } + throw new Error(`Invalid script url: ${value}`) + } + }), paths: { 'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/remote/web/node_modules/vscode-oniguruma/release/main`, diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 300ceb2f..1dbdce5d 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -32,7 +32,14 @@ self.require = { baseUrl: `${window.location.origin}/static/out`, recordStats: true, - createTrustedScriptURL: value => value, + trustedTypesPolicy: window.trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value) { + if(value.startsWith(window.location.origin)) { + return value; + } + throw new Error(`Invalid script url: ${value}`) + } + }), paths: { 'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/node_modules/vscode-oniguruma/release/main`, diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 0ef8b9dc..d1dba072 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -17,6 +17,7 @@ import { isStandalone } from 'vs/base/browser/browser'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import product from 'vs/platform/product/common/product'; +import { parseLogLevel } from 'vs/platform/log/common/log'; function doCreateUri(path: string, queryValues: Map): URI { let query: string | undefined = undefined; @@ -417,6 +418,7 @@ class WindowIndicator implements IWindowIndicator { let foundWorkspace = false; let workspace: IWorkspace; let payload = Object.create(null); + let logLevel: string | undefined = undefined; const query = new URL(document.location.href).searchParams; query.forEach((value, key) => { @@ -448,6 +450,11 @@ class WindowIndicator implements IWindowIndicator { console.error(error); // possible invalid JSON } break; + + // Log level + case 'logLevel': + logLevel = value; + break; } }); @@ -514,6 +521,7 @@ class WindowIndicator implements IWindowIndicator { // Finally create workbench create(document.body, { ...config, + logLevel: logLevel ? parseLogLevel(logLevel) : undefined, settingsSyncOptions, homeIndicator, windowIndicator, diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 88f3869f..3dcc360b 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -51,7 +51,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel, StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; @@ -63,7 +63,6 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; @@ -72,6 +71,7 @@ import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker'; import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; +import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -151,7 +151,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IStorageService, storageService); disposables.add(toDisposable(() => storageService.flush())); - services.set(IStorageKeysSyncRegistryService, new SyncDescriptor(StorageKeysSyncRegistryService)); services.set(IEnvironmentService, environmentService); services.set(INativeEnvironmentService, environmentService); @@ -188,7 +187,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat appender: telemetryAppender, commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath), sendErrorTelemetry: true, - piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] + piiPaths: [appRoot, extensionsPath] }; telemetryService = new TelemetryService(config, configurationService); @@ -199,10 +198,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat } server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(telemetryAppender)); - const storageKeysSyncRegistryService = accessor.get(IStorageKeysSyncRegistryService); - const storageKeysSyncChannel = new StorageKeysSyncRegistryChannel(storageKeysSyncRegistryService); - server.registerChannel('storageKeysSyncRegistryService', storageKeysSyncChannel); - services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); @@ -214,6 +209,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main'))); services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService)); services.set(IIgnoredExtensionsManagementService, new SyncDescriptor(IgnoredExtensionsManagementService)); + services.set(IExtensionsStorageSyncService, new SyncDescriptor(ExtensionsStorageSyncService)); services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService)); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService)); diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 3dd6691d..14c69946 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -9,15 +9,12 @@ 'use strict'; (function () { + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top - const perf = perfLib(); + const perf = bootstrapWindow.perfLib(); perf.mark('renderer/started'); - // Load environment in parallel to workbench loading to avoid waterfall - const bootstrapWindow = bootstrapWindowLib(); - const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved(); - // Load workbench main JS, CSS and NLS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because // we know for a fact that workbench.desktop.main will depend on @@ -31,12 +28,6 @@ // Mark start of workbench perf.mark('didLoadWorkbenchMain'); - performance.mark('workbench-start'); - - // Wait for process environment being fully resolved - await whenEnvResolved; - - perf.mark('main/startup'); // @ts-ignore return require('vs/workbench/electron-browser/desktop.main').main(configuration); @@ -58,23 +49,11 @@ //region Helpers - function perfLib() { - globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; - - return { - /** - * @param {string} name - */ - mark(name) { - globalThis.MonacoPerformanceMarks.push(name, Date.now()); - } - }; - } - /** * @returns {{ * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, - * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals'), + * perfLib: () => { mark: (name: string) => void } * }} */ function bootstrapWindowLib() { @@ -187,5 +166,5 @@ } //#endregion - + }()); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index cf79f58d..759c85bc 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, ipcMain as ipc, systemPreferences, contentTracing, protocol, IpcMainEvent, BrowserWindow, dialog, session } from 'electron'; -import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; +import { app, ipcMain, systemPreferences, contentTracing, protocol, BrowserWindow, dialog, session } from 'electron'; +import { IProcessEnvironment, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { getShellEnvironment } from 'vs/code/node/shellEnv'; +import { resolveShellEnv } from 'vs/code/node/shellEnv'; import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateChannel } from 'vs/platform/update/electron-main/updateIpc'; import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; @@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IStateService } from 'vs/platform/state/node/state'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IURLService } from 'vs/platform/url/common/url'; +import { IOpenURLOptions, IURLService } from 'vs/platform/url/common/url'; import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; import { ITelemetryService, machineIdKey } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -35,6 +35,7 @@ import { getDelayedChannel, StaticRouter, createChannelReceiver, createChannelSe import product from 'vs/platform/product/common/product'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { ProxyAuthHandler2 } from 'vs/code/electron-main/auth2'; +import { FileProtocolHandler } from 'vs/code/electron-main/protocol'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { URI } from 'vs/base/common/uri'; @@ -52,7 +53,7 @@ import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; -import { sep, posix } from 'vs/base/common/path'; +import { sep, posix, join, isAbsolute } from 'vs/base/common/path'; import { joinPath } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; @@ -72,7 +73,6 @@ import { INativeHostMainService, NativeHostMainService } from 'vs/platform/nativ import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { coalesce } from 'vs/base/common/arrays'; import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels'; import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService'; import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; @@ -82,6 +82,14 @@ import { generateUuid } from 'vs/base/common/uuid'; import { VSBuffer } from 'vs/base/common/buffer'; import { EncryptionMainService, IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker'; +import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from 'vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { DisplayMainService, IDisplayMainService } from 'vs/platform/display/electron-main/displayMainService'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { isEqualOrParent } from 'vs/base/common/extpath'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService'; export class CodeApplication extends Disposable { private windowsMainService: IWindowsMainService | undefined; @@ -96,7 +104,8 @@ export class CodeApplication extends Disposable { @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStateService private readonly stateService: IStateService + @IStateService private readonly stateService: IStateService, + @IFileService private readonly fileService: IFileService ) { super(); @@ -118,9 +127,7 @@ export class CodeApplication extends Disposable { // Accessibility change event app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); - } + this.windowsMainService?.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); }); // macOS dock activate @@ -128,8 +135,8 @@ export class CodeApplication extends Disposable { this.logService.trace('app#activate'); // Mac only event: open new window when we get activated - if (!hasVisibleWindows && this.windowsMainService) { - this.windowsMainService.openEmptyWindow({ context: OpenContext.DOCK }); + if (!hasVisibleWindows) { + this.windowsMainService?.openEmptyWindow({ context: OpenContext.DOCK }); } }); @@ -214,9 +221,7 @@ export class CodeApplication extends Disposable { contents.on('new-window', (event, url) => { event.preventDefault(); // prevent code that wants to open links - if (this.nativeHostMainService) { - this.nativeHostMainService.openExternal(undefined, url); - } + this.nativeHostMainService?.openExternal(undefined, url); }); session.defaultSession.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => { @@ -247,62 +252,119 @@ export class CodeApplication extends Disposable { // Handle paths delayed in case more are coming! runningTimeout = setTimeout(() => { - if (this.windowsMainService) { - this.windowsMainService.open({ - context: OpenContext.DOCK /* can also be opening from finder while app is running */, - cli: this.environmentService.args, - urisToOpen: macOpenFileURIs, - gotoLineMode: false, - preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ - }); + this.windowsMainService?.open({ + context: OpenContext.DOCK /* can also be opening from finder while app is running */, + cli: this.environmentService.args, + urisToOpen: macOpenFileURIs, + gotoLineMode: false, + preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ + }); - macOpenFileURIs = []; - runningTimeout = null; - } + macOpenFileURIs = []; + runningTimeout = null; }, 100); }); app.on('new-window-for-tab', () => { - if (this.windowsMainService) { - this.windowsMainService.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button - } + this.windowsMainService?.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button }); - ipc.on('vscode:fetchShellEnv', async (event: IpcMainEvent) => { + //#region Bootstrap IPC Handlers + + ipcMain.on('vscode:fetchShellEnv', async event => { const webContents = event.sender; + const window = this.windowsMainService?.getWindowByWebContents(event.sender); - try { - const shellEnv = await getShellEnvironment(this.logService, this.environmentService); + let replied = false; - if (!webContents.isDestroyed()) { - webContents.send('vscode:acceptShellEnv', shellEnv); + function acceptShellEnv(env: NodeJS.ProcessEnv): void { + clearTimeout(shellEnvSlowWarningHandle); + clearTimeout(shellEnvTimeoutErrorHandle); + + if (!replied) { + replied = true; + + if (!webContents.isDestroyed()) { + webContents.send('vscode:acceptShellEnv', env); + } } - } catch (error) { - if (!webContents.isDestroyed()) { - webContents.send('vscode:acceptShellEnv', {}); - } - - this.logService.error('Error fetching shell env', error); } + + // Handle slow shell environment resolve calls: + // - a warning after 3s but continue to resolve + // - an error after 10s and stop trying to resolve + const cts = new CancellationTokenSource(); + const shellEnvSlowWarningHandle = setTimeout(() => window?.sendWhenReady('vscode:showShellEnvSlowWarning', cts.token), 3000); + const shellEnvTimeoutErrorHandle = setTimeout(function () { + cts.dispose(true); + window?.sendWhenReady('vscode:showShellEnvTimeoutError', CancellationToken.None); + acceptShellEnv({}); + }, 10000); + + // Prefer to use the args and env from the target window + // when resolving the shell env. It is possible that + // a first window was opened from the UI but a second + // from the CLI and that has implications for wether to + // resolve the shell environment or not. + let args: NativeParsedArgs; + let env: NodeJS.ProcessEnv; + if (window?.config) { + args = window.config; + env = { ...process.env, ...window.config.userEnv }; + } else { + args = this.environmentService.args; + env = process.env; + } + + // Resolve shell env + const shellEnv = await resolveShellEnv(this.logService, args, env); + acceptShellEnv(shellEnv); }); - ipc.on('vscode:toggleDevTools', (event: IpcMainEvent) => event.sender.toggleDevTools()); - ipc.on('vscode:openDevTools', (event: IpcMainEvent) => event.sender.openDevTools()); + ipcMain.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => { + const uri = this.validateNlsPath([path]); + if (!uri || typeof data !== 'string') { + return Promise.reject('Invalid operation (vscode:writeNlsFile)'); + } - ipc.on('vscode:reloadWindow', (event: IpcMainEvent) => event.sender.reload()); + return this.fileService.writeFile(uri, VSBuffer.fromString(data)); + }); - // Some listeners after window opened - (async () => { - await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); + ipcMain.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => { + const uri = this.validateNlsPath(paths); + if (!uri) { + return Promise.reject('Invalid operation (vscode:readNlsFile)'); + } - // Keyboard layout changes (after window opened) - const nativeKeymap = await import('native-keymap'); - nativeKeymap.onDidChangeKeyboardLayout(() => { - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged'); + return (await this.fileService.readFile(uri)).value.toString(); + }); + + ipcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools()); + ipcMain.on('vscode:openDevTools', event => event.sender.openDevTools()); + + ipcMain.on('vscode:reloadWindow', event => event.sender.reload()); + + //#endregion + } + + private validateNlsPath(pathSegments: unknown[]): URI | undefined { + let path: string | undefined = undefined; + + for (const pathSegment of pathSegments) { + if (typeof pathSegment === 'string') { + if (typeof path !== 'string') { + path = pathSegment; + } else { + path = join(path, pathSegment); } - }); - })(); + } + } + + if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentService.cachedLanguagesPath, !isLinux)) { + return undefined; + } + + return URI.file(path); } private onUnexpectedError(err: Error): void { @@ -315,9 +377,7 @@ export class CodeApplication extends Disposable { }; // handle on client side - if (this.windowsMainService) { - this.windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); - } + this.windowsMainService?.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); } this.logService.error(`[uncaught exception in main]: ${err}`); @@ -354,6 +414,9 @@ export class CodeApplication extends Disposable { this.logService.error(error); } + // Setup Protocol Handler + const fileProtocolHandler = this._register(this.instantiationService.createInstance(FileProtocolHandler)); + // Create Electron IPC Server const electronIpcServer = new ElectronIPCServer(); @@ -376,7 +439,7 @@ export class CodeApplication extends Disposable { }); this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { this._register(new RunOnceScheduler(async () => { - sharedProcess.spawn(await getShellEnvironment(this.logService, this.environmentService)); + sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentService.args, process.env)); }, 3000)).schedule(); }); @@ -391,15 +454,15 @@ export class CodeApplication extends Disposable { this._register(server); } - // Setup Auth Handler - if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') !== true) { + // Setup Auth Handler (TODO@ben remove old auth handler eventually) + if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') === false) { this._register(new ProxyAuthHandler()); } else { this._register(appInstantiationService.createInstance(ProxyAuthHandler2)); } // Open Windows - const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient)); + const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient, fileProtocolHandler)); // Post Open Windows Tasks appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); @@ -453,10 +516,13 @@ export class CodeApplication extends Disposable { services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId])); + services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService)); + services.set(IDisplayMainService, new SyncDescriptor(DisplayMainService)); services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService)); services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService)); services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService)); services.set(IMenubarMainService, new SyncDescriptor(MenubarMainService)); + services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService)); const storageMainService = new StorageMainService(this.logService, this.environmentService); services.set(IStorageMainService, storageMainService); @@ -474,7 +540,7 @@ export class CodeApplication extends Disposable { const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender'))); const appender = new TelemetryAppenderClient(channel); const commonProperties = resolveCommonProperties(product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath); - const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot]; + const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); @@ -502,14 +568,12 @@ export class CodeApplication extends Disposable { const path = await contentTracing.stopRecording(joinPath(this.environmentService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); if (!timeout) { - if (this.dialogMainService) { - this.dialogMainService.showMessageBox({ - type: 'info', - message: localize('trace.message', "Successfully created trace."), - detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), - buttons: [localize('trace.ok', "OK")] - }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); - } + this.dialogMainService?.showMessageBox({ + type: 'info', + message: localize('trace.message', "Successfully created trace."), + detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), + buttons: [localize('trace.ok', "OK")] + }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); } else { this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); } @@ -525,7 +589,7 @@ export class CodeApplication extends Disposable { }); } - private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise>): ICodeWindow[] { + private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise>, fileProtocolHandler: FileProtocolHandler): ICodeWindow[] { // Register more Main IPC services const launchMainService = accessor.get(ILaunchMainService); @@ -545,6 +609,14 @@ export class CodeApplication extends Disposable { const encryptionChannel = createChannelReceiver(encryptionMainService); electronIpcServer.registerChannel('encryption', encryptionChannel); + const keyboardLayoutMainService = accessor.get(IKeyboardLayoutMainService); + const keyboardLayoutChannel = createChannelReceiver(keyboardLayoutMainService); + electronIpcServer.registerChannel('keyboardLayout', keyboardLayoutChannel); + + const displayMainService = accessor.get(IDisplayMainService); + const displayChannel = createChannelReceiver(displayMainService); + electronIpcServer.registerChannel('display', displayChannel); + const nativeHostMainService = this.nativeHostMainService = accessor.get(INativeHostMainService); const nativeHostChannel = createChannelReceiver(this.nativeHostMainService); electronIpcServer.registerChannel('nativeHost', nativeHostChannel); @@ -566,6 +638,10 @@ export class CodeApplication extends Disposable { const urlChannel = createChannelReceiver(urlService); electronIpcServer.registerChannel('url', urlChannel); + const extensionUrlTrustService = accessor.get(IExtensionUrlTrustService); + const extensionUrlTrustChannel = createChannelReceiver(extensionUrlTrustService); + electronIpcServer.registerChannel('extensionUrlTrust', extensionUrlTrustChannel); + const webviewManagerService = accessor.get(IWebviewManagerService); const webviewChannel = createChannelReceiver(webviewManagerService); electronIpcServer.registerChannel('webview', webviewChannel); @@ -579,8 +655,10 @@ export class CodeApplication extends Disposable { electronIpcServer.registerChannel('logger', loggerChannel); sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel)); - // ExtensionHost Debug broadcast service const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); + fileProtocolHandler.injectWindowsMainService(windowsMainService); + + // ExtensionHost Debug broadcast service electronIpcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ElectronExtensionHostDebugBroadcastChannel(windowsMainService)); // Signal phase: ready (services set) @@ -591,29 +669,32 @@ export class CodeApplication extends Disposable { // Check for initial URLs to handle from protocol link invocations const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = []; - const pendingProtocolLinksToHandle = coalesce([ - + const pendingProtocolLinksToHandle = [ // Windows/Linux: protocol handler invokes CLI with --open-url ...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [], // macOS: open-url events ...((global).getOpenUrls() || []) as string[] - ].map(pendingUrlToHandle => { + ].map(url => { try { - return URI.parse(pendingUrlToHandle); - } catch (error) { - return undefined; + return { uri: URI.parse(url), url }; + } catch { + return null; } - })).filter(pendingUriToHandle => { - // if URI should be blocked, filter it out - if (this.shouldBlockURI(pendingUriToHandle)) { + }).filter((obj): obj is { uri: URI, url: string } => { + if (!obj) { return false; } - // filter out any protocol link that wants to open as window so that + // If URI should be blocked, filter it out + if (this.shouldBlockURI(obj.uri)) { + return false; + } + + // Filter out any protocol link that wants to open as window so that // we open the right set of windows on startup and not restore the // previous workspace too. - const windowOpenable = this.getWindowOpenableFromProtocolLink(pendingUriToHandle); + const windowOpenable = this.getWindowOpenableFromProtocolLink(obj.uri); if (windowOpenable) { pendingWindowOpenablesFromProtocolLinks.push(windowOpenable); @@ -627,8 +708,9 @@ export class CodeApplication extends Disposable { const app = this; const environmentService = this.environmentService; urlService.registerHandler({ - async handleURL(uri: URI): Promise { - // if URI should be blocked, behave as if it's handled + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { + + // If URI should be blocked, behave as if it's handled if (app.shouldBlockURI(uri)) { return true; } @@ -658,7 +740,7 @@ export class CodeApplication extends Disposable { await window.ready(); - return urlService.open(uri); + return urlService.open(uri, options); } return false; @@ -666,11 +748,11 @@ export class CodeApplication extends Disposable { }); // Create a URL handler which forwards to the last active window - const activeWindowManager = new ActiveWindowManager({ + const activeWindowManager = this._register(new ActiveWindowManager({ onDidOpenWindow: nativeHostMainService.onDidOpenWindow, onDidFocusWindow: nativeHostMainService.onDidFocusWindow, getActiveWindowId: () => nativeHostMainService.getActiveWindowId(-1) - }); + })); const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter); const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', urlHandlerRouter); @@ -682,7 +764,7 @@ export class CodeApplication extends Disposable { // Open our first window const args = this.environmentService.args; const macOpenFiles: string[] = (global).macOpenFiles; - const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; + const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP; const hasCliArgs = args._.length; const hasFolderURIs = !!args['folder-uri']; const hasFileURIs = !!args['file-uri']; @@ -828,12 +910,14 @@ export class CodeApplication extends Disposable { updateService.initialize(); } + // Start to fetch shell environment (if needed) after window has opened + resolveShellEnv(this.logService, this.environmentService.args, process.env); + // If enable-crash-reporter argv is undefined then this is a fresh start, // based on telemetry.enableCrashreporter settings, generate a UUID which // will be used as crash reporter id and also update the json file. try { - const fileService = accessor.get(IFileService); - const argvContent = await fileService.readFile(this.environmentService.argvResource); + const argvContent = await this.fileService.readFile(this.environmentService.argvResource); const argvString = argvContent.value.toString(); const argvJSON = JSON.parse(stripComments(argvString)); if (argvJSON['enable-crash-reporter'] === undefined) { @@ -850,14 +934,11 @@ export class CodeApplication extends Disposable { '}' ]; const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n')); - await fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); + await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); } } catch (error) { this.logService.error(error); } - - // Start to fetch shell environment after window has opened - getShellEnvironment(this.logService, this.environmentService); } private handleRemoteAuthorities(): void { diff --git a/src/vs/code/electron-main/auth2.ts b/src/vs/code/electron-main/auth2.ts index 1bce0445..1b84d4bc 100644 --- a/src/vs/code/electron-main/auth2.ts +++ b/src/vs/code/electron-main/auth2.ts @@ -13,6 +13,7 @@ import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeH import { IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; import { generateUuid } from 'vs/base/common/uuid'; import product from 'vs/platform/product/common/product'; +import { CancellationToken } from 'vs/base/common/cancellation'; interface ElectronAuthenticationResponseDetails extends AuthenticationResponseDetails { firstAuthAttempt?: boolean; // https://github.com/electron/electron/blob/84a42a050e7d45225e69df5bd2d2bf9f1037ea41/shell/browser/login_handler.cc#L70 @@ -192,7 +193,7 @@ export class ProxyAuthHandler2 extends Disposable { password: this.sessionCredentials?.password ?? storedPassword, // prefer to show already used password (if any) over stored replyChannel: `vscode:proxyAuthResponse:${generateUuid()}` }; - window.sendWhenReady('vscode:openProxyAuthenticationDialog', payload); + window.sendWhenReady('vscode:openProxyAuthenticationDialog', CancellationToken.None, payload); this.state = ProxyAuthState.LoginDialogShown; // Handle reply diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 10f4a19f..783ac6fb 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -200,7 +200,7 @@ class CodeMain { VSCODE_IPC_HOOK: environmentService.mainIPCHandle }; - ['VSCODE_NLS_CONFIG', 'VSCODE_LOGS', 'VSCODE_PORTABLE'].forEach(key => { + ['VSCODE_NLS_CONFIG', 'VSCODE_PORTABLE'].forEach(key => { const value = process.env[key]; if (typeof value === 'string') { instanceEnvironment[key] = value; @@ -343,15 +343,7 @@ class CodeMain { private handleStartupDataDirError(environmentService: IEnvironmentMainService, error: NodeJS.ErrnoException): void { if (error.code === 'EACCES' || error.code === 'EPERM') { - const directories = [environmentService.userDataPath]; - - if (environmentService.extensionsPath) { - directories.push(environmentService.extensionsPath); - } - - if (XDG_RUNTIME_DIR) { - directories.push(XDG_RUNTIME_DIR); - } + const directories = coalesce([environmentService.userDataPath, environmentService.extensionsPath, XDG_RUNTIME_DIR]); this.showStartupWarningDialog( localize('startupDataDirError', "Unable to write program user data."), diff --git a/src/vs/code/electron-main/protocol.ts b/src/vs/code/electron-main/protocol.ts new file mode 100644 index 00000000..36d09319 --- /dev/null +++ b/src/vs/code/electron-main/protocol.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { session } from 'electron'; +import { ILogService } from 'vs/platform/log/common/log'; +import { TernarySearchTree } from 'vs/base/common/map'; +import { isLinux } from 'vs/base/common/platform'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; + +type ProtocolCallback = { (result: string | Electron.FilePathWithHeaders | { error: number }): void }; + +export class FileProtocolHandler extends Disposable { + + private readonly validRoots = TernarySearchTree.forUris(() => !isLinux); + + constructor( + @INativeEnvironmentService environmentService: INativeEnvironmentService, + @ILogService private readonly logService: ILogService + ) { + super(); + + const { defaultSession } = session; + + // Define an initial set of roots we allow loading from + // - appRoot : all files installed as part of the app + // - extensions : all files shipped from extensions + this.validRoots.set(URI.file(environmentService.appRoot), true); + this.validRoots.set(URI.file(environmentService.extensionsPath), true); + + // Register vscode-file:// handler + defaultSession.protocol.registerFileProtocol(Schemas.vscodeFileResource, (request, callback) => this.handleResourceRequest(request, callback as unknown as ProtocolCallback)); + + // Block any file:// access (sandbox only) + if (environmentService.args.__sandbox) { + defaultSession.protocol.interceptFileProtocol(Schemas.file, (request, callback) => this.handleFileRequest(request, callback as unknown as ProtocolCallback)); + } + + // Cleanup + this._register(toDisposable(() => { + defaultSession.protocol.unregisterProtocol(Schemas.vscodeFileResource); + if (environmentService.args.__sandbox) { + defaultSession.protocol.uninterceptProtocol(Schemas.file); + } + })); + } + + injectWindowsMainService(windowsMainService: IWindowsMainService): void { + this._register(windowsMainService.onWindowReady(window => { + if (window.config?.extensionDevelopmentPath || window.config?.extensionTestsPath) { + const disposables = new DisposableStore(); + disposables.add(Event.any(window.onClose, window.onDestroy)(() => disposables.dispose())); + + // Allow access to extension development path + if (window.config.extensionDevelopmentPath) { + for (const extensionDevelopmentPath of window.config.extensionDevelopmentPath) { + disposables.add(this.addValidRoot(URI.file(extensionDevelopmentPath))); + } + } + + // Allow access to extension tests path + if (window.config.extensionTestsPath) { + disposables.add(this.addValidRoot(URI.file(window.config.extensionTestsPath))); + } + } + })); + } + + private addValidRoot(root: URI): IDisposable { + if (!this.validRoots.get(root)) { + this.validRoots.set(root, true); + + return toDisposable(() => this.validRoots.delete(root)); + } + + return Disposable.None; + } + + private async handleFileRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) { + const uri = URI.parse(request.url); + + this.logService.error(`Refused to load resource ${uri.fsPath} from ${Schemas.file}: protocol`); + callback({ error: -3 /* ABORTED */ }); + } + + private async handleResourceRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback) { + const uri = URI.parse(request.url); + + // Restore the `vscode-file` URI to a `file` URI so that we can + // ensure the root is valid and properly tell Chrome where the + // resource is at. + const fileUri = FileAccess.asFileUri(uri); + if (this.validRoots.findSubstr(fileUri)) { + return callback({ + path: fileUri.fsPath + }); + } + + this.logService.error(`${Schemas.vscodeFileResource}: Refused to load resource ${fileUri.fsPath}}`); + callback({ error: -3 /* ABORTED */ }); + } +} diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index c513c3c6..dd721db9 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -60,8 +60,9 @@ export class SharedProcess implements ISharedProcess { windowId: this.window.id }; - const windowUrl = FileAccess - .asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) + const windowUrl = (this.environmentService.sandbox ? + FileAccess._asCodeFileUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) : + FileAccess.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require)) .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }); this.window.loadURL(windowUrl.toString(true)); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 79e1658c..a9f47be8 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; import * as path from 'vs/base/common/path'; -import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; +import * as perf from 'vs/base/common/performance'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, Details } from 'electron'; @@ -23,7 +24,6 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import * as perf from 'vs/base/common/performance'; import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -33,8 +33,10 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { IFileService } from 'vs/platform/files/common/files'; +import { ByteSize, IFileService } from 'vs/platform/files/common/files'; import { FileAccess, Schemas } from 'vs/base/common/network'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IWindowCreationOptions { state: IWindowState; @@ -84,7 +86,7 @@ const enum ReadyState { export class CodeWindow extends Disposable implements ICodeWindow { - private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 + private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 private readonly _onLoad = this._register(new Emitter()); readonly onLoad = this._onLoad.event; @@ -110,8 +112,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[]; - private pendingLoadConfig?: INativeWindowConfiguration; - private marketplaceHeadersPromise: Promise; private readonly touchBarGroups: TouchBarSegmentedControl[]; @@ -150,7 +150,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const options: BrowserWindowConstructorOptions = { width: this.windowState.width, @@ -211,7 +211,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs } - const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService, !!config.extensionDevelopmentPath) === 'custom'; + const useCustomTitleStyle = getTitleBarStyle(this.configurationService) === 'custom'; if (useCustomTitleStyle) { options.titleBarStyle = 'hidden'; this.hiddenTitleBarStyle = true; @@ -285,6 +285,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.registerListeners(); } + private pendingLoadConfig: INativeWindowConfiguration | undefined; + private currentConfig: INativeWindowConfiguration | undefined; get config(): INativeWindowConfiguration | undefined { return this.currentConfig; } @@ -296,11 +298,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } - get isExtensionDevelopmentHost(): boolean { return !!(this.config && this.config.extensionDevelopmentPath); } + get isExtensionDevelopmentHost(): boolean { return !!(this.currentConfig?.extensionDevelopmentPath); } - get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); } + get isExtensionTestHost(): boolean { return !!(this.currentConfig?.extensionTestsPath); } - get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.config?.debugId; } + get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.currentConfig?.debugId; } setRepresentedFilename(filename: string): void { if (isMacintosh) { @@ -468,9 +470,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { return; // disposed } - // Notify renderers about displays changed - this.sendWhenReady('vscode:displayChanged'); - // Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround // we need to detect when display metrics change or displays are added/removed and toggle the // fullscreen manually. @@ -520,11 +519,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Window Fullscreen this._win.on('enter-full-screen', () => { - this.sendWhenReady('vscode:enterFullScreen'); + this.sendWhenReady('vscode:enterFullScreen', CancellationToken.None); }); this._win.on('leave-full-screen', () => { - this.sendWhenReady('vscode:leaveFullScreen'); + this.sendWhenReady('vscode:leaveFullScreen', CancellationToken.None); }); // Window Failed to load @@ -680,6 +679,16 @@ export class CodeWindow extends Disposable implements ICodeWindow { load(config: INativeWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void { + // If this window was loaded before from the command line + // (as indicated by VSCODE_CLI environment), make sure to + // preserve that user environment in subsequent loads, + // unless the new configuration context was also a CLI + // (for https://github.com/microsoft/vscode/issues/108571) + const currentUserEnv = (this.currentConfig ?? this.pendingLoadConfig)?.userEnv; + if (currentUserEnv && isLaunchedFromCli(currentUserEnv) && !isLaunchedFromCli(config.userEnv)) { + config.userEnv = { ...currentUserEnv, ...config.userEnv }; // still allow to override certain environment as passed in + } + // If this is the first time the window is loaded, we associate the paths // directly with the window because we assume the loading will just work if (this._readyState === ReadyState.NONE) { @@ -738,10 +747,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._onLoad.fire(); } - reload(configurationIn?: INativeWindowConfiguration, cli?: NativeParsedArgs): void { + reload(cli?: NativeParsedArgs): void { - // If config is not provided, copy our current one - const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig); + // Copy our current config for reuse + const configuration = Object.assign({}, this.currentConfig); // Delete some properties we do not want during reload delete configuration.filesToOpenOrCreate; @@ -773,7 +782,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.logLevel = this.logService.getLevel(); // Set zoomlevel - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const zoomLevel = windowConfig?.zoomLevel; if (typeof zoomLevel === 'number') { windowConfiguration.zoomLevel = zoomLevel; @@ -799,6 +808,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Parts splash windowConfiguration.partsSplashPath = path.join(this.environmentService.userDataPath, 'rapid_render.json'); + // OS Info + windowConfiguration.os = { + release: os.release() + }; + // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv, OPTIONS); const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown }; @@ -814,10 +828,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { // large depending on user configuration, so we can only remove it in that case. let configUrl = this.doGetUrl(config); if (configUrl.length > CodeWindow.MAX_URL_LENGTH) { - delete config.userEnv; this.logService.warn('Application URL exceeds maximum of 2MB and was shortened.'); - configUrl = this.doGetUrl(config); + configUrl = this.doGetUrl({ ...config, userEnv: undefined }); if (configUrl.length > CodeWindow.MAX_URL_LENGTH) { this.logService.error('Application URL exceeds maximum of 2MB and cannot be loaded.'); @@ -835,10 +848,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { workbench = 'vs/code/electron-browser/workbench/workbench.html'; } - return FileAccess - .asBrowserUri(workbench, require) - .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) - .toString(true); + return (this.environmentService.sandbox ? + FileAccess._asCodeFileUri(workbench, require) : + FileAccess.asBrowserUri(workbench, require)) + .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }).toString(true); } serializeWindowState(): IWindowState { @@ -1088,7 +1101,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Events - this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen'); + this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen', CancellationToken.None); // Respect configured menu bar visibility or default to toggle if not set if (this.currentMenuBarVisibility) { @@ -1116,7 +1129,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private useNativeFullScreen(): boolean { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); if (!windowConfig || typeof windowConfig.nativeFullScreen !== 'boolean') { return true; // default } @@ -1133,7 +1146,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private getMenuBarVisibility(): MenuBarVisibility { - let menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService, !!this.config?.extensionDevelopmentPath); + let menuBarVisibility = getMenuBarVisibility(this.configurationService); if (['visible', 'toggle', 'hidden'].indexOf(menuBarVisibility) < 0) { menuBarVisibility = 'default'; } @@ -1229,11 +1242,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - sendWhenReady(channel: string, ...args: any[]): void { + sendWhenReady(channel: string, token: CancellationToken, ...args: any[]): void { if (this.isReady) { this.send(channel, ...args); } else { - this.ready().then(() => this.send(channel, ...args)); + this.ready().then(() => { + if (!token.isCancellationRequested) { + this.send(channel, ...args); + } + }); } } @@ -1283,7 +1300,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { mode: 'buttons', segmentStyle: 'automatic', change: (selectedIndex) => { - this.sendWhenReady('vscode:runAction', { id: (control.segments[selectedIndex] as ITouchBarSegment).id, from: 'touchbar' }); + this.sendWhenReady('vscode:runAction', CancellationToken.None, { id: (control.segments[selectedIndex] as ITouchBarSegment).id, from: 'touchbar' }); } }); diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index f48450b2..252260a5 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -150,6 +150,7 @@ export class IssueReporter extends Disposable { applyZoom(configuration.data.zoomLevel); this.applyStyles(configuration.data.styles); this.handleExtensionData(configuration.data.enabledExtensions); + this.updateExperimentsInfo(configuration.data.experiments); } render(): void { @@ -285,7 +286,7 @@ export class IssueReporter extends Disposable { this.render(); }); - (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'] as const).forEach(elementId => { + (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeExperiments'] as const).forEach(elementId => { this.addEventListener(elementId, 'click', (event: Event) => { event.stopPropagation(); this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] }); @@ -693,8 +694,7 @@ export class IssueReporter extends Disposable { const processBlock = document.querySelector('.block-process'); const workspaceBlock = document.querySelector('.block-workspace'); const extensionsBlock = document.querySelector('.block-extensions'); - const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions'); - const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults'); + const experimentsBlock = document.querySelector('.block-experiments'); const problemSource = this.getElementById('problem-source')!; const descriptionTitle = this.getElementById('issue-description-label')!; @@ -707,8 +707,7 @@ export class IssueReporter extends Disposable { hide(processBlock); hide(workspaceBlock); hide(extensionsBlock); - hide(searchedExtensionsBlock); - hide(settingsSearchResultsBlock); + hide(experimentsBlock); hide(problemSource); hide(extensionSelector); @@ -716,6 +715,7 @@ export class IssueReporter extends Disposable { show(blockContainer); show(systemBlock); show(problemSource); + show(experimentsBlock); if (fileOnExtension) { show(extensionSelector); @@ -730,6 +730,7 @@ export class IssueReporter extends Disposable { show(processBlock); show(workspaceBlock); show(problemSource); + show(experimentsBlock); if (fileOnExtension) { show(extensionSelector); @@ -1084,6 +1085,14 @@ export class IssueReporter extends Disposable { } } + private updateExperimentsInfo(experimentInfo: string | undefined) { + this.issueReporterModel.update({ experimentInfo }); + const target = document.querySelector('.block-experiments .block-info'); + if (target) { + target.textContent = experimentInfo ? experimentInfo : localize('noCurrentExperiments', "No current experiments."); + } + } + private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): HTMLTableElement { return $('table', undefined, $('tr', undefined, diff --git a/src/vs/code/electron-sandbox/issue/issueReporterModel.ts b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts index b529e48c..f850e4d8 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterModel.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts @@ -19,8 +19,7 @@ export interface IssueReporterData { includeWorkspaceInfo: boolean; includeProcessInfo: boolean; includeExtensions: boolean; - includeSearchedExtensions: boolean; - includeSettingsSearchDetails: boolean; + includeExperiments: boolean; numberOfThemeExtesions?: number; allExtensions: IssueReporterExtensionData[]; @@ -31,6 +30,7 @@ export interface IssueReporterData { actualSearchResults?: ISettingSearchResult[]; query?: string; filterResultCount?: number; + experimentInfo?: string; } export class IssueReporterModel { @@ -43,8 +43,7 @@ export class IssueReporterModel { includeWorkspaceInfo: true, includeProcessInfo: true, includeExtensions: true, - includeSearchedExtensions: true, - includeSettingsSearchDetails: true, + includeExperiments: true, allExtensions: [] }; @@ -133,6 +132,12 @@ ${this.getInfos()} } } + if (this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue) { + if (this._data.includeExperiments && this._data.experimentInfo) { + info += this.generateExperimentsInfoMd(); + } + } + return info; } @@ -150,7 +155,7 @@ ${this.getInfos()} |GPU Status|${Object.keys(this._data.systemInfo.gpuStatus).map(key => `${key}: ${this._data.systemInfo!.gpuStatus[key]}`).join('
')}| |Load (avg)|${this._data.systemInfo.load}| |Memory (System)|${this._data.systemInfo.memory}| -|Process Argv|${this._data.systemInfo.processArgs}| +|Process Argv|${this._data.systemInfo.processArgs.replace(/\\/g, '\\\\')}| |Screen Reader|${this._data.systemInfo.screenReader}| |VM|${this._data.systemInfo.vmHint}|`; @@ -203,6 +208,18 @@ ${this._data.processInfo} ${this._data.workspaceInfo}; \`\`\` + +`; + } + + private generateExperimentsInfoMd(): string { + return `
+A/B Experiments + +\`\`\` +${this._data.experimentInfo} +\`\`\` +
`; } diff --git a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts index d7be46ae..bf3c1353 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts @@ -111,25 +111,15 @@ export default (): string => ` -
- - - -
- - - +
`; diff --git a/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts b/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts index b09f4fb7..215f6649 100644 --- a/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts +++ b/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts @@ -18,8 +18,7 @@ suite('IssueReporter', () => { includeWorkspaceInfo: true, includeProcessInfo: true, includeExtensions: true, - includeSearchedExtensions: true, - includeSettingsSearchDetails: true, + includeExperiments: true, issueType: 0 }); }); @@ -78,6 +77,58 @@ OS version: undefined |Screen Reader|no| |VM|0%| Extensions: none +`); + }); + + test('serializes experiment info when data is provided', () => { + const issueReporterModel = new IssueReporterModel({ + issueType: 0, + systemInfo: { + os: 'Darwin', + cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)', + memory: '16.00GB', + vmHint: '0%', + processArgs: '', + screenReader: 'no', + remoteData: [], + gpuStatus: { + '2d_canvas': 'enabled', + 'checker_imaging': 'disabled_off' + } + }, + experimentInfo: 'vsliv695:30137379\nvsins829:30139715' + }); + assert.equal(issueReporterModel.serialize(), + ` +Issue Type: Bug + +undefined + +VS Code version: undefined +OS version: undefined + +
+System Info + +|Item|Value| +|---|---| +|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)| +|GPU Status|2d_canvas: enabled
checker_imaging: disabled_off| +|Load (avg)|undefined| +|Memory (System)|16.00GB| +|Process Argv|| +|Screen Reader|no| +|VM|0%| +
Extensions: none
+A/B Experiments + +\`\`\` +vsliv695:30137379 +vsins829:30139715 +\`\`\` + +
+ `); }); @@ -191,6 +242,45 @@ Remote OS version: Linux x64 4.18.0 `); }); + test('escapes backslashes in processArgs', () => { + const issueReporterModel = new IssueReporterModel({ + issueType: 0, + systemInfo: { + os: 'Darwin', + cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)', + memory: '16.00GB', + vmHint: '0%', + processArgs: '\\\\HOST\\path', + screenReader: 'no', + remoteData: [], + gpuStatus: {} + } + }); + assert.equal(issueReporterModel.serialize(), + ` +Issue Type: Bug + +undefined + +VS Code version: undefined +OS version: undefined + +
+System Info + +|Item|Value| +|---|---| +|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)| +|GPU Status|| +|Load (avg)|undefined| +|Memory (System)|16.00GB| +|Process Argv|\\\\\\\\HOST\\\\path| +|Screen Reader|no| +|VM|0%| +
Extensions: none +`); + }); + test('should normalize GitHub urls', () => { [ 'https://github.com/repo', diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index 947a6e9f..ef93a1c5 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -19,6 +19,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; +import { ByteSize } from 'vs/platform/files/common/files'; const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/; const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; @@ -67,18 +68,19 @@ class ProcessExplorer { private getProcessList(rootProcess: ProcessItem, isLocal: boolean, totalMem: number): FormattedProcessItem[] { const processes: FormattedProcessItem[] = []; + const handledProcesses = new Set(); if (rootProcess) { - this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem); + this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem, handledProcesses); } return processes; } - private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number): void { + private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number, handledProcesses: Set): void { const isRoot = (indent === 0); - const MB = 1024 * 1024; + handledProcesses.add(item.pid); let name = item.name; if (isRoot) { @@ -95,7 +97,7 @@ class ProcessExplorer { const memory = this.data.platform === 'win32' ? item.mem : (totalMem * (item.mem / 100)); processes.push({ cpu: item.load, - memory: (memory / MB), + memory: (memory / ByteSize.MB), pid: item.pid.toFixed(0), name, formattedName, @@ -105,9 +107,11 @@ class ProcessExplorer { // Recurse into children if any if (Array.isArray(item.children)) { item.children.forEach(child => { - if (child) { - this.getProcessItem(processes, child, indent + 1, isLocal, totalMem); + if (!child || handledProcesses.has(child.pid)) { + return; // prevent loops } + + this.getProcessItem(processes, child, indent + 1, isLocal, totalMem, handledProcesses); }); } } diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js index 3966a9d0..6a46ef2b 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -9,15 +9,12 @@ 'use strict'; (function () { + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top - const perf = perfLib(); + const perf = bootstrapWindow.perfLib(); perf.mark('renderer/started'); - // Load environment in parallel to workbench loading to avoid waterfall - const bootstrapWindow = bootstrapWindowLib(); - const whenEnvResolved = bootstrapWindow.globals().process.whenEnvResolved(); - // Load workbench main JS, CSS and NLS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because // we know for a fact that workbench.desktop.sandbox.main will depend on @@ -31,12 +28,6 @@ // Mark start of workbench perf.mark('didLoadWorkbenchMain'); - performance.mark('workbench-start'); - - // Wait for process environment being fully resolved - await whenEnvResolved; - - perf.mark('main/startup'); // @ts-ignore return require('vs/workbench/electron-sandbox/desktop.main').main(configuration); @@ -58,23 +49,11 @@ //region Helpers - function perfLib() { - globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; - - return { - /** - * @param {string} name - */ - mark(name) { - globalThis.MonacoPerformanceMarks.push(name, Date.now()); - } - }; - } - /** * @returns {{ * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, - * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals'), + * perfLib: () => { mark: (name: string) => void } * }} */ function bootstrapWindowLib() { diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 040144f4..c6415a55 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -123,10 +123,6 @@ export async function main(argv: string[]): Promise { 'ELECTRON_NO_ATTACH_CONSOLE': '1' }; - if (args['force-user-env']) { - env['VSCODE_FORCE_USER_ENV'] = '1'; - } - delete env['ELECTRON_RUN_AS_NODE']; const processCallbacks: ((child: ChildProcess) => Promise)[] = []; @@ -157,41 +153,39 @@ export async function main(argv: string[]): Promise { // stdin. We do this because there is no reliable way to find out if data is piped to stdin. Just // checking for stdin being connected to a TTY is not enough (https://github.com/microsoft/vscode/issues/40351) - if (args._.length === 0) { - if (hasReadStdinArg) { - stdinFilePath = getStdinFilePath(); + if (hasReadStdinArg) { + stdinFilePath = getStdinFilePath(); - // returns a file path where stdin input is written into (write in progress). - try { - readFromStdin(stdinFilePath, !!verbose); // throws error if file can not be written + // returns a file path where stdin input is written into (write in progress). + try { + readFromStdin(stdinFilePath, !!verbose); // throws error if file can not be written - // Make sure to open tmp file - addArg(argv, stdinFilePath); + // Make sure to open tmp file + addArg(argv, stdinFilePath); - // Enable --wait to get all data and ignore adding this to history - addArg(argv, '--wait'); - addArg(argv, '--skip-add-to-recently-opened'); - args.wait = true; + // Enable --wait to get all data and ignore adding this to history + addArg(argv, '--wait'); + addArg(argv, '--skip-add-to-recently-opened'); + args.wait = true; - console.log(`Reading from stdin via: ${stdinFilePath}`); - } catch (e) { - console.log(`Failed to create file to read via stdin: ${e.toString()}`); - stdinFilePath = undefined; - } - } else { - - // If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message - // if we detect that data flows into via stdin after a certain timeout. - processCallbacks.push(_ => stdinDataListener(1000).then(dataReceived => { - if (dataReceived) { - if (isWindows) { - console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`); - } else { - console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`); - } - } - })); + console.log(`Reading from stdin via: ${stdinFilePath}`); + } catch (e) { + console.log(`Failed to create file to read via stdin: ${e.toString()}`); + stdinFilePath = undefined; } + } else { + + // If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message + // if we detect that data flows into via stdin after a certain timeout. + processCallbacks.push(_ => stdinDataListener(1000).then(dataReceived => { + if (dataReceived) { + if (isWindows) { + console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`); + } else { + console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`); + } + } + })); } } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 426cde72..84fa06d7 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -31,7 +31,7 @@ import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { getBaseLabel } from 'vs/base/common/labels'; import { IStateService } from 'vs/platform/state/node/state'; import { StateService } from 'vs/platform/state/node/stateService'; -import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; +import { ILogService, getLogLevel, LogLevel, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { URI } from 'vs/base/common/uri'; @@ -78,7 +78,7 @@ export class Main { @IInstantiationService private readonly instantiationService: IInstantiationService, @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, ) { } async run(argv: NativeParsedArgs): Promise { @@ -93,7 +93,7 @@ export class Main { } else if (argv['locate-extension']) { await this.locateExtension(argv['locate-extension']); } else if (argv['telemetry']) { - console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath ? this.environmentService.extensionsPath : undefined)); + console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath)); } } @@ -126,13 +126,28 @@ export class Main { extensions.forEach(e => console.log(getId(e.manifest, showVersions))); } - private async installExtensions(extensions: string[], builtinExtensionIds: string[], isMachineScoped: boolean, force: boolean): Promise { + async installExtensions(extensions: string[], builtinExtensionIds: string[], isMachineScoped: boolean, force: boolean): Promise { const failed: string[] = []; const installedExtensionsManifests: IExtensionManifest[] = []; if (extensions.length) { console.log(localize('installingExtensions', "Installing extensions...")); } + const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); + const checkIfNotInstalled = (id: string, version?: string): boolean => { + const installedExtension = installed.find(i => areSameExtensions(i.identifier, { id })); + if (installedExtension) { + if (!version && !force) { + console.log(localize('alreadyInstalled-checkAndUpdate', "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@' to install a specific version, for example: '{2}@1.2.3'.", id, installedExtension.manifest.version, id)); + return false; + } + if (version && installedExtension.manifest.version === version) { + console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", `${id}@${version}`)); + return false; + } + } + return true; + }; const vsixs: string[] = []; const installExtensionInfos: InstallExtensionInfo[] = []; for (const extension of extensions) { @@ -140,12 +155,16 @@ export class Main { vsixs.push(extension); } else { const [id, version] = getIdAndVersion(extension); - installExtensionInfos.push({ id, version, installOptions: { isBuiltin: false, isMachineScoped } }); + if (checkIfNotInstalled(id, version)) { + installExtensionInfos.push({ id, version, installOptions: { isBuiltin: false, isMachineScoped } }); + } } } for (const extension of builtinExtensionIds) { const [id, version] = getIdAndVersion(extension); - installExtensionInfos.push({ id, version, installOptions: { isBuiltin: true, isMachineScoped: false } }); + if (checkIfNotInstalled(id, version)) { + installExtensionInfos.push({ id, version, installOptions: { isBuiltin: true, isMachineScoped: false } }); + } } if (vsixs.length) { @@ -162,28 +181,29 @@ export class Main { })); } - const [galleryExtensions, installed] = await Promise.all([ - this.getGalleryExtensions(installExtensionInfos), - this.extensionManagementService.getInstalled(ExtensionType.User) - ]); + if (installExtensionInfos.length) { - await Promise.all(installExtensionInfos.map(async extensionInfo => { - const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase()); - if (gallery) { - try { - const manifest = await this.installFromGallery(extensionInfo, gallery, installed, force); - if (manifest) { - installedExtensionsManifests.push(manifest); + const galleryExtensions = await this.getGalleryExtensions(installExtensionInfos); + + await Promise.all(installExtensionInfos.map(async extensionInfo => { + const gallery = galleryExtensions.get(extensionInfo.id.toLowerCase()); + if (gallery) { + try { + const manifest = await this.installFromGallery(extensionInfo, gallery, installed, force); + if (manifest) { + installedExtensionsManifests.push(manifest); + } + } catch (err) { + console.error(err.message || err.stack || err); + failed.push(extensionInfo.id); } - } catch (err) { - console.error(err.message || err.stack || err); + } else { + console.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`); failed.push(extensionInfo.id); } - } else { - console.error(`${notFound(extensionInfo.version ? `${extensionInfo.id}@${extensionInfo.version}` : extensionInfo.id)}\n${useId}`); - failed.push(extensionInfo.id); - } - })); + })); + + } if (installedExtensionsManifests.some(manifest => isLanguagePackExtension(manifest))) { await this.updateLocalizationsCache(); @@ -244,10 +264,6 @@ export class Main { console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id)); return null; } - if (!version && !force) { - console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, galleryExtension.version)); - return null; - } console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, galleryExtension.version)); } @@ -315,7 +331,7 @@ export class Main { return; } console.log(localize('uninstalling', "Uninstalling {0}...", id)); - await this.extensionManagementService.uninstall(extensionToUninstall, true); + await this.extensionManagementService.uninstall(extensionToUninstall); uninstalledExtensions.push(extensionToUninstall); console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id)); } @@ -353,7 +369,13 @@ export async function main(argv: NativeParsedArgs): Promise { const disposables = new DisposableStore(); const environmentService = new NativeEnvironmentService(argv); - const logService: ILogService = new SpdLogService('cli', environmentService.logsPath, getLogLevel(environmentService)); + const logLevel = getLogLevel(environmentService); + const loggers: ILogService[] = []; + loggers.push(new SpdLogService('cli', environmentService.logsPath, logLevel)); + if (logLevel === LogLevel.Trace) { + loggers.push(new ConsoleLogService(logLevel)); + } + const logService = new MultiplexLogService(loggers); process.once('exit', () => logService.dispose()); logService.info('main', argv); @@ -403,7 +425,7 @@ export async function main(argv: NativeParsedArgs): Promise { appender: combinedAppender(...appenders), sendErrorTelemetry: false, commonProperties: resolveCommonProperties(product.commit, product.version, stateService.getItem('telemetry.machineId'), product.msftInternalDomains, installSourcePath), - piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] + piiPaths: [appRoot, extensionsPath] }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index 619886d0..29ef8c14 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -7,9 +7,57 @@ import { spawn } from 'child_process'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; -import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; -function getUnixShellEnvironment(logService: ILogService): Promise { +/** + * We need to get the environment from a user's shell. + * This should only be done when Code itself is not launched + * from within a shell. + */ +export async function resolveShellEnv(logService: ILogService, args: NativeParsedArgs, env: NodeJS.ProcessEnv): Promise { + + // Skip if --force-disable-user-env + if (args['force-disable-user-env']) { + logService.trace('resolveShellEnv(): skipped (--force-disable-user-env)'); + + return {}; + } + + // Skip on windows + else if (isWindows) { + logService.trace('resolveShellEnv(): skipped (Windows)'); + + return {}; + } + + // Skip if running from CLI already + else if (isLaunchedFromCli(env) && !args['force-user-env']) { + logService.trace('resolveShellEnv(): skipped (VSCODE_CLI is set)'); + + return {}; + } + + // Otherwise resolve (macOS, Linux) + else { + if (isLaunchedFromCli(env)) { + logService.trace('resolveShellEnv(): running (--force-user-env)'); + } else { + logService.trace('resolveShellEnv(): running (macOS/Linux)'); + } + + if (!unixShellEnvPromise) { + unixShellEnvPromise = doResolveUnixShellEnv(logService); + } + + return unixShellEnvPromise; + } +} + +let unixShellEnvPromise: Promise | undefined = undefined; + +async function doResolveUnixShellEnv(logService: ILogService): Promise { const promise = new Promise((resolve, reject) => { const runAsNode = process.env['ELECTRON_RUN_AS_NODE']; logService.trace('getUnixShellEnvironment#runAsNode', runAsNode); @@ -78,33 +126,11 @@ function getUnixShellEnvironment(logService: ILogService): Promise ({})); -} + try { + return await promise; + } catch (error) { + logService.error('getUnixShellEnvironment#error', toErrorMessage(error)); -let shellEnvPromise: Promise | undefined = undefined; - -/** - * We need to get the environment from a user's shell. - * This should only be done when Code itself is not launched - * from within a shell. - */ -export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise { - if (!shellEnvPromise) { - if (environmentService.args['disable-user-env-probe']) { - logService.trace('getShellEnvironment: disable-user-env-probe set, skipping'); - shellEnvPromise = Promise.resolve({}); - } else if (isWindows) { - logService.trace('getShellEnvironment: running on Windows, skipping'); - shellEnvPromise = Promise.resolve({}); - } else if (process.env['VSCODE_CLI'] === '1' && process.env['VSCODE_FORCE_USER_ENV'] !== '1') { - logService.trace('getShellEnvironment: running on CLI, skipping'); - shellEnvPromise = Promise.resolve({}); - } else { - logService.trace('getShellEnvironment: running on Unix'); - shellEnvPromise = getUnixShellEnvironment(logService); - } + return {}; // ignore any errors } - - return shellEnvPromise; } diff --git a/src/vs/code/test/electron-main/nativeHelpers.test.ts b/src/vs/code/test/electron-main/nativeHelpers.test.ts deleted file mode 100644 index 1ce46448..00000000 --- a/src/vs/code/test/electron-main/nativeHelpers.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { isWindows } from 'vs/base/common/platform'; - -suite('Windows Native Helpers', () => { - if (!isWindows) { - return; - } - - test('windows-mutex', async () => { - const mutex = await import('windows-mutex'); - assert.ok(mutex && typeof mutex.isActive === 'function', 'Unable to load windows-mutex dependency.'); - assert.ok(typeof mutex.isActive === 'function', 'Unable to load windows-mutex dependency.'); - }); - - test('windows-foreground-love', async () => { - const foregroundLove = await import('windows-foreground-love'); - assert.ok(foregroundLove && typeof foregroundLove.allowSetForegroundWindow === 'function', 'Unable to load windows-foreground-love dependency.'); - }); - - test('windows-process-tree', async () => { - const processTree = await import('windows-process-tree'); - assert.ok(processTree && typeof processTree.getProcessTree === 'function', 'Unable to load windows-process-tree dependency.'); - }); - - test('vscode-windows-ca-certs', async () => { - // @ts-ignore Windows only - const windowsCerts = await import('vscode-windows-ca-certs'); - assert.ok(windowsCerts, 'Unable to load vscode-windows-ca-certs dependency.'); - }); - - test('vscode-windows-registry', async () => { - const windowsRegistry = await import('vscode-windows-registry'); - assert.ok(windowsRegistry && typeof windowsRegistry.GetStringRegKey === 'function', 'Unable to load vscode-windows-registry dependency.'); - }); -}); diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 1e7b0929..06eecaa6 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -355,13 +355,18 @@ class MouseDownOperation extends Disposable { e.buttons, createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), - () => { + (browserEvent?: MouseEvent | KeyboardEvent) => { const position = this._findMousePosition(this._lastMouseEvent!, true); - this._viewController.emitMouseDrop({ - event: this._lastMouseEvent!, - target: (position ? this._createMouseTarget(this._lastMouseEvent!, true) : null) // Ignoring because position is unknown, e.g., Content View Zone - }); + if (browserEvent && browserEvent instanceof KeyboardEvent) { + // cancel + this._viewController.emitMouseDropCanceled(); + } else { + this._viewController.emitMouseDrop({ + event: this._lastMouseEvent!, + target: (position ? this._createMouseTarget(this._lastMouseEvent!, true) : null) // Ignoring because position is unknown, e.g., Content View Zone + }); + } this._stop(); } diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index f2be04e3..37c1d8b0 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -18,6 +18,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; import * as dom from 'vs/base/browser/dom'; +import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations'; export interface IViewZoneData { viewZoneId: string; @@ -239,6 +240,7 @@ export class HitTestContext { public readonly layoutInfo: EditorLayoutInfo; public readonly viewDomNode: HTMLElement; public readonly lineHeight: number; + public readonly stickyTabStops: boolean; public readonly typicalHalfwidthCharacterWidth: number; public readonly lastRenderData: PointerHandlerLastRenderData; @@ -251,6 +253,7 @@ export class HitTestContext { this.layoutInfo = options.get(EditorOption.layoutInfo); this.viewDomNode = viewHelper.viewDomNode; this.lineHeight = options.get(EditorOption.lineHeight); + this.stickyTabStops = options.get(EditorOption.stickyTabStops); this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; this.lastRenderData = lastRenderData; this._context = context; @@ -329,6 +332,14 @@ export class HitTestContext { return this._context.viewLayout.isAfterLines(mouseVerticalOffset); } + public isInTopPadding(mouseVerticalOffset: number): boolean { + return this._context.viewLayout.isInTopPadding(mouseVerticalOffset); + } + + public isInBottomPadding(mouseVerticalOffset: number): boolean { + return this._context.viewLayout.isInBottomPadding(mouseVerticalOffset); + } + public getVerticalOffsetForLineNumber(lineNumber: number): number { return this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber); } @@ -656,8 +667,12 @@ export class MouseTargetFactory { return null; } + if (ctx.isInTopPadding(request.mouseVerticalOffset)) { + return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(1, 1), undefined, EMPTY_CONTENT_AFTER_LINES); + } + // Check if it is below any lines and any view zones - if (ctx.isAfterLines(request.mouseVerticalOffset)) { + if (ctx.isAfterLines(request.mouseVerticalOffset) || ctx.isInBottomPadding(request.mouseVerticalOffset)) { // This most likely indicates it happened after the last view-line const lineCount = ctx.model.getLineCount(); const maxLineColumn = ctx.model.getLineMaxColumn(lineCount); @@ -998,6 +1013,17 @@ export class MouseTargetFactory { }; } + private static _snapToSoftTabBoundary(position: Position, viewModel: IViewModel): Position { + const minColumn = viewModel.getLineMinColumn(position.lineNumber); + const lineContent = viewModel.getLineContent(position.lineNumber); + const { tabSize } = viewModel.getTextModelOptions(); + const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, position.column - minColumn, tabSize, Direction.Nearest); + if (newPosition !== -1) { + return new Position(position.lineNumber, newPosition + minColumn); + } + return position; + } + private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): IHitTestResult { // State of the art (18.10.2012): // The spec says browsers should support document.caretPositionFromPoint, but nobody implemented it (http://dev.w3.org/csswg/cssom-view/) @@ -1016,24 +1042,24 @@ export class MouseTargetFactory { // Thank you browsers for making this so 'easy' :) + let result: IHitTestResult; if (typeof document.caretRangeFromPoint === 'function') { - - return this._doHitTestWithCaretRangeFromPoint(ctx, request); - + result = this._doHitTestWithCaretRangeFromPoint(ctx, request); } else if ((document).caretPositionFromPoint) { - - return this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates()); - + result = this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates()); } else if ((document.body).createTextRange) { - - return this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates()); - + result = this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates()); + } else { + result = { + position: null, + hitTarget: null + }; } - - return { - position: null, - hitTarget: null - }; + // Snap to the nearest soft tab boundary if atomic soft tabs are enabled. + if (result.position && ctx.stickyTabStops) { + result.position = this._snapToSoftTabBoundary(result.position, ctx.model); + } + return result; } } @@ -1047,7 +1073,7 @@ export function shadowCaretRangeFromPoint(shadowRoot: ShadowRoot, x: number, y: // Get the last child of the element until its firstChild is a text node // This assumes that the pointer is on the right of the line, out of the tokens // and that we want to get the offset of the last token of the line - while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE) { + while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE && el.lastChild && el.lastChild.firstChild) { el = el.lastChild; } diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 83fd67d6..b86fd84f 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -182,7 +182,7 @@ export class PointerEventHandler extends MouseHandler { } public _onMouseDown(e: EditorMouseEvent): void { - if (e.target && this.viewHelper.linesContentDomNode.contains(e.target) && this._lastPointerType === 'touch') { + if ((e.browserEvent as any).pointerType === 'touch') { return; } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 07eadee8..2f26618a 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -276,6 +276,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME} ime-input`); this._viewController.compositionStart(); + this._context.model.onCompositionStart(); })); this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => { @@ -297,6 +298,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setClassName(`inputarea ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); this._viewController.compositionEnd(); + this._context.model.onCompositionEnd(); })); this._register(this._textAreaInput.onFocus(() => { diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index e2d42baf..bc1e26c1 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -295,9 +295,12 @@ export class TextAreaInput extends Disposable { this._onType.fire(typeInput); } - // Due to isEdgeOrIE (where the textarea was not cleared initially) and isChrome (the textarea is not updated correctly when composition ends) + // Due to + // isEdgeOrIE (where the textarea was not cleared initially) + // and isChrome (the textarea is not updated correctly when composition ends) + // and isFirefox (the textare ais not updated correctly after inserting emojis) // we cannot assume the text at the end consists only of the composited text - if (browser.isEdge || browser.isChrome) { + if (browser.isEdge || browser.isChrome || browser.isFirefox) { this._textAreaState = TextAreaState.readFromTextArea(this._textArea); } diff --git a/src/vs/editor/browser/core/markdownRenderer.ts b/src/vs/editor/browser/core/markdownRenderer.ts index 375518d2..e1eb76c8 100644 --- a/src/vs/editor/browser/core/markdownRenderer.ts +++ b/src/vs/editor/browser/core/markdownRenderer.ts @@ -38,8 +38,8 @@ export class MarkdownRenderer { } }); - private readonly _onDidRenderCodeBlock = new Emitter(); - readonly onDidRenderCodeBlock = this._onDidRenderCodeBlock.event; + private readonly _onDidRenderAsync = new Emitter(); + readonly onDidRenderAsync = this._onDidRenderAsync.event; constructor( private readonly _options: IMarkdownRendererOptions, @@ -48,7 +48,7 @@ export class MarkdownRenderer { ) { } dispose(): void { - this._onDidRenderCodeBlock.dispose(); + this._onDidRenderAsync.dispose(); } render(markdown: IMarkdownString | undefined, options?: MarkdownRenderOptions, markedOptions?: MarkedOptions): IMarkdownRenderResult { @@ -103,7 +103,7 @@ export class MarkdownRenderer { return element; }, - codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(), + asyncRenderCallback: () => this._onDidRenderAsync.fire(), actionHandler: { callback: (content) => this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError), disposeables diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 815fdf6b..43305c28 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -494,6 +494,12 @@ export interface ICodeEditor extends editorCommon.IEditor { * @event */ onMouseDrop(listener: (e: IPartialEditorMouseEvent) => void): IDisposable; + /** + * An event emitted on a "mousedropcanceled". + * @internal + * @event + */ + onMouseDropCanceled(listener: () => void): IDisposable; /** * An event emitted on a "contextmenu". * @event @@ -678,10 +684,15 @@ export interface ICodeEditor extends editorCommon.IEditor { executeCommand(source: string | null | undefined, command: editorCommon.ICommand): void; /** - * Push an "undo stop" in the undo-redo stack. + * Create an "undo stop" in the undo-redo stack. */ pushUndoStop(): boolean; + /** + * Remove the "undo stop" in the undo-redo stack. + */ + popUndoStop(): boolean; + /** * Execute edits on the editor. * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 4042ae82..3d9ffe64 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -187,7 +187,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { initialButtons: number, merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, - onStopCallback: () => void + onStopCallback: (browserEvent?: MouseEvent | KeyboardEvent) => void ): void { // Add a <> keydown event listener that will cancel the monitoring @@ -198,16 +198,16 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { // Allow modifier keys return; } - this._globalMouseMoveMonitor.stopMonitoring(true); + this._globalMouseMoveMonitor.stopMonitoring(true, e.browserEvent); }, true); const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; - this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, () => { + this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, (e) => { this._keydownListener!.dispose(); - onStopCallback(); + onStopCallback(e); }); } } diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index aaf9feaf..893c2159 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -4,8 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IPosition } from 'vs/base/browser/ui/contextview/contextview'; -import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -409,47 +407,6 @@ export abstract class EditorAction2 extends Action2 { // --- Registration of commands and actions -export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: Args) => any) { - CommandsRegistry.registerCommand(id, (accessor, args) => handler(accessor, args || {})); -} - -interface IDefaultArgs { - resource: URI; - position: IPosition; - [name: string]: any; -} - -export function registerDefaultLanguageCommand(id: string, handler: (model: ITextModel, position: Position, args: IDefaultArgs) => any) { - registerLanguageCommand(id, function (accessor, args: IDefaultArgs) { - - const { resource, position } = args; - if (!(resource instanceof URI)) { - throw illegalArgument('resource'); - } - if (!Position.isIPosition(position)) { - throw illegalArgument('position'); - } - - const model = accessor.get(IModelService).getModel(resource); - if (model) { - const editorPosition = Position.lift(position); - return handler(model, editorPosition, args); - } - - return accessor.get(ITextModelService).createModelReference(resource).then(reference => { - return new Promise((resolve, reject) => { - try { - const result = handler(reference.object.textEditorModel, Position.lift(position), args); - resolve(result); - } catch (err) { - reject(err); - } - }).finally(() => { - reference.dispose(); - }); - }); - }); -} export function registerModelAndPositionCommand(id: string, handler: (model: ITextModel, position: Position, ...args: any[]) => any) { CommandsRegistry.registerCommand(id, function (accessor, ...args) { diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index f48a9cff..3ebb6454 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -31,6 +31,8 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC private readonly _onDidChangeTransientModelProperty: Emitter = this._register(new Emitter()); public readonly onDidChangeTransientModelProperty: Event = this._onDidChangeTransientModelProperty.event; + protected readonly _onDecorationTypeRegistered: Emitter = this._register(new Emitter()); + public onDecorationTypeRegistered: Event = this._onDecorationTypeRegistered.event; private readonly _codeEditors: { [editorId: string]: ICodeEditor; }; private readonly _diffEditors: { [editorId: string]: IDiffEditor; }; @@ -93,6 +95,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; abstract removeDecorationType(key: string): void; abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions; + abstract resolveDecorationCSSRules(decorationTypeKey: string): CSSRuleList | null; private readonly _transientWatchers: { [uri: string]: ModelTransientSettingWatcher; } = {}; private readonly _modelProperties = new Map>(); diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 7a1f3346..a9e8b99d 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -10,6 +10,8 @@ import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { isObject } from 'vs/base/common/types'; +import { UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); @@ -65,9 +67,13 @@ export class ResourceFileEdit extends ResourceEdit { export interface IBulkEditOptions { editor?: ICodeEditor; progress?: IProgress; + token?: CancellationToken; showPreview?: boolean; + suppressPreview?: boolean; label?: string; quotableLabel?: string; + undoRedoSource?: UndoRedoSource; + undoRedoGroupId?: number; } export interface IBulkEditResult { diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index 0174886d..8665de00 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -23,6 +23,7 @@ export interface ICodeEditorService { readonly onDiffEditorRemove: Event; readonly onDidChangeTransientModelProperty: Event; + readonly onDecorationTypeRegistered: Event; addCodeEditor(editor: ICodeEditor): void; @@ -41,6 +42,7 @@ export interface ICodeEditorService { registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; removeDecorationType(key: string): void; resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; + resolveDecorationCSSRules(decorationTypeKey: string): CSSRuleList | null; setModelProperty(resource: URI, key: string, value: any): void; getModelProperty(resource: URI, key: string): any; diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 464c1a33..c961bfd7 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -21,6 +21,10 @@ export class RefCountedStyleSheet { private readonly _styleSheet: HTMLStyleElement; private _refCount: number; + public get sheet() { + return this._styleSheet.sheet as CSSStyleSheet; + } + constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) { this._parent = parent; this._editorId = editorId; @@ -53,6 +57,10 @@ export class RefCountedStyleSheet { export class GlobalStyleSheet { private readonly _styleSheet: HTMLStyleElement; + public get sheet() { + return this._styleSheet.sheet as CSSStyleSheet; + } + constructor(styleSheet: HTMLStyleElement) { this._styleSheet = styleSheet; } @@ -129,6 +137,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { provider = new DecorationSubTypeOptionsProvider(this._themeService, styleSheet, providerArgs); } this._decorationOptionProviders.set(key, provider); + this._onDecorationTypeRegistered.fire(key); } provider.refCount++; } @@ -153,6 +162,14 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { return provider.getOptions(this, writable); } + public resolveDecorationCSSRules(decorationTypeKey: string) { + const provider = this._decorationOptionProviders.get(decorationTypeKey); + if (!provider) { + return null; + } + return provider.resolveDecorationCSSRules(); + } + abstract getActiveCodeEditor(): ICodeEditor | null; abstract openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; } @@ -160,9 +177,10 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { interface IModelDecorationOptionsProvider extends IDisposable { refCount: number; getOptions(codeEditorService: AbstractCodeEditorService, writable: boolean): IModelDecorationOptions; + resolveDecorationCSSRules(): CSSRuleList; } -class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider { +export class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider { private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; public refCount: number; @@ -192,6 +210,10 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide return options; } + public resolveDecorationCSSRules(): CSSRuleList { + return this._styleSheet.sheet.cssRules; + } + public dispose(): void { if (this._beforeContentRules) { this._beforeContentRules.dispose(); @@ -213,7 +235,7 @@ interface ProviderArguments { } -class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { +export class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { private readonly _disposables = new DisposableStore(); private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; @@ -295,6 +317,10 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { }; } + public resolveDecorationCSSRules(): CSSRuleList { + return this._styleSheet.sheet.rules; + } + public dispose(): void { this._disposables.dispose(); this._styleSheet.unref(); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index d898abd3..1360871f 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -14,6 +14,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { ICommandService } from 'vs/platform/commands/common/commands'; import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener, matchesScheme } from 'vs/platform/opener/common/opener'; import { EditorOpenContext } from 'vs/platform/editor/common/editor'; +import { ResourceMap } from 'vs/base/common/map'; class CommandOpener implements IOpener { @@ -74,7 +75,14 @@ class EditorOpener implements IOpener { } await this._editorService.openCodeEditor( - { resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, + { + resource: target, + options: { + selection, + context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API, + ...options?.editorOptions + } + }, this._editorService.getFocusedCodeEditor(), options?.openToSide ); @@ -90,6 +98,7 @@ export class OpenerService implements IOpenerService { private readonly _openers = new LinkedList(); private readonly _validators = new LinkedList(); private readonly _resolvers = new LinkedList(); + private readonly _resolvedUriTargets = new ResourceMap(uri => uri.with({ path: null, fragment: null, query: null }).toString()); private _externalOpener: IExternalOpener; @@ -148,16 +157,18 @@ export class OpenerService implements IOpenerService { } async open(target: URI | string, options?: OpenOptions): Promise { - // check with contributed validators - for (const validator of this._validators.toArray()) { - if (!(await validator.shouldOpen(target))) { + const targetURI = typeof target === 'string' ? URI.parse(target) : target; + // validate against the original URI that this URI resolves to, if one exists + const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target; + for (const validator of this._validators) { + if (!(await validator.shouldOpen(validationTarget))) { return false; } } // check with contributed openers - for (const opener of this._openers.toArray()) { + for (const opener of this._openers) { const handled = await opener.open(target, options); if (handled) { return true; @@ -168,9 +179,10 @@ export class OpenerService implements IOpenerService { } async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise { - for (const resolver of this._resolvers.toArray()) { + for (const resolver of this._resolvers) { const result = await resolver.resolveExternalUri(resource, options); if (result) { + this._resolvedUriTargets.set(result.resolved, resource); return result; } } @@ -180,7 +192,7 @@ export class OpenerService implements IOpenerService { private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise { - //todo@joh IExternalUriResolver should support `uri: URI | string` + //todo@jrieken IExternalUriResolver should support `uri: URI | string` const uri = typeof resource === 'string' ? URI.parse(resource) : resource; const { resolved } = await this.resolveExternalUri(uri, options); diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 45c568d0..87c6461d 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -3,13 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { Configuration } from 'vs/editor/browser/config/configuration'; +import { ILineBreaksComputer, LineBreakData } from 'vs/editor/common/viewModel/viewModel'; + +const ttPolicy = window.trustedTypes?.createPolicy('domLineBreaksComputer', { createHTML: value => value }); export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory { @@ -107,7 +110,9 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe allCharOffsets[i] = tmp[0]; allVisibleColumns[i] = tmp[1]; } - containerDomNode.innerHTML = sb.build(); + const html = sb.build(); + const trustedhtml = ttPolicy ? ttPolicy.createHTML(html) : html; + containerDomNode.innerHTML = trustedhtml as unknown as string; containerDomNode.style.position = 'absolute'; containerDomNode.style.top = '10000'; diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index 519a26bb..a218b707 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -322,6 +322,10 @@ export class ViewController { this.userInputEvents.emitMouseDrop(e); } + public emitMouseDropCanceled(): void { + this.userInputEvents.emitMouseDropCanceled(); + } + public emitMouseWheel(e: IMouseWheelEvent): void { this.userInputEvents.emitMouseWheel(e); } diff --git a/src/vs/editor/browser/view/viewUserInputEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts index 22906bda..2a58f89d 100644 --- a/src/vs/editor/browser/view/viewUserInputEvents.ts +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -26,6 +26,7 @@ export class ViewUserInputEvents { public onMouseUp: EventCallback | null = null; public onMouseDrag: EventCallback | null = null; public onMouseDrop: EventCallback | null = null; + public onMouseDropCanceled: EventCallback | null = null; public onMouseWheel: EventCallback | null = null; private readonly _coordinatesConverter: ICoordinatesConverter; @@ -88,6 +89,12 @@ export class ViewUserInputEvents { } } + public emitMouseDropCanceled(): void { + if (this.onMouseDropCanceled) { + this.onMouseDropCanceled(); + } + } + public emitMouseWheel(e: IMouseWheelEvent): void { if (this.onMouseWheel) { this.onMouseWheel(e); diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index 098bc6c7..6208c89f 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -23,7 +23,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { protected _contentLeft: number; protected _contentWidth: number; protected _selectionIsEmpty: boolean; - protected _renderLineHightlightOnlyWhenFocus: boolean; + protected _renderLineHighlightOnlyWhenFocus: boolean; protected _focused: boolean; private _cursorLineNumbers: number[]; private _selections: Selection[]; @@ -37,7 +37,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { const layoutInfo = options.get(EditorOption.layoutInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._renderLineHighlight = options.get(EditorOption.renderLineHighlight); - this._renderLineHightlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); + this._renderLineHighlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; this._selectionIsEmpty = true; @@ -85,7 +85,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { const layoutInfo = options.get(EditorOption.layoutInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._renderLineHighlight = options.get(EditorOption.renderLineHighlight); - this._renderLineHightlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); + this._renderLineHighlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; return true; @@ -110,7 +110,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { return true; } public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { - if (!this._renderLineHightlightOnlyWhenFocus) { + if (!this._renderLineHighlightOnlyWhenFocus) { return false; } @@ -170,33 +170,36 @@ export class CurrentLineHighlightOverlay extends AbstractLineHighlightOverlay { return ( (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') && this._selectionIsEmpty - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } protected _shouldRenderOther(): boolean { return ( (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } } export class CurrentLineMarginHighlightOverlay extends AbstractLineHighlightOverlay { protected _renderOne(ctx: RenderingContext): string { - const className = 'current-line current-line-margin' + (this._shouldRenderOther() ? ' current-line-margin-both' : ''); + const className = 'current-line' + (this._shouldRenderMargin() ? ' current-line-margin' : '') + (this._shouldRenderOther() ? ' current-line-margin-both' : ''); return `
`; } - protected _shouldRenderThis(): boolean { + protected _shouldRenderMargin(): boolean { return ( (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } + protected _shouldRenderThis(): boolean { + return true; + } protected _shouldRenderOther(): boolean { return ( (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') && this._selectionIsEmpty - && (!this._renderLineHightlightOnlyWhenFocus || this._focused) + && (!this._renderLineHighlightOnlyWhenFocus || this._focused) ); } } diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index 21193bac..5a6df759 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -56,6 +56,7 @@ export class EditorScrollbar extends ViewPart { mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, fastScrollSensitivity: fastScrollSensitivity, scrollPredominantAxis: scrollPredominantAxis, + scrollByPage: scrollbar.scrollByPage, }; this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.getScrollable())); diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 1be53aad..7ca33211 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -226,8 +226,7 @@ class MinimapLayout { * Compute a desired `scrollPosition` such that the slider moves by `delta`. */ public getDesiredScrollTopFromDelta(delta: number): number { - const desiredSliderPosition = this.sliderTop + delta; - return Math.round(desiredSliderPosition / this._computedSliderRatio); + return Math.round(this.scrollTop + delta / this._computedSliderRatio); } public getDesiredScrollTopFromTouchLocation(pageY: number): number { @@ -238,6 +237,7 @@ class MinimapLayout { options: MinimapOptions, viewportStartLineNumber: number, viewportEndLineNumber: number, + viewportStartLineNumberVerticalOffset: number, viewportHeight: number, viewportContainsWhitespaceGaps: boolean, lineCount: number, @@ -332,8 +332,10 @@ class MinimapLayout { } const endLineNumber = Math.min(lineCount, startLineNumber + minimapLinesFitting - 1); + const partialLine = (scrollTop - viewportStartLineNumberVerticalOffset) / lineHeight; + const sliderTopAligned = (viewportStartLineNumber - startLineNumber + partialLine) * minimapLineHeight / pixelRatio; - return new MinimapLayout(scrollTop, scrollHeight, true, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + return new MinimapLayout(scrollTop, scrollHeight, true, computedSliderRatio, sliderTopAligned, sliderHeight, startLineNumber, endLineNumber); } } } @@ -505,6 +507,7 @@ interface IMinimapRenderingContext { readonly viewportStartLineNumber: number; readonly viewportEndLineNumber: number; + readonly viewportStartLineNumberVerticalOffset: number; readonly scrollTop: number; readonly scrollLeft: number; @@ -891,6 +894,7 @@ export class Minimap extends ViewPart implements IMinimapModel { viewportStartLineNumber: viewportStartLineNumber, viewportEndLineNumber: viewportEndLineNumber, + viewportStartLineNumberVerticalOffset: ctx.getVerticalOffsetForLineNumber(viewportStartLineNumber), scrollTop: ctx.scrollTop, scrollLeft: ctx.scrollLeft, @@ -1344,6 +1348,7 @@ class InnerMinimap extends Disposable { this._model.options, renderingCtx.viewportStartLineNumber, renderingCtx.viewportEndLineNumber, + renderingCtx.viewportStartLineNumberVerticalOffset, renderingCtx.viewportHeight, renderingCtx.viewportContainsWhitespaceGaps, this._model.getLineCount(), diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index ef3f6e30..ec5077ea 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -25,6 +25,7 @@ export class ViewCursors extends ViewPart { private _cursorStyle: TextEditorCursorStyle; private _cursorSmoothCaretAnimation: boolean; private _selectionIsEmpty: boolean; + private _isComposingInput: boolean; private _isVisible: boolean; @@ -49,6 +50,7 @@ export class ViewCursors extends ViewPart { this._cursorStyle = options.get(EditorOption.cursorStyle); this._cursorSmoothCaretAnimation = options.get(EditorOption.cursorSmoothCaretAnimation); this._selectionIsEmpty = true; + this._isComposingInput = false; this._isVisible = false; @@ -83,7 +85,16 @@ export class ViewCursors extends ViewPart { } // --- begin event handlers - + public onCompositionStart(e: viewEvents.ViewCompositionStartEvent): boolean { + this._isComposingInput = true; + this._updateBlinking(); + return true; + } + public onCompositionEnd(e: viewEvents.ViewCompositionEndEvent): boolean { + this._isComposingInput = false; + this._updateBlinking(); + return true; + } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { const options = this._context.configuration.options; @@ -195,6 +206,10 @@ export class ViewCursors extends ViewPart { // ---- blinking logic private _getCursorBlinking(): TextEditorCursorBlinkingStyle { + if (this._isComposingInput) { + // avoid double cursors + return TextEditorCursorBlinkingStyle.Hidden; + } if (!this._editorHasFocus) { return TextEditorCursorBlinkingStyle.Hidden; } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 17539558..4c87ce2b 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -37,7 +37,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; -import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground, editorErrorBackground, editorInfoBackground, editorWarningBackground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; @@ -176,6 +176,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onMouseDrop: Emitter = this._register(new Emitter()); public readonly onMouseDrop: Event = this._onMouseDrop.event; + private readonly _onMouseDropCanceled: Emitter = this._register(new Emitter()); + public readonly onMouseDropCanceled: Event = this._onMouseDropCanceled.event; + private readonly _onContextMenu: Emitter = this._register(new Emitter()); public readonly onContextMenu: Event = this._onContextMenu.event; @@ -1024,6 +1027,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (this._triggerEditorCommand(source, handlerId, payload)) { return; } + + this._commandService.executeCommand(handlerId, payload); } private _startComposition(): void { @@ -1117,6 +1122,18 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return true; } + public popUndoStop(): boolean { + if (!this._modelData) { + return false; + } + if (this._configuration.options.get(EditorOption.readOnly)) { + // read only editor => sorry! + return false; + } + this._modelData.model.popStackElement(); + return true; + } + public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean { if (!this._modelData) { return false; @@ -1614,6 +1631,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE viewUserInputEvents.onMouseUp = (e) => this._onMouseUp.fire(e); viewUserInputEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e); viewUserInputEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e); + viewUserInputEvents.onMouseDropCanceled = (e) => this._onMouseDropCanceled.fire(e); viewUserInputEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e); const view = new View( @@ -1716,6 +1734,7 @@ class EditorContextKeysManager extends Disposable { private readonly _editorTextFocus: IContextKey; private readonly _editorTabMovesFocus: IContextKey; private readonly _editorReadonly: IContextKey; + private readonly _inDiffEditor: IContextKey; private readonly _editorColumnSelection: IContextKey; private readonly _hasMultipleSelections: IContextKey; private readonly _hasNonEmptySelection: IContextKey; @@ -1738,6 +1757,7 @@ class EditorContextKeysManager extends Disposable { this._editorTextFocus = EditorContextKeys.editorTextFocus.bindTo(contextKeyService); this._editorTabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService); this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService); + this._inDiffEditor = EditorContextKeys.inDiffEditor.bindTo(contextKeyService); this._editorColumnSelection = EditorContextKeys.columnSelection.bindTo(contextKeyService); this._hasMultipleSelections = EditorContextKeys.hasMultipleSelections.bindTo(contextKeyService); this._hasNonEmptySelection = EditorContextKeys.hasNonEmptySelection.bindTo(contextKeyService); @@ -1766,6 +1786,7 @@ class EditorContextKeysManager extends Disposable { this._editorTabMovesFocus.set(options.get(EditorOption.tabFocusMode)); this._editorReadonly.set(options.get(EditorOption.readOnly)); + this._inDiffEditor.set(options.get(EditorOption.inDiffEditor)); this._editorColumnSelection.set(options.get(EditorOption.columnSelection)); } @@ -1981,6 +2002,10 @@ registerThemingParticipant((theme, collector) => { if (errorForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}") repeat-x bottom left; }`); } + const errorBackground = theme.getColor(editorErrorBackground); + if (errorBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${errorBackground}; }`); + } const warningBorderColor = theme.getColor(editorWarningBorder); if (warningBorderColor) { @@ -1990,6 +2015,10 @@ registerThemingParticipant((theme, collector) => { if (warningForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(warningForeground)}") repeat-x bottom left; }`); } + const warningBackground = theme.getColor(editorWarningBackground); + if (warningBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${warningBackground}; }`); + } const infoBorderColor = theme.getColor(editorInfoBorder); if (infoBorderColor) { @@ -1999,6 +2028,10 @@ registerThemingParticipant((theme, collector) => { if (infoForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorInfoDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(infoForeground)}") repeat-x bottom left; }`); } + const infoBackground = theme.getColor(editorInfoBackground); + if (infoBackground) { + collector.addRule(`.monaco-editor .${ClassName.EditorInfoDecoration}::before { display: block; content: ''; width: 100%; height: 100%; background: ${infoBackground}; }`); + } const hintBorderColor = theme.getColor(editorHintBorder); if (hintBorderColor) { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ada21178..0730e4f0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -20,7 +20,7 @@ import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffReview } from 'vs/editor/browser/widget/diffReview'; -import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, IComputedEditorOptions, EditorOption, EditorOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; +import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, EditorOption, EditorOptions, EditorFontLigatures, stringSet as validateStringSetOption, boolean as validateBooleanOption } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; @@ -33,13 +33,13 @@ import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; -import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { ILineBreaksComputer, InlineDecoration, InlineDecorationType, IViewModel, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, diffDiagonalFill } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService, getThemeTypeSelector, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -48,8 +48,11 @@ import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs import { onUnexpectedError } from 'vs/base/common/errors'; import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; +import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; interface IEditorDiffDecorations { decorations: IModelDeltaDecoration[]; @@ -70,17 +73,9 @@ interface IEditorsZones { modified: IMyViewZone[]; } -export interface IDiffEditorWidgetStyle { - getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; - setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; - applyColors(theme: IColorTheme): boolean; - layout(): number; - dispose(): void; -} - class VisualEditorState { private _zones: string[]; - private inlineDiffMargins: InlineDiffMargin[]; + private _inlineDiffMargins: InlineDiffMargin[]; private _zonesMap: { [zoneId: string]: boolean; }; private _decorations: string[]; @@ -89,7 +84,7 @@ class VisualEditorState { private _clipboardService: IClipboardService ) { this._zones = []; - this.inlineDiffMargins = []; + this._inlineDiffMargins = []; this._zonesMap = {}; this._decorations = []; } @@ -102,8 +97,8 @@ class VisualEditorState { // (1) View zones if (this._zones.length > 0) { editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (let i = 0, length = this._zones.length; i < length; i++) { - viewChangeAccessor.removeZone(this._zones[i]); + for (const zoneId of this._zones) { + viewChangeAccessor.removeZone(zoneId); } }); } @@ -120,25 +115,25 @@ class VisualEditorState { // view zones editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (let i = 0, length = this._zones.length; i < length; i++) { - viewChangeAccessor.removeZone(this._zones[i]); + for (const zoneId of this._zones) { + viewChangeAccessor.removeZone(zoneId); } - for (let i = 0, length = this.inlineDiffMargins.length; i < length; i++) { - this.inlineDiffMargins[i].dispose(); + for (const inlineDiffMargin of this._inlineDiffMargins) { + inlineDiffMargin.dispose(); } this._zones = []; this._zonesMap = {}; - this.inlineDiffMargins = []; + this._inlineDiffMargins = []; for (let i = 0, length = newDecorations.zones.length; i < length; i++) { const viewZone = newDecorations.zones[i]; viewZone.suppressMouseDown = true; - let zoneId = viewChangeAccessor.addZone(viewZone); + const zoneId = viewChangeAccessor.addZone(viewZone); this._zones.push(zoneId); this._zonesMap[String(zoneId)] = true; if (newDecorations.zones[i].diff && viewZone.marginDomNode) { viewZone.suppressMouseDown = false; - this.inlineDiffMargins.push(new InlineDiffMargin(zoneId, viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService)); + this._inlineDiffMargins.push(new InlineDiffMargin(zoneId, viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService)); } } }); @@ -160,8 +155,9 @@ class VisualEditorState { let DIFF_EDITOR_ID = 0; -const diffInsertIcon = registerIcon('diff-insert', Codicon.add); -const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove); +const diffInsertIcon = registerIcon('diff-insert', Codicon.add, nls.localize('diffInsertIcon', 'Line decoration for inserts in the diff editor.')); +const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove, nls.localize('diffRemoveIcon', 'Line decoration for removals in the diff editor.')); +const ttPolicy = window.trustedTypes?.createPolicy('diffEditorWidget', { createHTML: value => value }); export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor { @@ -178,7 +174,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _onDidContentSizeChange: Emitter = this._register(new Emitter()); public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; - private readonly id: number; + private readonly _id: number; private _state: editorBrowser.DiffEditorState; private _updatingDiffProgress: IProgressRunner | null; @@ -189,12 +185,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private readonly _elementSizeObserver: ElementSizeObserver; - private readonly originalEditor: CodeEditorWidget; + private readonly _originalEditor: CodeEditorWidget; private readonly _originalDomNode: HTMLElement; private readonly _originalEditorState: VisualEditorState; private _originalOverviewRuler: editorBrowser.IOverviewRuler | null; - private readonly modifiedEditor: CodeEditorWidget; + private readonly _modifiedEditor: CodeEditorWidget; private readonly _modifiedDomNode: HTMLElement; private readonly _modifiedEditorState: VisualEditorState; private _modifiedOverviewRuler: editorBrowser.IOverviewRuler | null; @@ -209,14 +205,14 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _ignoreTrimWhitespace: boolean; private _originalIsEditable: boolean; - private _originalCodeLens: boolean; - private _modifiedCodeLens: boolean; + private _diffCodeLens: boolean; + private _diffWordWrap: 'off' | 'on' | 'inherit'; private _renderSideBySide: boolean; private _maxComputationTime: number; private _renderIndicators: boolean; private _enableSplitViewResizing: boolean; - private _strategy!: IDiffEditorWidgetStyle; + private _strategy!: DiffEditorWidgetStyle; private readonly _updateDecorationsRunner: RunOnceScheduler; @@ -250,7 +246,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._themeService = themeService; this._notificationService = notificationService; - this.id = (++DIFF_EDITOR_ID); + this._id = (++DIFF_EDITOR_ID); this._state = editorBrowser.DiffEditorState.Idle; this._updatingDiffProgress = null; @@ -281,19 +277,14 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._renderIndicators = options.renderIndicators; } - this._originalIsEditable = false; - if (typeof options.originalEditable !== 'undefined') { - this._originalIsEditable = Boolean(options.originalEditable); - } + this._originalIsEditable = validateBooleanOption(options.originalEditable, false); + this._diffCodeLens = validateBooleanOption(options.diffCodeLens, false); + this._diffWordWrap = validateDiffWordWrap(options.diffWordWrap, 'inherit'); - this._originalCodeLens = false; - if (typeof options.originalCodeLens !== 'undefined') { - this._originalCodeLens = Boolean(options.originalCodeLens); - } - - this._modifiedCodeLens = false; - if (typeof options.modifiedCodeLens !== 'undefined') { - this._modifiedCodeLens = Boolean(options.modifiedCodeLens); + if (typeof options.isInEmbeddedEditor !== 'undefined') { + this._contextKeyService.createKey('isInEmbeddedDiffEditor', options.isInEmbeddedEditor); + } else { + this._contextKeyService.createKey('isInEmbeddedDiffEditor', false); } this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0)); @@ -315,7 +306,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewDomElement.appendChild(this._overviewViewportDomElement.domNode); this._register(dom.addStandardDisposableListener(this._overviewDomElement, 'mousedown', (e) => { - this.modifiedEditor.delegateVerticalScrollbarMouseDown(e); + this._modifiedEditor.delegateVerticalScrollbarMouseDown(e); })); this._containerDomElement.appendChild(this._overviewDomElement); @@ -362,8 +353,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE rightServices.set(IContextKeyService, rightContextKeyService); const rightScopedInstantiationService = instantiationService.createChild(rightServices); - this.originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService, leftContextKeyService); - this.modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService, rightContextKeyService); + this._originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService, leftContextKeyService); + this._modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService, rightContextKeyService); this._originalOverviewRuler = null; this._modifiedOverviewRuler = null; @@ -421,7 +412,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public getContentHeight(): number { - return this.modifiedEditor.getContentHeight(); + return this._modifiedEditor.getContentHeight(); } private _setState(newState: editorBrowser.DiffEditorState): void { @@ -466,8 +457,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); this._originalOverviewRuler.dispose(); } - if (this.originalEditor.hasModel()) { - this._originalOverviewRuler = this.originalEditor.createOverviewRuler('original diffOverviewRuler')!; + if (this._originalEditor.hasModel()) { + this._originalOverviewRuler = this._originalEditor.createOverviewRuler('original diffOverviewRuler')!; this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode()); } @@ -475,8 +466,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); this._modifiedOverviewRuler.dispose(); } - if (this.modifiedEditor.hasModel()) { - this._modifiedOverviewRuler = this.modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; + if (this._modifiedEditor.hasModel()) { + this._modifiedOverviewRuler = this._modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode()); } @@ -484,7 +475,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _createLeftHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options, this._originalIsEditable, this._originalCodeLens)); + const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options)); this._register(editor.onDidScrollChange((e) => { if (this._isHandlingScrollEvent) { @@ -494,56 +485,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return; } this._isHandlingScrollEvent = true; - this.modifiedEditor.setScrollPosition({ - scrollLeft: e.scrollLeft, - scrollTop: e.scrollTop - }); - this._isHandlingScrollEvent = false; - - this._layoutOverviewViewport(); - })); - - this._register(editor.onDidChangeViewZones(() => { - this._onViewZonesChanged(); - })); - - this._register(editor.onDidChangeModelContent(() => { - if (this._isVisible) { - this._beginUpdateDecorationsSoon(); - } - })); - - const isInDiffLeftEditorKey = contextKeyService.createKey('isInDiffLeftEditor', undefined); - this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); - - this._register(editor.onDidContentSizeChange(e => { - const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; - const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); - - this._onDidContentSizeChange.fire({ - contentHeight: height, - contentWidth: width, - contentHeightChanged: e.contentHeightChanged, - contentWidthChanged: e.contentWidthChanged - }); - })); - - return editor; - } - - private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { - const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options, this._modifiedCodeLens)); - - this._register(editor.onDidScrollChange((e) => { - if (this._isHandlingScrollEvent) { - return; - } - if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { - return; - } - this._isHandlingScrollEvent = true; - this.originalEditor.setScrollPosition({ + this._modifiedEditor.setScrollPosition({ scrollLeft: e.scrollLeft, scrollTop: e.scrollTop }); @@ -557,8 +499,77 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE })); this._register(editor.onDidChangeConfiguration((e) => { - if (e.hasChanged(EditorOption.fontInfo) && editor.getModel()) { - this._onViewZonesChanged(); + if (!editor.getModel()) { + return; + } + if (e.hasChanged(EditorOption.fontInfo)) { + this._updateDecorationsRunner.schedule(); + } + if (e.hasChanged(EditorOption.wrappingInfo)) { + this._updateDecorationsRunner.cancel(); + this._updateDecorations(); + } + })); + + this._register(editor.onDidChangeModelContent(() => { + if (this._isVisible) { + this._beginUpdateDecorationsSoon(); + } + })); + + const isInDiffLeftEditorKey = contextKeyService.createKey('isInDiffLeftEditor', undefined); + this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); + + this._register(editor.onDidContentSizeChange(e => { + const width = this._originalEditor.getContentWidth() + this._modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this._modifiedEditor.getContentHeight(), this._originalEditor.getContentHeight()); + + this._onDidContentSizeChange.fire({ + contentHeight: height, + contentWidth: width, + contentHeightChanged: e.contentHeightChanged, + contentWidthChanged: e.contentWidthChanged + }); + })); + + return editor; + } + + private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget { + const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options)); + + this._register(editor.onDidScrollChange((e) => { + if (this._isHandlingScrollEvent) { + return; + } + if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { + return; + } + this._isHandlingScrollEvent = true; + this._originalEditor.setScrollPosition({ + scrollLeft: e.scrollLeft, + scrollTop: e.scrollTop + }); + this._isHandlingScrollEvent = false; + + this._layoutOverviewViewport(); + })); + + this._register(editor.onDidChangeViewZones(() => { + this._onViewZonesChanged(); + })); + + this._register(editor.onDidChangeConfiguration((e) => { + if (!editor.getModel()) { + return; + } + if (e.hasChanged(EditorOption.fontInfo)) { + this._updateDecorationsRunner.schedule(); + } + if (e.hasChanged(EditorOption.wrappingInfo)) { + this._updateDecorationsRunner.cancel(); + this._updateDecorations(); } })); @@ -579,8 +590,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); this._register(editor.onDidContentSizeChange(e => { - const width = this.originalEditor.getContentWidth() + this.modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; - const height = Math.max(this.modifiedEditor.getContentHeight(), this.originalEditor.getContentHeight()); + const width = this._originalEditor.getContentWidth() + this._modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const height = Math.max(this._modifiedEditor.getContentHeight(), this._originalEditor.getContentHeight()); this._onDidContentSizeChange.fire({ contentHeight: height, @@ -619,10 +630,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._containerDomElement.removeChild(this._overviewDomElement); this._containerDomElement.removeChild(this._originalDomNode); - this.originalEditor.dispose(); + this._originalEditor.dispose(); this._containerDomElement.removeChild(this._modifiedDomNode); - this.modifiedEditor.dispose(); + this._modifiedEditor.dispose(); this._strategy.dispose(); @@ -641,7 +652,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE //------------ begin IDiffEditor methods public getId(): string { - return this.getEditorType() + ':' + this.id; + return this.getEditorType() + ':' + this._id; } public getEditorType(): string { @@ -660,11 +671,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public getOriginalEditor(): editorBrowser.ICodeEditor { - return this.originalEditor; + return this._originalEditor; } public getModifiedEditor(): editorBrowser.ICodeEditor { - return this.modifiedEditor; + return this._modifiedEditor; } public updateOptions(newOptions: IDiffEditorOptions): void { @@ -706,18 +717,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._beginUpdateDecorations(); } - if (typeof newOptions.originalEditable !== 'undefined') { - this._originalIsEditable = Boolean(newOptions.originalEditable); - } - if (typeof newOptions.originalCodeLens !== 'undefined') { - this._originalCodeLens = Boolean(newOptions.originalCodeLens); - } - if (typeof newOptions.modifiedCodeLens !== 'undefined') { - this._modifiedCodeLens = Boolean(newOptions.modifiedCodeLens); - } + this._originalIsEditable = validateBooleanOption(newOptions.originalEditable, this._originalIsEditable); + this._diffCodeLens = validateBooleanOption(newOptions.diffCodeLens, this._diffCodeLens); + this._diffWordWrap = validateDiffWordWrap(newOptions.diffWordWrap, this._diffWordWrap); - this.modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions, this._modifiedCodeLens)); - this.originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions, this._originalIsEditable, this._originalCodeLens)); + this._modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(newOptions)); + this._originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(newOptions)); // enableSplitViewResizing if (typeof newOptions.enableSplitViewResizing !== 'undefined') { @@ -739,8 +744,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE public getModel(): editorCommon.IDiffEditorModel { return { - original: this.originalEditor.getModel()!, - modified: this.modifiedEditor.getModel()! + original: this._originalEditor.getModel()!, + modified: this._modifiedEditor.getModel()! }; } @@ -754,15 +759,15 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._cleanViewZonesAndDecorations(); // Update code editor models - this.originalEditor.setModel(model ? model.original : null); - this.modifiedEditor.setModel(model ? model.modified : null); + this._originalEditor.setModel(model ? model.original : null); + this._modifiedEditor.setModel(model ? model.modified : null); this._updateDecorationsRunner.cancel(); // this.originalEditor.onDidChangeModelOptions if (model) { - this.originalEditor.setScrollTop(0); - this.modifiedEditor.setScrollTop(0); + this._originalEditor.setScrollTop(0); + this._modifiedEditor.setScrollTop(0); } // Disable any diff computations that will come in @@ -785,59 +790,59 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public getVisibleColumnFromPosition(position: IPosition): number { - return this.modifiedEditor.getVisibleColumnFromPosition(position); + return this._modifiedEditor.getVisibleColumnFromPosition(position); } public getStatusbarColumn(position: IPosition): number { - return this.modifiedEditor.getStatusbarColumn(position); + return this._modifiedEditor.getStatusbarColumn(position); } public getPosition(): Position | null { - return this.modifiedEditor.getPosition(); + return this._modifiedEditor.getPosition(); } public setPosition(position: IPosition): void { - this.modifiedEditor.setPosition(position); + this._modifiedEditor.setPosition(position); } public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLine(lineNumber, scrollType); + this._modifiedEditor.revealLine(lineNumber, scrollType); } public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLineInCenter(lineNumber, scrollType); + this._modifiedEditor.revealLineInCenter(lineNumber, scrollType); } public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); + this._modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); } public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLineNearTop(lineNumber, scrollType); + this._modifiedEditor.revealLineNearTop(lineNumber, scrollType); } public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPosition(position, scrollType); + this._modifiedEditor.revealPosition(position, scrollType); } public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPositionInCenter(position, scrollType); + this._modifiedEditor.revealPositionInCenter(position, scrollType); } public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); + this._modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); } public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealPositionNearTop(position, scrollType); + this._modifiedEditor.revealPositionNearTop(position, scrollType); } public getSelection(): Selection | null { - return this.modifiedEditor.getSelection(); + return this._modifiedEditor.getSelection(); } public getSelections(): Selection[] | null { - return this.modifiedEditor.getSelections(); + return this._modifiedEditor.getSelections(); } public setSelection(range: IRange): void; @@ -845,60 +850,60 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE public setSelection(selection: ISelection): void; public setSelection(editorSelection: Selection): void; public setSelection(something: any): void { - this.modifiedEditor.setSelection(something); + this._modifiedEditor.setSelection(something); } public setSelections(ranges: readonly ISelection[]): void { - this.modifiedEditor.setSelections(ranges); + this._modifiedEditor.setSelections(ranges); } public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType); } public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType); } public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); } public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType); + this._modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType); } public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { - this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); + this._modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); } public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeInCenter(range, scrollType); + this._modifiedEditor.revealRangeInCenter(range, scrollType); } public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); + this._modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); } public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeNearTop(range, scrollType); + this._modifiedEditor.revealRangeNearTop(range, scrollType); } public revealRangeNearTopIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeNearTopIfOutsideViewport(range, scrollType); + this._modifiedEditor.revealRangeNearTopIfOutsideViewport(range, scrollType); } public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this.modifiedEditor.revealRangeAtTop(range, scrollType); + this._modifiedEditor.revealRangeAtTop(range, scrollType); } public getSupportedActions(): editorCommon.IEditorAction[] { - return this.modifiedEditor.getSupportedActions(); + return this._modifiedEditor.getSupportedActions(); } public saveViewState(): editorCommon.IDiffEditorViewState { - let originalViewState = this.originalEditor.saveViewState(); - let modifiedViewState = this.modifiedEditor.saveViewState(); + const originalViewState = this._originalEditor.saveViewState(); + const modifiedViewState = this._modifiedEditor.saveViewState(); return { original: originalViewState, modified: modifiedViewState @@ -907,9 +912,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE public restoreViewState(s: editorCommon.IDiffEditorViewState): void { if (s.original && s.modified) { - let diffEditorState = s; - this.originalEditor.restoreViewState(diffEditorState.original); - this.modifiedEditor.restoreViewState(diffEditorState.modified); + const diffEditorState = s; + this._originalEditor.restoreViewState(diffEditorState.original); + this._modifiedEditor.restoreViewState(diffEditorState.modified); } } @@ -918,35 +923,35 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public focus(): void { - this.modifiedEditor.focus(); + this._modifiedEditor.focus(); } public hasTextFocus(): boolean { - return this.originalEditor.hasTextFocus() || this.modifiedEditor.hasTextFocus(); + return this._originalEditor.hasTextFocus() || this._modifiedEditor.hasTextFocus(); } public onVisible(): void { this._isVisible = true; - this.originalEditor.onVisible(); - this.modifiedEditor.onVisible(); + this._originalEditor.onVisible(); + this._modifiedEditor.onVisible(); // Begin comparing this._beginUpdateDecorations(); } public onHide(): void { this._isVisible = false; - this.originalEditor.onHide(); - this.modifiedEditor.onHide(); + this._originalEditor.onHide(); + this._modifiedEditor.onHide(); // Remove all view zones & decorations this._cleanViewZonesAndDecorations(); } public trigger(source: string | null | undefined, handlerId: string, payload: any): void { - this.modifiedEditor.trigger(source, handlerId, payload); + this._modifiedEditor.trigger(source, handlerId, payload); } public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { - return this.modifiedEditor.changeDecorations(callback); + return this._modifiedEditor.changeDecorations(callback); } //------------ end IDiffEditor methods @@ -970,8 +975,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE const height = this._elementSizeObserver.getHeight(); const reviewHeight = this._getReviewHeight(); - let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; - let layoutInfo = this.modifiedEditor.getLayoutInfo(); + const freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; + const layoutInfo = this._modifiedEditor.getLayoutInfo(); if (layoutInfo) { this._originalOverviewRuler.setLayout({ top: 0, @@ -1021,8 +1026,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _beginUpdateDecorations(): void { this._beginUpdateDecorationsTimeout = -1; - const currentOriginalModel = this.originalEditor.getModel(); - const currentModifiedModel = this.modifiedEditor.getModel(); + const currentOriginalModel = this._originalEditor.getModel(); + const currentModifiedModel = this._modifiedEditor.getModel(); if (!currentOriginalModel || !currentModifiedModel) { return; } @@ -1031,7 +1036,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE // The best method would be to call cancel on the Promise, but this is not // yet supported, so using tokens for now. this._diffComputationToken++; - let currentToken = this._diffComputationToken; + const currentToken = this._diffComputationToken; this._setState(editorBrowser.DiffEditorState.ComputingDiff); if (!this._editorWorkerService.canComputeDiff(currentOriginalModel.uri, currentModifiedModel.uri)) { @@ -1048,8 +1053,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._editorWorkerService.computeDiff(currentOriginalModel.uri, currentModifiedModel.uri, this._ignoreTrimWhitespace, this._maxComputationTime).then((result) => { if (currentToken === this._diffComputationToken - && currentOriginalModel === this.originalEditor.getModel() - && currentModifiedModel === this.modifiedEditor.getModel() + && currentOriginalModel === this._originalEditor.getModel() + && currentModifiedModel === this._modifiedEditor.getModel() ) { this._setState(editorBrowser.DiffEditorState.DiffComputed); this._diffComputationResult = result; @@ -1058,8 +1063,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } }, (error) => { if (currentToken === this._diffComputationToken - && currentOriginalModel === this.originalEditor.getModel() - && currentModifiedModel === this.modifiedEditor.getModel() + && currentOriginalModel === this._originalEditor.getModel() + && currentModifiedModel === this._modifiedEditor.getModel() ) { this._setState(editorBrowser.DiffEditorState.DiffComputed); this._diffComputationResult = null; @@ -1069,40 +1074,38 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _cleanViewZonesAndDecorations(): void { - this._originalEditorState.clean(this.originalEditor); - this._modifiedEditorState.clean(this.modifiedEditor); + this._originalEditorState.clean(this._originalEditor); + this._modifiedEditorState.clean(this._modifiedEditor); } private _updateDecorations(): void { - if (!this.originalEditor.getModel() || !this.modifiedEditor.getModel() || !this._originalOverviewRuler || !this._modifiedOverviewRuler) { + if (!this._originalEditor.getModel() || !this._modifiedEditor.getModel() || !this._originalOverviewRuler || !this._modifiedOverviewRuler) { return; } const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []); - let foreignOriginal = this._originalEditorState.getForeignViewZones(this.originalEditor.getWhitespaces()); - let foreignModified = this._modifiedEditorState.getForeignViewZones(this.modifiedEditor.getWhitespaces()); + const foreignOriginal = this._originalEditorState.getForeignViewZones(this._originalEditor.getWhitespaces()); + const foreignModified = this._modifiedEditorState.getForeignViewZones(this._modifiedEditor.getWhitespaces()); - let diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified, this.originalEditor, this.modifiedEditor); + const diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._ignoreTrimWhitespace, this._renderIndicators, foreignOriginal, foreignModified); try { this._currentlyChangingViewZones = true; - this._originalEditorState.apply(this.originalEditor, this._originalOverviewRuler, diffDecorations.original, false); - this._modifiedEditorState.apply(this.modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified, true); + this._originalEditorState.apply(this._originalEditor, this._originalOverviewRuler, diffDecorations.original, false); + this._modifiedEditorState.apply(this._modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified, true); } finally { this._currentlyChangingViewZones = false; } } private _adjustOptionsForSubEditor(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IDiffEditorConstructionOptions { - let clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {}); + const clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {}); clonedOptions.inDiffEditor = true; - clonedOptions.wordWrap = 'off'; - clonedOptions.wordWrapMinified = false; clonedOptions.automaticLayout = false; clonedOptions.scrollbar = clonedOptions.scrollbar || {}; clonedOptions.scrollbar.vertical = 'visible'; clonedOptions.folding = false; - clonedOptions.codeLens = false; + clonedOptions.codeLens = this._diffCodeLens; clonedOptions.fixedOverflowWidgets = true; clonedOptions.overflowWidgetsDomNode = options.overflowWidgetsDomNode; // clonedOptions.lineDecorationsWidth = '2ch'; @@ -1113,21 +1116,22 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return clonedOptions; } - private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isEditable: boolean, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { - let result = this._adjustOptionsForSubEditor(options); - if (isCodeLensEnabled) { - result.codeLens = true; + private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IEditorConstructionOptions { + const result = this._adjustOptionsForSubEditor(options); + if (!this._renderSideBySide) { + // never wrap hidden editor + result.wordWrapOverride1 = 'off'; + } else { + result.wordWrapOverride1 = this._diffWordWrap; } - result.readOnly = !isEditable; + result.readOnly = !this._originalIsEditable; result.extraEditorClassName = 'original-in-monaco-diff-editor'; return result; } - private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions, isCodeLensEnabled: boolean): editorBrowser.IEditorConstructionOptions { - let result = this._adjustOptionsForSubEditor(options); - if (isCodeLensEnabled) { - result.codeLens = true; - } + private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IEditorConstructionOptions { + const result = this._adjustOptionsForSubEditor(options); + result.wordWrapOverride1 = this._diffWordWrap; result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; result.scrollbar!.verticalHasArrows = false; result.extraEditorClassName = 'modified-in-monaco-diff-editor'; @@ -1144,7 +1148,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE const height = this._elementSizeObserver.getHeight(); const reviewHeight = this._getReviewHeight(); - let splitPoint = this._strategy.layout(); + const splitPoint = this._strategy.layout(); this._originalDomNode.style.width = splitPoint + 'px'; this._originalDomNode.style.left = '0px'; @@ -1159,8 +1163,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH); this._overviewViewportDomElement.setHeight(30); - this.originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) }); - this.modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) }); + this._originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) }); + this._modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) }); if (this._originalOverviewRuler || this._modifiedOverviewRuler) { this._layoutOverviewRulers(); @@ -1172,7 +1176,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _layoutOverviewViewport(): void { - let layout = this._computeOverviewViewport(); + const layout = this._computeOverviewViewport(); if (!layout) { this._overviewViewportDomElement.setTop(0); this._overviewViewportDomElement.setHeight(0); @@ -1183,20 +1187,20 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _computeOverviewViewport(): { height: number; top: number; } | null { - let layoutInfo = this.modifiedEditor.getLayoutInfo(); + const layoutInfo = this._modifiedEditor.getLayoutInfo(); if (!layoutInfo) { return null; } - let scrollTop = this.modifiedEditor.getScrollTop(); - let scrollHeight = this.modifiedEditor.getScrollHeight(); + const scrollTop = this._modifiedEditor.getScrollTop(); + const scrollHeight = this._modifiedEditor.getScrollHeight(); - let computedAvailableSize = Math.max(0, layoutInfo.height); - let computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); - let computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; + const computedAvailableSize = Math.max(0, layoutInfo.height); + const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); + const computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; - let computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); - let computedSliderPosition = Math.floor(scrollTop * computedRatio); + const computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); + const computedSliderPosition = Math.floor(scrollTop * computedRatio); return { height: computedSliderSize, @@ -1223,16 +1227,16 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE }, getOriginalEditor: () => { - return this.originalEditor; + return this._originalEditor; }, getModifiedEditor: () => { - return this.modifiedEditor; + return this._modifiedEditor; } }; } - private _setStrategy(newStrategy: IDiffEditorWidgetStyle): void { + private _setStrategy(newStrategy: DiffEditorWidgetStyle): void { if (this._strategy) { this._strategy.dispose(); } @@ -1255,11 +1259,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return null; } - let min = 0, max = lineChanges.length - 1; + let min = 0; + let max = lineChanges.length - 1; while (min < max) { - let mid = Math.floor((min + max) / 2); - let midStart = startLineNumberExtractor(lineChanges[mid]); - let midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Constants.MAX_SAFE_SMALL_INTEGER); + const mid = Math.floor((min + max) / 2); + const midStart = startLineNumberExtractor(lineChanges[mid]); + const midEnd = (mid + 1 <= max ? startLineNumberExtractor(lineChanges[mid + 1]) : Constants.MAX_SAFE_SMALL_INTEGER); if (lineNumber < midStart) { max = mid - 1; @@ -1275,19 +1280,19 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _getEquivalentLineForOriginalLineNumber(lineNumber: number): number { - let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber); + const lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.originalStartLineNumber); if (!lineChange) { return lineNumber; } - let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); - let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + const originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + const modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + const lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + const lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); - let delta = lineNumber - originalEquivalentLineNumber; + const delta = lineNumber - originalEquivalentLineNumber; if (delta <= lineChangeOriginalLength) { return modifiedEquivalentLineNumber + Math.min(delta, lineChangeModifiedLength); @@ -1297,19 +1302,19 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } private _getEquivalentLineForModifiedLineNumber(lineNumber: number): number { - let lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber); + const lineChange = this._getLineChangeAtOrBeforeLineNumber(lineNumber, (lineChange) => lineChange.modifiedStartLineNumber); if (!lineChange) { return lineNumber; } - let originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); - let modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - let lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - let lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + const originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); + const modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); + const lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); + const lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); - let delta = lineNumber - modifiedEquivalentLineNumber; + const delta = lineNumber - modifiedEquivalentLineNumber; if (delta <= lineChangeModifiedLength) { return originalEquivalentLineNumber + Math.min(delta, lineChangeOriginalLength); @@ -1345,15 +1350,15 @@ interface IDataSource { getContainerDomNode(): HTMLElement; relayoutEditors(): void; - getOriginalEditor(): editorBrowser.ICodeEditor; - getModifiedEditor(): editorBrowser.ICodeEditor; + getOriginalEditor(): CodeEditorWidget; + getModifiedEditor(): CodeEditorWidget; } -abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWidgetStyle { +abstract class DiffEditorWidgetStyle extends Disposable { - _dataSource: IDataSource; - _insertColor: Color | null; - _removeColor: Color | null; + protected _dataSource: IDataSource; + protected _insertColor: Color | null; + protected _removeColor: Color | null; constructor(dataSource: IDataSource) { super(); @@ -1363,15 +1368,15 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi } public applyColors(theme: IColorTheme): boolean { - let newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); - let newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); - let hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); + const newInsertColor = (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); + const newRemoveColor = (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); + const hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); this._insertColor = newInsertColor; this._removeColor = newRemoveColor; return hasChanges; } - public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones { + public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[]): IEditorsDiffDecorationsWithZones { // Get view zones modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; @@ -1379,11 +1384,11 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi originalWhitespaces = originalWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; }); - let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators); + const zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, renderIndicators); // Get decorations & overview ruler zones - let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor); - let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalEditor, modifiedEditor); + const originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators); + const modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators); return { original: { @@ -1399,9 +1404,9 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi }; } - protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; - protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; - protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; + protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], renderIndicators: boolean): IEditorsZones; + protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations; + protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations; public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; public abstract layout(): number; @@ -1410,6 +1415,7 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi interface IMyViewZone { shouldNotShrink?: boolean; afterLineNumber: number; + afterColumn?: number; heightInLines: number; minWidthInPx?: number; domNode: HTMLElement | null; @@ -1442,22 +1448,37 @@ class ForeignViewZonesIterator { abstract class ViewZonesComputer { - private readonly lineChanges: editorCommon.ILineChange[]; - private readonly originalForeignVZ: IEditorWhitespace[]; - private readonly originalLineHeight: number; - private readonly modifiedForeignVZ: IEditorWhitespace[]; - private readonly modifiedLineHeight: number; + constructor( + private readonly _lineChanges: editorCommon.ILineChange[], + private readonly _originalForeignVZ: IEditorWhitespace[], + private readonly _modifiedForeignVZ: IEditorWhitespace[], + protected readonly _originalEditor: CodeEditorWidget, + protected readonly _modifiedEditor: CodeEditorWidget + ) { + } - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { - this.lineChanges = lineChanges; - this.originalForeignVZ = originalForeignVZ; - this.originalLineHeight = originalLineHeight; - this.modifiedForeignVZ = modifiedForeignVZ; - this.modifiedLineHeight = modifiedLineHeight; + private static _getViewLineCount(editor: CodeEditorWidget, startLineNumber: number, endLineNumber: number): number { + const model = editor.getModel(); + const viewModel = editor._getViewModel(); + if (model && viewModel) { + const viewRange = getViewRange(model, viewModel, startLineNumber, endLineNumber); + return (viewRange.endLineNumber - viewRange.startLineNumber + 1); + } + + return (endLineNumber - startLineNumber + 1); } public getViewZones(): IEditorsZones { - let result: { original: IMyViewZone[]; modified: IMyViewZone[]; } = { + const originalLineHeight = this._originalEditor.getOption(EditorOption.lineHeight); + const modifiedLineHeight = this._modifiedEditor.getOption(EditorOption.lineHeight); + const originalHasWrapping = (this._originalEditor.getOption(EditorOption.wrappingInfo).wrappingColumn !== -1); + const modifiedHasWrapping = (this._modifiedEditor.getOption(EditorOption.wrappingInfo).wrappingColumn !== -1); + const hasWrapping = (originalHasWrapping || modifiedHasWrapping); + const originalModel = this._originalEditor.getModel()!; + const originalCoordinatesConverter = this._originalEditor._getViewModel()!.coordinatesConverter; + const modifiedCoordinatesConverter = this._modifiedEditor._getViewModel()!.coordinatesConverter; + + const result: { original: IMyViewZone[]; modified: IMyViewZone[]; } = { original: [], modified: [] }; @@ -1469,13 +1490,13 @@ abstract class ViewZonesComputer { let originalEndEquivalentLineNumber: number = 0; let modifiedEndEquivalentLineNumber: number = 0; - let sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { + const sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { return a.afterLineNumber - b.afterLineNumber; }; - let addAndCombineIfPossible = (destination: IMyViewZone[], item: IMyViewZone) => { + const addAndCombineIfPossible = (destination: IMyViewZone[], item: IMyViewZone) => { if (item.domNode === null && destination.length > 0) { - let lastItem = destination[destination.length - 1]; + const lastItem = destination[destination.length - 1]; if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) { lastItem.heightInLines += item.heightInLines; return; @@ -1484,18 +1505,21 @@ abstract class ViewZonesComputer { destination.push(item); }; - let modifiedForeignVZ = new ForeignViewZonesIterator(this.modifiedForeignVZ); - let originalForeignVZ = new ForeignViewZonesIterator(this.originalForeignVZ); + const modifiedForeignVZ = new ForeignViewZonesIterator(this._modifiedForeignVZ); + const originalForeignVZ = new ForeignViewZonesIterator(this._originalForeignVZ); + + let lastOriginalLineNumber = 1; + let lastModifiedLineNumber = 1; // In order to include foreign view zones after the last line change, the for loop will iterate once more after the end of the `lineChanges` array - for (let i = 0, length = this.lineChanges.length; i <= length; i++) { - let lineChange = (i < length ? this.lineChanges[i] : null); + for (let i = 0, length = this._lineChanges.length; i <= length; i++) { + const lineChange = (i < length ? this._lineChanges[i] : null); if (lineChange !== null) { originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? (lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1) : 0); - lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? (lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1) : 0); + lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? ViewZonesComputer._getViewLineCount(this._originalEditor, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber) : 0); + lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? ViewZonesComputer._getViewLineCount(this._modifiedEditor, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber) : 0); originalEndEquivalentLineNumber = Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); modifiedEndEquivalentLineNumber = Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); } else { @@ -1512,6 +1536,48 @@ abstract class ViewZonesComputer { // ---------------------------- PRODUCE VIEW ZONES + // [PRODUCE] View zones due to line mapping differences (equal lines but wrapped differently) + if (hasWrapping) { + let count: number; + if (lineChange) { + if (lineChange.originalEndLineNumber > 0) { + count = lineChange.originalStartLineNumber - lastOriginalLineNumber; + } else { + count = lineChange.modifiedStartLineNumber - lastModifiedLineNumber; + } + } else { + count = originalModel.getLineCount() - lastOriginalLineNumber; + } + + for (let i = 0; i < count; i++) { + const originalLineNumber = lastOriginalLineNumber + i; + const modifiedLineNumber = lastModifiedLineNumber + i; + + const originalViewLineCount = originalCoordinatesConverter.getModelLineViewLineCount(originalLineNumber); + const modifiedViewLineCount = modifiedCoordinatesConverter.getModelLineViewLineCount(modifiedLineNumber); + + if (originalViewLineCount < modifiedViewLineCount) { + stepOriginal.push({ + afterLineNumber: originalLineNumber, + heightInLines: modifiedViewLineCount - originalViewLineCount, + domNode: null, + marginDomNode: null + }); + } else if (originalViewLineCount > modifiedViewLineCount) { + stepModified.push({ + afterLineNumber: modifiedLineNumber, + heightInLines: originalViewLineCount - modifiedViewLineCount, + domNode: null, + marginDomNode: null + }); + } + } + if (lineChange) { + lastOriginalLineNumber = (lineChange.originalEndLineNumber > 0 ? lineChange.originalEndLineNumber : lineChange.originalStartLineNumber) + 1; + lastModifiedLineNumber = (lineChange.modifiedEndLineNumber > 0 ? lineChange.modifiedEndLineNumber : lineChange.modifiedStartLineNumber) + 1; + } + } + // [PRODUCE] View zone(s) in original-side due to foreign view zone(s) in modified-side while (modifiedForeignVZ.current && modifiedForeignVZ.current.afterLineNumber <= modifiedEndEquivalentLineNumber) { let viewZoneLineNumber: number; @@ -1528,7 +1594,7 @@ abstract class ViewZonesComputer { stepOriginal.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: modifiedForeignVZ.current.height / this.modifiedLineHeight, + heightInLines: modifiedForeignVZ.current.height / modifiedLineHeight, domNode: null, marginDomNode: marginDomNode }); @@ -1545,21 +1611,21 @@ abstract class ViewZonesComputer { } stepModified.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: originalForeignVZ.current.height / this.originalLineHeight, + heightInLines: originalForeignVZ.current.height / originalLineHeight, domNode: null }); originalForeignVZ.advance(); } if (lineChange !== null && isChangeOrInsert(lineChange)) { - let r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + const r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); if (r) { stepOriginal.push(r); } } if (lineChange !== null && isChangeOrDelete(lineChange)) { - let r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); + const r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); if (r) { stepModified.push(r); } @@ -1578,11 +1644,11 @@ abstract class ViewZonesComputer { stepModified = stepModified.sort(sortMyViewZones); while (stepOriginalIndex < stepOriginal.length && stepModifiedIndex < stepModified.length) { - let original = stepOriginal[stepOriginalIndex]; - let modified = stepModified[stepModifiedIndex]; + const original = stepOriginal[stepOriginalIndex]; + const modified = stepModified[stepModifiedIndex]; - let originalDelta = original.afterLineNumber - originalEquivalentLineNumber; - let modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; + const originalDelta = original.afterLineNumber - originalEquivalentLineNumber; + const modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; if (originalDelta < modifiedDelta) { addAndCombineIfPossible(result.original, original); @@ -1646,14 +1712,14 @@ abstract class ViewZonesComputer { protected abstract _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null; } -export function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { +function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { return { range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), options: options }; } -export const DECORATIONS = { +const DECORATIONS = { charDelete: ModelDecorationOptions.register({ className: 'char-delete' @@ -1678,7 +1744,7 @@ export const DECORATIONS = { }), lineInsertWithSign: ModelDecorationOptions.register({ className: 'line-insert', - linesDecorationsClassName: 'insert-sign ' + diffInsertIcon.classNames, + linesDecorationsClassName: 'insert-sign ' + ThemeIcon.asClassName(diffInsertIcon), marginClassName: 'line-insert', isWholeLine: true }), @@ -1690,7 +1756,7 @@ export const DECORATIONS = { }), lineDeleteWithSign: ModelDecorationOptions.register({ className: 'line-delete', - linesDecorationsClassName: 'delete-sign ' + diffRemoveIcon.classNames, + linesDecorationsClassName: 'delete-sign ' + ThemeIcon.asClassName(diffRemoveIcon), marginClassName: 'line-delete', isWholeLine: true @@ -1701,7 +1767,7 @@ export const DECORATIONS = { }; -export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider { +class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerticalSashLayoutProvider { static readonly MINIMUM_EDITOR_WIDTH = 100; @@ -1724,14 +1790,14 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements this._sash.state = SashState.Disabled; } - this._sash.onDidStart(() => this.onSashDragStart()); - this._sash.onDidChange((e: ISashEvent) => this.onSashDrag(e)); - this._sash.onDidEnd(() => this.onSashDragEnd()); - this._sash.onDidReset(() => this.onSashReset()); + this._sash.onDidStart(() => this._onSashDragStart()); + this._sash.onDidChange((e: ISashEvent) => this._onSashDrag(e)); + this._sash.onDidEnd(() => this._onSashDragEnd()); + this._sash.onDidReset(() => this._onSashReset()); } public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { - let newDisableSash = (enableSplitViewResizing === false); + const newDisableSash = (enableSplitViewResizing === false); if (this._disableSash !== newDisableSash) { this._disableSash = newDisableSash; this._sash.state = this._disableSash ? SashState.Disabled : SashState.Enabled; @@ -1739,11 +1805,11 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements } public layout(sashRatio: number | null = this._sashRatio): number { - let w = this._dataSource.getWidth(); - let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + const w = this._dataSource.getWidth(); + const contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); - let midPoint = Math.floor(0.5 * contentWidth); + const midPoint = Math.floor(0.5 * contentWidth); sashPosition = this._disableSash ? midPoint : sashPosition || midPoint; @@ -1767,25 +1833,25 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements return this._sashPosition; } - private onSashDragStart(): void { + private _onSashDragStart(): void { this._startSashPosition = this._sashPosition!; } - private onSashDrag(e: ISashEvent): void { - let w = this._dataSource.getWidth(); - let contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; - let sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth); + private _onSashDrag(e: ISashEvent): void { + const w = this._dataSource.getWidth(); + const contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; + const sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth); this._sashRatio = sashPosition / contentWidth; this._dataSource.relayoutEditors(); } - private onSashDragEnd(): void { + private _onSashDragEnd(): void { this._sash.layout(); } - private onSashReset(): void { + private _onSashReset(): void { this._sashRatio = 0.5; this._dataSource.relayoutEditors(); this._sash.layout(); @@ -1803,23 +1869,26 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements return this._dataSource.getHeight(); } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { - let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]): IEditorsZones { + const originalEditor = this._dataSource.getOriginalEditor(); + const modifiedEditor = this._dataSource.getModifiedEditor(); + const c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); return c.getViewZones(); } - protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + const originalEditor = this._dataSource.getOriginalEditor(); const overviewZoneColor = String(this._removeColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - let originalModel = originalEditor.getModel()!; + const originalModel = originalEditor.getModel()!; + const originalViewModel = originalEditor._getViewModel()!; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + for (const lineChange of lineChanges) { if (isChangeOrDelete(lineChange)) { result.decorations.push({ @@ -1830,15 +1899,11 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charDeleteWholeLine)); } - result.overviewZones.push(new OverviewRulerZone( - lineChange.originalStartLineNumber, - lineChange.originalEndLineNumber, - overviewZoneColor - )); + const viewRange = getViewRange(originalModel, originalViewModel, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; + for (const charChange of lineChange.charChanges) { if (isChangeOrDelete(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { @@ -1868,18 +1933,19 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements return result; } - protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + const modifiedEditor = this._dataSource.getModifiedEditor(); const overviewZoneColor = String(this._insertColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - let modifiedModel = modifiedEditor.getModel()!; + const modifiedModel = modifiedEditor.getModel()!; + const modifiedViewModel = modifiedEditor._getViewModel()!; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + for (const lineChange of lineChanges) { if (isChangeOrInsert(lineChange)) { @@ -1890,15 +1956,12 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) { result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charInsertWholeLine)); } - result.overviewZones.push(new OverviewRulerZone( - lineChange.modifiedStartLineNumber, - lineChange.modifiedEndLineNumber, - overviewZoneColor - )); + + const viewRange = getViewRange(modifiedModel, modifiedViewModel, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; + for (const charChange of lineChange.charChanges) { if (isChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { @@ -1931,8 +1994,14 @@ export class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements class SideBySideViewZonesComputer extends ViewZonesComputer { - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { - super(lineChanges, originalForeignVZ, originalLineHeight, modifiedForeignVZ, modifiedLineHeight); + constructor( + lineChanges: editorCommon.ILineChange[], + originalForeignVZ: IEditorWhitespace[], + modifiedForeignVZ: IEditorWhitespace[], + originalEditor: CodeEditorWidget, + modifiedEditor: CodeEditorWidget, + ) { + super(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); } protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { @@ -1962,18 +2031,18 @@ class SideBySideViewZonesComputer extends ViewZonesComputer { } } -class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle { +class DiffEditorWidgetInline extends DiffEditorWidgetStyle { - private decorationsLeft: number; + private _decorationsLeft: number; constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) { super(dataSource); - this.decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft; + this._decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft; this._register(dataSource.getOriginalEditor().onDidLayoutChange((layoutInfo: EditorLayoutInfo) => { - if (this.decorationsLeft !== layoutInfo.decorationsLeft) { - this.decorationsLeft = layoutInfo.decorationsLeft; + if (this._decorationsLeft !== layoutInfo.decorationsLeft) { + this._decorationsLeft = layoutInfo.decorationsLeft; dataSource.relayoutEditors(); } })); @@ -1983,21 +2052,26 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito // Nothing to do.. } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { - let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], renderIndicators: boolean): IEditorsZones { + const originalEditor = this._dataSource.getOriginalEditor(); + const modifiedEditor = this._dataSource.getModifiedEditor(); + const computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); return computer.getViewZones(); } - protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { const overviewZoneColor = String(this._removeColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + const originalEditor = this._dataSource.getOriginalEditor(); + const originalModel = originalEditor.getModel()!; + const originalViewModel = originalEditor._getViewModel()!; + + for (const lineChange of lineChanges) { // Add overview zones in the overview ruler if (isChangeOrDelete(lineChange)) { @@ -2006,29 +2080,27 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito options: DECORATIONS.lineDeleteMargin }); - result.overviewZones.push(new OverviewRulerZone( - lineChange.originalStartLineNumber, - lineChange.originalEndLineNumber, - overviewZoneColor - )); + const viewRange = getViewRange(originalModel, originalViewModel, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); } } return result; } - protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations { + protected _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + const modifiedEditor = this._dataSource.getModifiedEditor(); const overviewZoneColor = String(this._insertColor); - let result: IEditorDiffDecorations = { + const result: IEditorDiffDecorations = { decorations: [], overviewZones: [] }; - let modifiedModel = modifiedEditor.getModel()!; + const modifiedModel = modifiedEditor.getModel()!; + const modifiedViewModel = modifiedEditor._getViewModel()!; - for (let i = 0, length = lineChanges.length; i < length; i++) { - let lineChange = lineChanges[i]; + for (const lineChange of lineChanges) { // Add decorations & overview zones if (isChangeOrInsert(lineChange)) { @@ -2037,15 +2109,11 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) }); - result.overviewZones.push(new OverviewRulerZone( - lineChange.modifiedStartLineNumber, - lineChange.modifiedEndLineNumber, - overviewZoneColor - )); + const viewRange = getViewRange(modifiedModel, modifiedViewModel, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); + result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, overviewZoneColor)); if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; + for (const charChange of lineChange.charChanges) { if (isChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { @@ -2079,34 +2147,59 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito public layout(): number { // An editor should not be smaller than 5px - return Math.max(5, this.decorationsLeft); + return Math.max(5, this._decorationsLeft); } } +interface InlineModifiedViewZone extends IMyViewZone { + shouldNotShrink: boolean; + afterLineNumber: number; + heightInLines: number; + minWidthInPx: number; + domNode: HTMLElement; + marginDomNode: HTMLElement; + diff: IDiffLinesChange; +} + class InlineViewZonesComputer extends ViewZonesComputer { - private readonly originalModel: ITextModel; - private readonly modifiedEditorOptions: IComputedEditorOptions; - private readonly modifiedEditorTabSize: number; - private readonly renderIndicators: boolean; + private readonly _originalModel: ITextModel; + private readonly _renderIndicators: boolean; + private readonly _pendingLineChange: editorCommon.ILineChange[]; + private readonly _pendingViewZones: InlineModifiedViewZone[]; + private readonly _lineBreaksComputer: ILineBreaksComputer; - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { - super(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); - this.originalModel = originalEditor.getModel()!; - this.modifiedEditorOptions = modifiedEditor.getOptions(); - this.modifiedEditorTabSize = modifiedEditor.getModel()!.getOptions().tabSize; - this.renderIndicators = renderIndicators; + constructor( + lineChanges: editorCommon.ILineChange[], + originalForeignVZ: IEditorWhitespace[], + modifiedForeignVZ: IEditorWhitespace[], + originalEditor: CodeEditorWidget, + modifiedEditor: CodeEditorWidget, + renderIndicators: boolean + ) { + super(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); + this._originalModel = originalEditor.getModel()!; + this._renderIndicators = renderIndicators; + this._pendingLineChange = []; + this._pendingViewZones = []; + this._lineBreaksComputer = this._modifiedEditor._getViewModel()!.createLineBreaksComputer(); + } + + public getViewZones(): IEditorsZones { + const result = super.getViewZones(); + this._finalize(result); + return result; } protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { - let result = document.createElement('div'); + const result = document.createElement('div'); result.className = 'inline-added-margin-view-zone'; return result; } protected _produceOriginalFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - let marginDomNode = document.createElement('div'); + const marginDomNode = document.createElement('div'); marginDomNode.className = 'inline-added-margin-view-zone'; return { @@ -2118,57 +2211,17 @@ class InlineViewZonesComputer extends ViewZonesComputer { } protected _produceModifiedFromDiff(lineChange: editorCommon.ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - let decorations: InlineDecoration[] = []; - if (lineChange.charChanges) { - for (let j = 0, lengthJ = lineChange.charChanges.length; j < lengthJ; j++) { - let charChange = lineChange.charChanges[j]; - if (isChangeOrDelete(charChange)) { - decorations.push(new InlineDecoration( - new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), - 'char-delete', - InlineDecorationType.Regular - )); - } - } - } - - let sb = createStringBuilder(10000); - let marginDomNode = document.createElement('div'); - const layoutInfo = this.modifiedEditorOptions.get(EditorOption.layoutInfo); - const fontInfo = this.modifiedEditorOptions.get(EditorOption.fontInfo); - const lineDecorationsWidth = layoutInfo.decorationsWidth; - - let lineHeight = this.modifiedEditorOptions.get(EditorOption.lineHeight); - const typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; - let maxCharsPerLine = 0; - const originalContent: string[] = []; - for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { - maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorOptions, this.modifiedEditorTabSize, lineNumber, decorations, sb)); - originalContent.push(this.originalModel.getLineContent(lineNumber)); - - if (this.renderIndicators) { - let index = lineNumber - lineChange.originalStartLineNumber; - const marginElement = document.createElement('div'); - marginElement.className = `delete-sign ${diffRemoveIcon.classNames}`; - marginElement.setAttribute('style', `position:absolute;top:${index * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;`); - marginDomNode.appendChild(marginElement); - } - } - maxCharsPerLine += this.modifiedEditorOptions.get(EditorOption.scrollBeyondLastColumn); - - let domNode = document.createElement('div'); + const domNode = document.createElement('div'); domNode.className = `view-lines line-delete ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`; - domNode.innerHTML = sb.build(); - Configuration.applyFontInfoSlow(domNode, fontInfo); + const marginDomNode = document.createElement('div'); marginDomNode.className = 'inline-deleted-margin-view-zone'; - Configuration.applyFontInfoSlow(marginDomNode, fontInfo); - return { + const viewZone: InlineModifiedViewZone = { shouldNotShrink: true, afterLineNumber: (lineChange.modifiedEndLineNumber === 0 ? lineChange.modifiedStartLineNumber : lineChange.modifiedStartLineNumber - 1), heightInLines: lineChangeOriginalLength, - minWidthInPx: (maxCharsPerLine * typicalHalfwidthCharacterWidth), + minWidthInPx: 0, domNode: domNode, marginDomNode: marginDomNode, diff: { @@ -2176,31 +2229,199 @@ class InlineViewZonesComputer extends ViewZonesComputer { originalEndLineNumber: lineChange.originalEndLineNumber, modifiedStartLineNumber: lineChange.modifiedStartLineNumber, modifiedEndLineNumber: lineChange.modifiedEndLineNumber, - originalContent: originalContent + originalModel: this._originalModel, + viewLineCounts: null, } }; + + for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { + this._lineBreaksComputer.addRequest(this._originalModel.getLineContent(lineNumber), null); + } + + this._pendingLineChange.push(lineChange); + this._pendingViewZones.push(viewZone); + + return viewZone; } - private _renderOriginalLine(count: number, originalModel: ITextModel, options: IComputedEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[], sb: IStringBuilder): number { - const lineTokens = originalModel.getLineTokens(lineNumber); - const lineContent = lineTokens.getLineContent(); - const fontInfo = options.get(EditorOption.fontInfo); + private _finalize(result: IEditorsZones): void { + const modifiedEditorOptions = this._modifiedEditor.getOptions(); + const tabSize = this._modifiedEditor.getModel()!.getOptions().tabSize; + const fontInfo = modifiedEditorOptions.get(EditorOption.fontInfo); + const disableMonospaceOptimizations = modifiedEditorOptions.get(EditorOption.disableMonospaceOptimizations); + const typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; + const scrollBeyondLastColumn = modifiedEditorOptions.get(EditorOption.scrollBeyondLastColumn); + const mightContainNonBasicASCII = this._originalModel.mightContainNonBasicASCII(); + const mightContainRTL = this._originalModel.mightContainRTL(); + const lineHeight = modifiedEditorOptions.get(EditorOption.lineHeight); + const layoutInfo = modifiedEditorOptions.get(EditorOption.layoutInfo); + const lineDecorationsWidth = layoutInfo.decorationsWidth; + const stopRenderingLineAfter = modifiedEditorOptions.get(EditorOption.stopRenderingLineAfter); + const renderWhitespace = modifiedEditorOptions.get(EditorOption.renderWhitespace); + const renderControlCharacters = modifiedEditorOptions.get(EditorOption.renderControlCharacters); + const fontLigatures = modifiedEditorOptions.get(EditorOption.fontLigatures); - const actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); + const lineBreaks = this._lineBreaksComputer.finalize(); + let lineBreakIndex = 0; + + for (let i = 0; i < this._pendingLineChange.length; i++) { + const lineChange = this._pendingLineChange[i]; + const viewZone = this._pendingViewZones[i]; + const domNode = viewZone.domNode; + Configuration.applyFontInfoSlow(domNode, fontInfo); + + const marginDomNode = viewZone.marginDomNode; + Configuration.applyFontInfoSlow(marginDomNode, fontInfo); + + const decorations: InlineDecoration[] = []; + if (lineChange.charChanges) { + for (const charChange of lineChange.charChanges) { + if (isChangeOrDelete(charChange)) { + decorations.push(new InlineDecoration( + new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), + 'char-delete', + InlineDecorationType.Regular + )); + } + } + } + const hasCharChanges = (decorations.length > 0); + + const sb = createStringBuilder(10000); + let maxCharsPerLine = 0; + let renderedLineCount = 0; + let viewLineCounts: number[] | null = null; + for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { + const lineIndex = lineNumber - lineChange.originalStartLineNumber; + const lineTokens = this._originalModel.getLineTokens(lineNumber); + const lineContent = lineTokens.getLineContent(); + const lineBreakData = lineBreaks[lineBreakIndex++]; + const actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); + + if (lineBreakData) { + let lastBreakOffset = 0; + for (const breakOffset of lineBreakData.breakOffsets) { + const viewLineTokens = lineTokens.sliceAndInflate(lastBreakOffset, breakOffset, 0); + const viewLineContent = lineContent.substring(lastBreakOffset, breakOffset); + maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine( + renderedLineCount++, + viewLineContent, + viewLineTokens, + LineDecoration.extractWrapped(actualDecorations, lastBreakOffset, breakOffset), + hasCharChanges, + mightContainNonBasicASCII, + mightContainRTL, + fontInfo, + disableMonospaceOptimizations, + lineHeight, + lineDecorationsWidth, + stopRenderingLineAfter, + renderWhitespace, + renderControlCharacters, + fontLigatures, + tabSize, + sb, + marginDomNode + )); + lastBreakOffset = breakOffset; + } + if (!viewLineCounts) { + viewLineCounts = []; + } + // make sure all lines before this one have an entry in `viewLineCounts` + while (viewLineCounts.length < lineIndex) { + viewLineCounts[viewLineCounts.length] = 1; + } + viewLineCounts[lineIndex] = lineBreakData.breakOffsets.length; + viewZone.heightInLines += (lineBreakData.breakOffsets.length - 1); + const marginDomNode2 = document.createElement('div'); + marginDomNode2.className = 'line-delete'; + result.original.push({ + afterLineNumber: lineNumber, + afterColumn: 0, + heightInLines: lineBreakData.breakOffsets.length - 1, + domNode: createFakeLinesDiv(), + marginDomNode: marginDomNode2 + }); + } else { + maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine( + renderedLineCount++, + lineContent, + lineTokens, + actualDecorations, + hasCharChanges, + mightContainNonBasicASCII, + mightContainRTL, + fontInfo, + disableMonospaceOptimizations, + lineHeight, + lineDecorationsWidth, + stopRenderingLineAfter, + renderWhitespace, + renderControlCharacters, + fontLigatures, + tabSize, + sb, + marginDomNode + )); + } + } + maxCharsPerLine += scrollBeyondLastColumn; + + const html = sb.build(); + const trustedhtml = ttPolicy ? ttPolicy.createHTML(html) : html; + domNode.innerHTML = trustedhtml as unknown as string; + viewZone.minWidthInPx = (maxCharsPerLine * typicalHalfwidthCharacterWidth); + + if (viewLineCounts) { + // make sure all lines have an entry in `viewLineCounts` + const cnt = lineChange.originalEndLineNumber - lineChange.originalStartLineNumber; + while (viewLineCounts.length <= cnt) { + viewLineCounts[viewLineCounts.length] = 1; + } + } + viewZone.diff.viewLineCounts = viewLineCounts; + } + + result.original.sort((a, b) => { + return a.afterLineNumber - b.afterLineNumber; + }); + } + + private _renderOriginalLine( + renderedLineCount: number, + lineContent: string, + lineTokens: IViewLineTokens, + decorations: LineDecoration[], + hasCharChanges: boolean, + mightContainNonBasicASCII: boolean, + mightContainRTL: boolean, + fontInfo: FontInfo, + disableMonospaceOptimizations: boolean, + lineHeight: number, + lineDecorationsWidth: number, + stopRenderingLineAfter: number, + renderWhitespace: 'selection' | 'none' | 'boundary' | 'trailing' | 'all', + renderControlCharacters: boolean, + fontLigatures: string, + tabSize: number, + sb: IStringBuilder, + marginDomNode: HTMLElement + ): number { sb.appendASCIIString('
'); - const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, originalModel.mightContainNonBasicASCII()); - const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, originalModel.mightContainRTL()); + const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, mightContainNonBasicASCII); + const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, mightContainRTL); const output = renderViewLine(new RenderLineInput( - (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)), + (fontInfo.isMonospace && !disableMonospaceOptimizations), fontInfo.canUseHalfwidthRightwardsArrow, lineContent, false, @@ -2208,40 +2429,61 @@ class InlineViewZonesComputer extends ViewZonesComputer { containsRTL, 0, lineTokens, - actualDecorations, + decorations, tabSize, 0, fontInfo.spaceWidth, fontInfo.middotWidth, fontInfo.wsmiddotWidth, - options.get(EditorOption.stopRenderingLineAfter), - options.get(EditorOption.renderWhitespace), - options.get(EditorOption.renderControlCharacters), - options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF, + stopRenderingLineAfter, + renderWhitespace, + renderControlCharacters, + fontLigatures !== EditorFontLigatures.OFF, null // Send no selections, original line cannot be selected ), sb); sb.appendASCIIString('
'); + if (this._renderIndicators) { + const marginElement = document.createElement('div'); + marginElement.className = `delete-sign ${ThemeIcon.asClassName(diffRemoveIcon)}`; + marginElement.setAttribute('style', `position:absolute;top:${renderedLineCount * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;`); + marginDomNode.appendChild(marginElement); + } + const absoluteOffsets = output.characterMapping.getAbsoluteOffsets(); return absoluteOffsets.length > 0 ? absoluteOffsets[absoluteOffsets.length - 1] : 0; } } -export function isChangeOrInsert(lineChange: editorCommon.IChange): boolean { +function validateDiffWordWrap(value: 'off' | 'on' | 'inherit' | undefined, defaultValue: 'off' | 'on' | 'inherit'): 'off' | 'on' | 'inherit' { + return validateStringSetOption<'off' | 'on' | 'inherit'>(value, defaultValue, ['off', 'on', 'inherit']); +} + +function isChangeOrInsert(lineChange: editorCommon.IChange): boolean { return lineChange.modifiedEndLineNumber > 0; } -export function isChangeOrDelete(lineChange: editorCommon.IChange): boolean { +function isChangeOrDelete(lineChange: editorCommon.IChange): boolean { return lineChange.originalEndLineNumber > 0; } function createFakeLinesDiv(): HTMLElement { - let r = document.createElement('div'); + const r = document.createElement('div'); r.className = 'diagonal-fill'; return r; } +function getViewRange(model: ITextModel, viewModel: IViewModel, startLineNumber: number, endLineNumber: number): Range { + const lineCount = model.getLineCount(); + startLineNumber = Math.min(lineCount, Math.max(1, startLineNumber)); + endLineNumber = Math.min(lineCount, Math.max(1, endLineNumber)); + return viewModel.coordinatesConverter.convertModelRangeToViewRange(new Range( + startLineNumber, model.getLineMinColumn(startLineNumber), + endLineNumber, model.getLineMaxColumn(endLineNumber) + )); +} + registerThemingParticipant((theme, collector) => { const added = theme.getColor(diffInserted); if (added) { diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 69326136..fd53314e 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -29,9 +29,10 @@ import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Constants } from 'vs/base/common/uint'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const DIFF_LINES_PADDING = 3; @@ -73,9 +74,9 @@ class Diff { } } -const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add); -const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove); -const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close); +const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add, nls.localize('diffReviewInsertIcon', 'Icon for \'Insert\' in diff review.')); +const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, nls.localize('diffReviewRemoveIcon', 'Icon for \'Remove\' in diff review.')); +const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close, nls.localize('diffReviewCloseIcon', 'Icon for \'Close\' in diff review.')); export class DiffReview extends Disposable { @@ -104,7 +105,7 @@ export class DiffReview extends Disposable { this.actionBarContainer.domNode )); - this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + diffReviewCloseIcon.classNames, true, () => { + this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + ThemeIcon.asClassName(diffReviewCloseIcon), true, () => { this.hide(); return Promise.resolve(null); }), { label: false, icon: true }); @@ -647,7 +648,7 @@ export class DiffReview extends Disposable { let rowClassName: string = 'diff-review-row'; let lineNumbersExtraClassName: string = ''; const spacerClassName: string = 'diff-review-spacer'; - let spacerIcon: Codicon | null = null; + let spacerIcon: ThemeIcon | null = null; switch (type) { case DiffEntryType.Insert: rowClassName = 'diff-review-row line-insert'; @@ -723,7 +724,7 @@ export class DiffReview extends Disposable { if (spacerIcon) { const spacerCodicon = document.createElement('span'); - spacerCodicon.className = spacerIcon.classNames; + spacerCodicon.className = ThemeIcon.asClassName(spacerIcon); spacerCodicon.innerText = '\u00a0\u00a0'; spacer.appendChild(spacerCodicon); } else { diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts index 7ae64d75..baf5aadc 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -14,13 +14,15 @@ import { Range } from 'vs/editor/common/core/range'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Codicon } from 'vs/base/common/codicons'; +import { ITextModel } from 'vs/editor/common/model'; export interface IDiffLinesChange { readonly originalStartLineNumber: number; readonly originalEndLineNumber: number; readonly modifiedStartLineNumber: number; readonly modifiedEndLineNumber: number; - readonly originalContent: string[]; + readonly originalModel: ITextModel; + viewLineCounts: number[] | null; } export class InlineDiffMargin extends Disposable { @@ -45,12 +47,12 @@ export class InlineDiffMargin extends Disposable { } constructor( - private _viewZoneId: string, - private _marginDomNode: HTMLElement, - public editor: CodeEditorWidget, - public diff: IDiffLinesChange, - private _contextMenuService: IContextMenuService, - private _clipboardService: IClipboardService + private readonly _viewZoneId: string, + private readonly _marginDomNode: HTMLElement, + public readonly editor: CodeEditorWidget, + public readonly diff: IDiffLinesChange, + private readonly _contextMenuService: IContextMenuService, + private readonly _clipboardService: IClipboardService ) { super(); @@ -79,7 +81,9 @@ export class InlineDiffMargin extends Disposable { undefined, true, async () => { - await this._clipboardService.writeText(diff.originalContent.join(lineFeed) + lineFeed); + const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber + 1, 1); + const deletedText = diff.originalModel.getValueInRange(range); + await this._clipboardService.writeText(deletedText); } )); @@ -92,7 +96,8 @@ export class InlineDiffMargin extends Disposable { undefined, true, async () => { - await this._clipboardService.writeText(diff.originalContent[currentLineNumberOffset]); + const lineContent = diff.originalModel.getLineContent(diff.originalStartLineNumber + currentLineNumberOffset); + await this._clipboardService.writeText(lineContent); } ); @@ -102,13 +107,15 @@ export class InlineDiffMargin extends Disposable { const readOnly = editor.getOption(EditorOption.readOnly); if (!readOnly) { actions.push(new Action('diff.inline.revertChange', nls.localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => { + const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber, diff.originalModel.getLineMaxColumn(diff.originalEndLineNumber)); + const deletedText = diff.originalModel.getValueInRange(range); if (diff.modifiedEndLineNumber === 0) { // deletion only const column = editor.getModel()!.getLineMaxColumn(diff.modifiedStartLineNumber); editor.executeEdits('diffEditor', [ { range: new Range(diff.modifiedStartLineNumber, column, diff.modifiedStartLineNumber, column), - text: lineFeed + diff.originalContent.join(lineFeed) + text: lineFeed + deletedText } ]); } else { @@ -116,7 +123,7 @@ export class InlineDiffMargin extends Disposable { editor.executeEdits('diffEditor', [ { range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber, column), - text: diff.originalContent.join(lineFeed) + text: deletedText } ]); } @@ -189,6 +196,15 @@ export class InlineDiffMargin extends Disposable { const lineNumberOffset = Math.floor(offset / lineHeight); const newTop = lineNumberOffset * lineHeight; this._diffActions.style.top = `${newTop}px`; + if (this.diff.viewLineCounts) { + let acc = 0; + for (let i = 0; i < this.diff.viewLineCounts.length; i++) { + acc += this.diff.viewLineCounts[i]; + if (lineNumberOffset < acc) { + return i; + } + } + } return lineNumberOffset; } } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 3bde5dfd..bf1d608f 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -502,6 +502,16 @@ const editorConfiguration: IConfigurationNode = { default: true, description: nls.localize('wordBasedSuggestions', "Controls whether completions should be computed based on words in the document.") }, + 'editor.wordBasedSuggestionsMode': { + enum: ['currentDocument', 'matchingDocuments', 'allDocuments'], + default: 'matchingDocuments', + enumDescriptions: [ + nls.localize('wordBasedSuggestionsMode.currentDocument', 'Only suggest words from the active document.'), + nls.localize('wordBasedSuggestionsMode.matchingDocuments', 'Suggest words from all open documents of the same language.'), + nls.localize('wordBasedSuggestionsMode.allDocuments', 'Suggest words from all open documents.') + ], + description: nls.localize('wordBasedSuggestionsMode', "Controls form what documents word based completions are computed.") + }, 'editor.semanticHighlighting.enabled': { enum: [true, false, 'configuredByTheme'], enumDescriptions: [ @@ -546,6 +556,16 @@ const editorConfiguration: IConfigurationNode = { type: 'boolean', default: false, description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") + }, + 'diffEditor.wordWrap': { + type: 'string', + enum: ['off', 'on', 'inherit'], + default: 'inherit', + markdownEnumDescriptions: [ + nls.localize('wordWrap.off', "Lines will never wrap."), + nls.localize('wordWrap.on', "Lines will wrap at the viewport width."), + nls.localize('wordWrap.inherit', "Lines will wrap according to the `#editor.wordWrap#` setting."), + ] } } }; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index a49bf9ce..1c6b481c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -144,9 +144,13 @@ export interface IEditorOptions { */ readOnly?: boolean; /** - * Rename matching regions on type. + * Enable linked editing. * Defaults to false. */ + linkedEditing?: boolean; + /** + * deprecated, use linkedEditing instead + */ renameOnType?: boolean; /** * Should the editor render validation decorations. @@ -260,6 +264,14 @@ export interface IEditorOptions { * Defaults to "off". */ wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded'; + /** + * Override the `wordWrap` setting. + */ + wordWrapOverride1?: 'off' | 'on' | 'inherit'; + /** + * Override the `wordWrapOverride1` setting. + */ + wordWrapOverride2?: 'off' | 'on' | 'inherit'; /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. @@ -269,11 +281,6 @@ export interface IEditorOptions { * Defaults to 80. */ wordWrapColumn?: number; - /** - * Force word wrapping when the text appears to be of a minified/generated file. - * Defaults to true. - */ - wordWrapMinified?: boolean; /** * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. * Defaults to 'same' in vscode and to 'none' in monaco-editor. @@ -370,6 +377,10 @@ export interface IEditorOptions { * Suggest options. */ suggest?: ISuggestOptions; + /** + * Smart select opptions; + */ + smartSelect?: ISmartSelectOptions; /** * */ @@ -416,6 +427,11 @@ export interface IEditorOptions { * Defaults to advanced. */ autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; + /** + * Emulate selection behaviour of tab characters when using spaces for indentation. + * This means selection will stick to tab stops. + */ + stickyTabStops?: boolean; /** * Enable format on type. * Defaults to false. @@ -491,6 +507,14 @@ export interface IEditorOptions { * Defaults to true. */ codeLens?: boolean; + /** + * Code lens font family. Defaults to editor font family. + */ + codeLensFontFamily?: string; + /** + * Code lens font size. Default to 90% of the editor font size + */ + codeLensFontSize?: number; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -644,15 +668,19 @@ export interface IDiffEditorOptions extends IEditorOptions { */ originalEditable?: boolean; /** - * Original editor should be have code lens enabled? + * Should the diff editor enable code lens? * Defaults to false. */ - originalCodeLens?: boolean; + diffCodeLens?: boolean; /** - * Modified editor should be have code lens enabled? - * Defaults to false. + * Is the diff editor inside another editor + * Defaults to false */ - modifiedCodeLens?: boolean; + isInEmbeddedEditor?: boolean; + /** + * Control the wrapping of the diff editor. + */ + diffWordWrap?: 'off' | 'on' | 'inherit'; } //#endregion @@ -828,18 +856,21 @@ class SimpleEditorOption implements IEditorOption extends SimpleEditorOption { - - public static boolean(value: any, defaultValue: boolean): boolean { - if (typeof value === 'undefined') { - return defaultValue; - } - if (value === 'false') { - // treat the string 'false' as false - return false; - } - return Boolean(value); +/** + * @internal + */ +export function boolean(value: any, defaultValue: boolean): boolean { + if (typeof value === 'undefined') { + return defaultValue; } + if (value === 'false') { + // treat the string 'false' as false + return false; + } + return Boolean(value); +} + +class EditorBooleanOption extends SimpleEditorOption { constructor(id: K1, name: PossibleKeyName, defaultValue: boolean, schema: IConfigurationPropertySchema | undefined = undefined) { if (typeof schema !== 'undefined') { @@ -850,7 +881,7 @@ class EditorBooleanOption extends SimpleEditorOption extends SimpleEditorOption extends SimpleEditorOption { - - public static stringSet(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray): T { - if (typeof value !== 'string') { - return defaultValue; - } - if (allowedValues.indexOf(value) === -1) { - return defaultValue; - } - return value; +/** + * @internal + */ +export function stringSet(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray): T { + if (typeof value !== 'string') { + return defaultValue; } + if (allowedValues.indexOf(value) === -1) { + return defaultValue; + } + return value; +} + +class EditorStringEnumOption extends SimpleEditorOption { private readonly _allowedValues: ReadonlyArray; @@ -975,7 +1009,7 @@ class EditorStringEnumOption extends } public validate(input: any): V { - return EditorStringEnumOption.stringSet(input, this.defaultValue, this._allowedValues); + return stringSet(input, this.defaultValue, this._allowedValues); } } @@ -1113,8 +1147,8 @@ class EditorComments extends BaseEditorOption } const input = _input as IEditorFindOptions; return { - cursorMoveOnType: EditorBooleanOption.boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), - seedSearchStringFromSelection: EditorBooleanOption.boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection), + cursorMoveOnType: boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), + seedSearchStringFromSelection: boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection), autoFindInSelection: typeof _input.autoFindInSelection === 'boolean' ? (_input.autoFindInSelection ? 'always' : 'never') - : EditorStringEnumOption.stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']), - globalFindClipboard: EditorBooleanOption.boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard), - addExtraSpaceOnTop: EditorBooleanOption.boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop), - loop: EditorBooleanOption.boolean(input.loop, this.defaultValue.loop), + : stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']), + globalFindClipboard: boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard), + addExtraSpaceOnTop: boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop), + loop: boolean(input.loop, this.defaultValue.loop), }; } } @@ -1406,14 +1440,14 @@ export class EditorFontLigatures extends BaseEditorOption(input.multiple, this.defaultValue.multiple!, ['peek', 'gotoAndPeek', 'goto']), - multipleDefinitions: input.multipleDefinitions ?? EditorStringEnumOption.stringSet(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleTypeDefinitions: input.multipleTypeDefinitions ?? EditorStringEnumOption.stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleDeclarations: input.multipleDeclarations ?? EditorStringEnumOption.stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleImplementations: input.multipleImplementations ?? EditorStringEnumOption.stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleReferences: input.multipleReferences ?? EditorStringEnumOption.stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multiple: stringSet(input.multiple, this.defaultValue.multiple!, ['peek', 'gotoAndPeek', 'goto']), + multipleDefinitions: input.multipleDefinitions ?? stringSet(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleTypeDefinitions: input.multipleTypeDefinitions ?? stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleDeclarations: input.multipleDeclarations ?? stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleImplementations: input.multipleImplementations ?? stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleReferences: input.multipleReferences ?? stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), alternativeDefinitionCommand: EditorStringOption.string(input.alternativeDefinitionCommand, this.defaultValue.alternativeDefinitionCommand), alternativeTypeDefinitionCommand: EditorStringOption.string(input.alternativeTypeDefinitionCommand, this.defaultValue.alternativeTypeDefinitionCommand), alternativeDeclarationCommand: EditorStringOption.string(input.alternativeDeclarationCommand, this.defaultValue.alternativeDeclarationCommand), @@ -1722,9 +1756,9 @@ class EditorHover extends BaseEditorOption(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']), - side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']), - showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']), - renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters), + enabled: boolean(input.enabled, this.defaultValue.enabled), + size: stringSet<'proportional' | 'fill' | 'fit'>(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']), + side: stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']), + showSlider: stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']), + renderCharacters: boolean(input.renderCharacters, this.defaultValue.renderCharacters), scale: EditorIntOption.clampedInt(input.scale, 1, 1, 3), maxColumn: EditorIntOption.clampedInt(input.maxColumn, this.defaultValue.maxColumn, 1, 10000), }; @@ -2598,8 +2634,8 @@ class EditorParameterHints extends BaseEditorOption>; + +class SmartSelect extends BaseEditorOption { + + constructor() { + super( + EditorOption.smartSelect, 'smartSelect', + { + selectLeadingAndTrailingWhitespace: true + }, + { + 'editor.smartSelect.selectLeadingAndTrailingWhitespace': { + description: nls.localize('selectLeadingAndTrailingWhitespace', "Whether leading and trailing whitespace should always be selected."), + default: true, + type: 'boolean' + } + } + ); + } + + public validate(input: any): Readonly> { + if (!input || typeof input !== 'object') { + return this.defaultValue; + } + return { + selectLeadingAndTrailingWhitespace: boolean((input as ISmartSelectOptions).selectLeadingAndTrailingWhitespace, this.defaultValue.selectLeadingAndTrailingWhitespace) }; } } @@ -3552,6 +3646,8 @@ export const enum EditorOption { automaticLayout, autoSurround, codeLens, + codeLensFontFamily, + codeLensFontSize, colorDecorators, columnSelection, comments, @@ -3594,6 +3690,7 @@ export const enum EditorOption { lineHeight, lineNumbers, lineNumbersMinChars, + linkedEditing, links, matchBrackets, minimap, @@ -3634,7 +3731,9 @@ export const enum EditorOption { showFoldingControls, showUnused, snippetSuggestions, + smartSelect, smoothScrolling, + stickyTabStops, stopRenderingLineAfter, suggest, suggestFontSize, @@ -3650,7 +3749,8 @@ export const enum EditorOption { wordWrapBreakAfterCharacters, wordWrapBreakBeforeCharacters, wordWrapColumn, - wordWrapMinified, + wordWrapOverride1, + wordWrapOverride2, wrappingIndent, wrappingStrategy, showDeprecated, @@ -3776,10 +3876,25 @@ export const EditorOptions = { description: nls.localize('autoSurround', "Controls whether the editor should automatically surround selections when typing quotes or brackets.") } )), + stickyTabStops: register(new EditorBooleanOption( + EditorOption.stickyTabStops, 'stickyTabStops', false, + { description: nls.localize('stickyTabStops', "Emulate selection behaviour of tab characters when using spaces for indentation. Selection will stick to tab stops.") } + )), codeLens: register(new EditorBooleanOption( EditorOption.codeLens, 'codeLens', true, { description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") } )), + codeLensFontFamily: register(new EditorStringOption( + EditorOption.codeLensFontFamily, 'codeLensFontFamily', '', + { description: nls.localize('codeLensFontFamily', "Controls the font family for CodeLens.") } + )), + codeLensFontSize: register(new EditorIntOption(EditorOption.codeLensFontSize, 'codeLensFontSize', 0, 0, 100, { + type: 'number', + default: 0, + minimum: 0, + maximum: 100, + description: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, the 90% of `#editor.fontSize#` is used.") + })), colorDecorators: register(new EditorBooleanOption( EditorOption.colorDecorators, 'colorDecorators', true, { description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") } @@ -3914,7 +4029,7 @@ export const EditorOptions = { )), hover: register(new EditorHover()), inDiffEditor: register(new EditorBooleanOption( - EditorOption.inDiffEditor, 'inDiffEditor', false, + EditorOption.inDiffEditor, 'inDiffEditor', false )), letterSpacing: register(new EditorFloatOption( EditorOption.letterSpacing, 'letterSpacing', @@ -3929,6 +4044,10 @@ export const EditorOptions = { EditorOption.lineNumbersMinChars, 'lineNumbersMinChars', 5, 1, 300 )), + linkedEditing: register(new EditorBooleanOption( + EditorOption.linkedEditing, 'linkedEditing', false, + { description: nls.localize('linkedEditing', "Controls whether the editor has linked editing enabled. Depending on the language, related symbols, e.g. HTML tags, are updated while editing.") } + )), links: register(new EditorBooleanOption( EditorOption.links, 'links', true, { description: nls.localize('links', "Controls whether the editor should detect links and make them clickable.") } @@ -4030,7 +4149,7 @@ export const EditorOptions = { )), renameOnType: register(new EditorBooleanOption( EditorOption.renameOnType, 'renameOnType', false, - { description: nls.localize('renameOnType', "Controls whether the editor auto renames on type.") } + { description: nls.localize('renameOnType', "Controls whether the editor auto renames on type."), markdownDeprecationMessage: nls.localize('renameOnTypeDeprecate', "Deprecated, use `editor.linkedEditing` instead.") } )), renderControlCharacters: register(new EditorBooleanOption( EditorOption.renderControlCharacters, 'renderControlCharacters', false, @@ -4153,6 +4272,7 @@ export const EditorOptions = { description: nls.localize('snippetSuggestions', "Controls whether snippets are shown with other suggestions and how they are sorted.") } )), + smartSelect: register(new SmartSelect()), smoothScrolling: register(new EditorBooleanOption( EditorOption.smoothScrolling, 'smoothScrolling', false, { description: nls.localize('smoothScrolling', "Controls whether the editor will scroll using an animation.") } @@ -4170,7 +4290,7 @@ export const EditorOptions = { suggestLineHeight: register(new EditorIntOption( EditorOption.suggestLineHeight, 'suggestLineHeight', 0, 0, 1000, - { markdownDescription: nls.localize('suggestLineHeight', "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used.") } + { markdownDescription: nls.localize('suggestLineHeight', "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used. The minimum value is 8.") } )), suggestOnTriggerCharacters: register(new EditorBooleanOption( EditorOption.suggestOnTriggerCharacters, 'suggestOnTriggerCharacters', true, @@ -4279,8 +4399,15 @@ export const EditorOptions = { }, "Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.") } )), - wordWrapMinified: register(new EditorBooleanOption( - EditorOption.wordWrapMinified, 'wordWrapMinified', true, + wordWrapOverride1: register(new EditorStringEnumOption( + EditorOption.wordWrapOverride1, 'wordWrapOverride1', + 'inherit' as 'off' | 'on' | 'inherit', + ['off', 'on', 'inherit'] as const + )), + wordWrapOverride2: register(new EditorStringEnumOption( + EditorOption.wordWrapOverride2, 'wordWrapOverride2', + 'inherit' as 'off' | 'on' | 'inherit', + ['off', 'on', 'inherit'] as const )), wrappingIndent: register(new EditorEnumOption( EditorOption.wrappingIndent, 'wrappingIndent', diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 7a966ed2..2bb67894 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -531,7 +531,7 @@ export class Cursor extends Disposable { } const closeChar = m[1]; - const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairsClose2.get(closeChar); + const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairs.autoClosingPairsCloseSingleChar.get(closeChar); if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) { return null; } diff --git a/src/vs/editor/common/controller/cursorAtomicMoveOperations.ts b/src/vs/editor/common/controller/cursorAtomicMoveOperations.ts new file mode 100644 index 00000000..4b3a2352 --- /dev/null +++ b/src/vs/editor/common/controller/cursorAtomicMoveOperations.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from 'vs/base/common/charCode'; +import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; + +export const enum Direction { + Left, + Right, + Nearest, +} + +export class AtomicTabMoveOperations { + /** + * Get the visible column at the position. If we get to a non-whitespace character first + * or past the end of string then return -1. + * + * **Note** `position` and the return value are 0-based. + */ + public static whitespaceVisibleColumn(lineContent: string, position: number, tabSize: number): [number, number, number] { + const lineLength = lineContent.length; + let visibleColumn = 0; + let prevTabStopPosition = -1; + let prevTabStopVisibleColumn = -1; + for (let i = 0; i < lineLength; i++) { + if (i === position) { + return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn]; + } + if (visibleColumn % tabSize === 0) { + prevTabStopPosition = i; + prevTabStopVisibleColumn = visibleColumn; + } + const chCode = lineContent.charCodeAt(i); + switch (chCode) { + case CharCode.Space: + visibleColumn += 1; + break; + case CharCode.Tab: + // Skip to the next multiple of tabSize. + visibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize); + break; + default: + return [-1, -1, -1]; + } + } + if (position === lineLength) { + return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn]; + } + return [-1, -1, -1]; + } + + /** + * Return the position that should result from a move left, right or to the + * nearest tab, if atomic tabs are enabled. Left and right are used for the + * arrow key movements, nearest is used for mouse selection. It returns + * -1 if atomic tabs are not relevant and you should fall back to normal + * behaviour. + * + * **Note**: `position` and the return value are 0-based. + */ + public static atomicPosition(lineContent: string, position: number, tabSize: number, direction: Direction): number { + const lineLength = lineContent.length; + + // Get the 0-based visible column corresponding to the position, or return + // -1 if it is not in the initial whitespace. + const [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn] = AtomicTabMoveOperations.whitespaceVisibleColumn(lineContent, position, tabSize); + + if (visibleColumn === -1) { + return -1; + } + + // Is the output left or right of the current position. The case for nearest + // where it is the same as the current position is handled in the switch. + let left: boolean; + switch (direction) { + case Direction.Left: + left = true; + break; + case Direction.Right: + left = false; + break; + case Direction.Nearest: + // The code below assumes the output position is either left or right + // of the input position. If it is the same, return immediately. + if (visibleColumn % tabSize === 0) { + return position; + } + // Go to the nearest indentation. + left = visibleColumn % tabSize <= (tabSize / 2); + break; + } + + // If going left, we can just use the info about the last tab stop position and + // last tab stop visible column that we computed in the first walk over the whitespace. + if (left) { + if (prevTabStopPosition === -1) { + return -1; + } + // If the direction is left, we need to keep scanning right to ensure + // that targetVisibleColumn + tabSize is before non-whitespace. + // This is so that when we press left at the end of a partial + // indentation it only goes one character. For example ' foo' with + // tabSize 4, should jump from position 6 to position 5, not 4. + let currentVisibleColumn = prevTabStopVisibleColumn; + for (let i = prevTabStopPosition; i < lineLength; ++i) { + if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) { + // It is a full indentation. + return prevTabStopPosition; + } + + const chCode = lineContent.charCodeAt(i); + switch (chCode) { + case CharCode.Space: + currentVisibleColumn += 1; + break; + case CharCode.Tab: + currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize); + break; + default: + return -1; + } + } + if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) { + return prevTabStopPosition; + } + // It must have been a partial indentation. + return -1; + } + + // We are going right. + const targetVisibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize); + + // We can just continue from where whitespaceVisibleColumn got to. + let currentVisibleColumn = visibleColumn; + for (let i = position; i < lineLength; i++) { + if (currentVisibleColumn === targetVisibleColumn) { + return i; + } + + const chCode = lineContent.charCodeAt(i); + switch (chCode) { + case CharCode.Space: + currentVisibleColumn += 1; + break; + case CharCode.Tab: + currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize); + break; + default: + return -1; + } + } + // This condition handles when the target column is at the end of the line. + if (currentVisibleColumn === targetVisibleColumn) { + return lineLength; + } + return -1; + } +} diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 92d36e23..e35e89e0 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -14,7 +14,7 @@ import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon'; import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; -import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { AutoClosingPairs, IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; import { Constants } from 'vs/base/common/uint'; @@ -62,6 +62,7 @@ export class CursorConfiguration { public readonly tabSize: number; public readonly indentSize: number; public readonly insertSpaces: boolean; + public readonly stickyTabStops: boolean; public readonly pageSize: number; public readonly lineHeight: number; public readonly useTabStops: boolean; @@ -75,8 +76,7 @@ export class CursorConfiguration { public readonly autoClosingOvertype: EditorAutoClosingOvertypeStrategy; public readonly autoSurround: EditorAutoSurroundStrategy; public readonly autoIndent: EditorAutoIndentStrategy; - public readonly autoClosingPairsOpen2: Map; - public readonly autoClosingPairsClose2: Map; + public readonly autoClosingPairs: AutoClosingPairs; public readonly surroundingPairs: CharacterMap; public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean, bracket: (ch: string) => boolean }; @@ -114,6 +114,7 @@ export class CursorConfiguration { this.tabSize = modelOptions.tabSize; this.indentSize = modelOptions.indentSize; this.insertSpaces = modelOptions.insertSpaces; + this.stickyTabStops = options.get(EditorOption.stickyTabStops); this.lineHeight = options.get(EditorOption.lineHeight); this.pageSize = Math.max(1, Math.floor(layoutInfo.height / this.lineHeight) - 2); this.useTabStops = options.get(EditorOption.useTabStops); @@ -136,9 +137,7 @@ export class CursorConfiguration { bracket: CursorConfiguration._getShouldAutoClose(languageIdentifier, this.autoClosingBrackets) }; - const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); - this.autoClosingPairsOpen2 = autoClosingPairs.autoClosingPairsOpen; - this.autoClosingPairsClose2 = autoClosingPairs.autoClosingPairsClose; + this.autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); let surroundingPairs = CursorConfiguration._getSurroundingPairs(languageIdentifier); if (surroundingPairs) { @@ -557,14 +556,14 @@ export class CursorColumns { } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) */ public static prevRenderTabStop(column: number, tabSize: number): number { return column - 1 - (column - 1) % tabSize; } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) */ public static prevIndentTabStop(column: number, indentSize: number): number { return column - 1 - (column - 1) % indentSize; diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 1147e78a..1a4a86d8 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -122,7 +122,7 @@ export class DeleteOperations { public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, Array] { - if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairsOpen2, model, selections)) { + if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections)) { return this._runAutoClosingPairDelete(config, model, selections); } diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index bc9bbb1c..909a2255 100644 --- a/src/vs/editor/common/controller/cursorMoveCommands.ts +++ b/src/vs/editor/common/controller/cursorMoveCommands.ts @@ -412,7 +412,11 @@ export class CursorMoveCommands { const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection(); let newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns); - if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { + if (skipWrappingPointStop + && noOfColumns === 1 + && cursor.viewState.position.column === viewModel.getLineMinColumn(cursor.viewState.position.lineNumber) + && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber + ) { // moved over to the previous view line const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) { @@ -445,7 +449,11 @@ export class CursorMoveCommands { const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection(); let newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns); - if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) { + if (skipWrappingPointStop + && noOfColumns === 1 + && cursor.viewState.position.column === viewModel.getLineMaxColumn(cursor.viewState.position.lineNumber) + && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber + ) { // moved over to the next view line const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position); if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) { diff --git a/src/vs/editor/common/controller/cursorMoveOperations.ts b/src/vs/editor/common/controller/cursorMoveOperations.ts index 20ca2894..9229c2e3 100644 --- a/src/vs/editor/common/controller/cursorMoveOperations.ts +++ b/src/vs/editor/common/controller/cursorMoveOperations.ts @@ -8,6 +8,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import * as strings from 'vs/base/common/strings'; import { Constants } from 'vs/base/common/uint'; +import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations'; export class CursorPosition { _cursorPositionBrand: void; @@ -35,8 +36,20 @@ export class MoveOperations { return new Position(lineNumber, column); } + public static leftPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number): Position { + const minColumn = model.getLineMinColumn(lineNumber); + const lineContent = model.getLineContent(lineNumber); + const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Left); + if (newPosition === -1) { + return this.leftPosition(model, lineNumber, column); + } + return new Position(lineNumber, minColumn + newPosition); + } + public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { - const pos = MoveOperations.leftPosition(model, lineNumber, column); + const pos = config.stickyTabStops + ? MoveOperations.leftPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize) + : MoveOperations.leftPosition(model, lineNumber, column); return new CursorPosition(pos.lineNumber, pos.column, 0); } @@ -67,8 +80,20 @@ export class MoveOperations { return new Position(lineNumber, column); } + public static rightPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number, indentSize: number): Position { + const minColumn = model.getLineMinColumn(lineNumber); + const lineContent = model.getLineContent(lineNumber); + const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Right); + if (newPosition === -1) { + return this.rightPosition(model, lineNumber, column); + } + return new Position(lineNumber, minColumn + newPosition); + } + public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { - const pos = MoveOperations.rightPosition(model, lineNumber, column); + const pos = config.stickyTabStops + ? MoveOperations.rightPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize, config.indentSize) + : MoveOperations.rightPosition(model, lineNumber, column); return new CursorPosition(pos.lineNumber, pos.column, 0); } diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index d383ffc6..75da0485 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -128,7 +128,7 @@ export class TypeOperations { if (text.charCodeAt(text.length - 1) === CharCode.CarriageReturn) { text = text.substr(0, text.length - 1); } - let lines = text.split(/\r\n|\r|\n/); + let lines = strings.splitLines(text); if (lines.length === selections.length) { return lines; } @@ -439,7 +439,7 @@ export class TypeOperations { return false; } - if (!config.autoClosingPairsClose2.has(ch)) { + if (!config.autoClosingPairs.autoClosingPairsCloseSingleChar.has(ch)) { return false; } @@ -498,31 +498,20 @@ export class TypeOperations { }); } - private static _autoClosingPairIsSymmetric(autoClosingPair: StandardAutoClosingPairConditional): boolean { - const { open, close } = autoClosingPair; - return (open.indexOf(close) >= 0 || close.indexOf(open) >= 0); - } + private static _isBeforeClosingBrace(config: CursorConfiguration, lineAfter: string) { + // If the start of lineAfter can be interpretted as both a starting or ending brace, default to returning false + const nextChar = lineAfter.charAt(0); + const potentialStartingBraces = config.autoClosingPairs.autoClosingPairsOpenByStart.get(nextChar) || []; + const potentialClosingBraces = config.autoClosingPairs.autoClosingPairsCloseByStart.get(nextChar) || []; - private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) { - const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter); - if (!otherAutoClosingPairs) { - return false; - } + const isBeforeStartingBrace = potentialStartingBraces.some(x => lineAfter.startsWith(x.open)); + const isBeforeClosingBrace = potentialClosingBraces.some(x => lineAfter.startsWith(x.close)); - const thisBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(autoClosingPair); - for (const otherAutoClosingPair of otherAutoClosingPairs) { - const otherBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(otherAutoClosingPair); - if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { - continue; - } - return true; - } - - return false; + return !isBeforeStartingBrace && isBeforeClosingBrace; } private static _findAutoClosingPairOpen(config: CursorConfiguration, model: ITextModel, positions: Position[], ch: string): StandardAutoClosingPairConditional | null { - const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(ch); + const autoClosingPairCandidates = config.autoClosingPairs.autoClosingPairsOpenByEnd.get(ch); if (!autoClosingPairCandidates) { return null; } @@ -548,7 +537,29 @@ export class TypeOperations { return autoClosingPair; } - private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): StandardAutoClosingPairConditional | null { + private static _findSubAutoClosingPairClose(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional): string { + if (autoClosingPair.open.length <= 1) { + return ''; + } + const lastChar = autoClosingPair.close.charAt(autoClosingPair.close.length - 1); + // get candidates with the same last character as close + const subPairCandidates = config.autoClosingPairs.autoClosingPairsCloseByEnd.get(lastChar) || []; + let subPairMatch: StandardAutoClosingPairConditional | null = null; + for (const x of subPairCandidates) { + if (x.open !== autoClosingPair.open && autoClosingPair.open.includes(x.open) && autoClosingPair.close.endsWith(x.close)) { + if (!subPairMatch || x.open.length > subPairMatch.open.length) { + subPairMatch = x; + } + } + } + if (subPairMatch) { + return subPairMatch.close; + } else { + return ''; + } + } + + private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): string | null { const chIsQuote = isQuote(ch); const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; if (autoCloseConfig === 'never') { @@ -560,6 +571,9 @@ export class TypeOperations { return null; } + const subAutoClosingPairClose = this._findSubAutoClosingPairClose(config, autoClosingPair); + let isSubAutoClosingPairPresent = true; + const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; for (let i = 0, len = selections.length; i < len; i++) { @@ -570,11 +584,16 @@ export class TypeOperations { const position = selection.getPosition(); const lineText = model.getLineContent(position.lineNumber); + const lineAfter = lineText.substring(position.column - 1); - // Only consider auto closing the pair if a space follows or if another autoclosed pair follows + if (!lineAfter.startsWith(subAutoClosingPairClose)) { + isSubAutoClosingPairPresent = false; + } + + // Only consider auto closing the pair if an allowed character follows or if another autoclosed pair closing brace follows if (lineText.length > position.column - 1) { const characterAfter = lineText.charAt(position.column - 1); - const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, autoClosingPair, characterAfter); + const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, lineAfter); if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) { return null; @@ -612,14 +631,18 @@ export class TypeOperations { } } - return autoClosingPair; + if (isSubAutoClosingPairPresent) { + return autoClosingPair.close.substring(0, autoClosingPair.close.length - subAutoClosingPairClose.length); + } else { + return autoClosingPair.close; + } } - private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPair: StandardAutoClosingPairConditional): EditOperationResult { + private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPairClose: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; - commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPair.close); + commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPairClose); } return new EditOperationResult(EditOperationType.Typing, commands, { shouldPushStackElementBefore: true, @@ -794,9 +817,9 @@ export class TypeOperations { }); } - const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, false); - if (autoClosingPairOpenCharType) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairOpenCharType); + const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, false); + if (autoClosingPairClose !== null) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairClose); } return null; @@ -838,9 +861,9 @@ export class TypeOperations { } if (!isDoingComposition) { - const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true); - if (autoClosingPairOpenCharType) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType); + const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, true); + if (autoClosingPairClose) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairClose); } } diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index c67d8f96..73820f9a 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -384,7 +384,7 @@ export class WordOperations { return selection; } - if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpen, ctx.model, [ctx.selection])) { + if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection])) { const position = ctx.selection.getPosition(); return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1); } @@ -438,6 +438,122 @@ export class WordOperations { return new Range(lineNumber, column, position.lineNumber, position.column); } + public static deleteInsideWord(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let r = this._deleteInsideWordWhitespace(model, position); + if (r) { + return r; + } + + return this._deleteInsideWordDetermineDeleteRange(wordSeparators, model, position); + } + + private static _charAtIsWhitespace(str: string, index: number): boolean { + const charCode = str.charCodeAt(index); + return (charCode === CharCode.Space || charCode === CharCode.Tab); + } + + private static _deleteInsideWordWhitespace(model: ICursorSimpleModel, position: Position): Range | null { + const lineContent = model.getLineContent(position.lineNumber); + const lineContentLength = lineContent.length; + + if (lineContentLength === 0) { + // empty line + return null; + } + + let leftIndex = Math.max(position.column - 2, 0); + if (!this._charAtIsWhitespace(lineContent, leftIndex)) { + // touches a non-whitespace character to the left + return null; + } + + let rightIndex = Math.min(position.column - 1, lineContentLength - 1); + if (!this._charAtIsWhitespace(lineContent, rightIndex)) { + // touches a non-whitespace character to the right + return null; + } + + // walk over whitespace to the left + while (leftIndex > 0 && this._charAtIsWhitespace(lineContent, leftIndex - 1)) { + leftIndex--; + } + + // walk over whitespace to the right + while (rightIndex + 1 < lineContentLength && this._charAtIsWhitespace(lineContent, rightIndex + 1)) { + rightIndex++; + } + + return new Range(position.lineNumber, leftIndex + 1, position.lineNumber, rightIndex + 2); + } + + private static _deleteInsideWordDetermineDeleteRange(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): Range { + const lineContent = model.getLineContent(position.lineNumber); + const lineLength = lineContent.length; + if (lineLength === 0) { + // empty line + if (position.lineNumber > 1) { + return new Range(position.lineNumber - 1, model.getLineMaxColumn(position.lineNumber - 1), position.lineNumber, 1); + } else { + if (position.lineNumber < model.getLineCount()) { + return new Range(position.lineNumber, 1, position.lineNumber + 1, 1); + } else { + // empty model + return new Range(position.lineNumber, 1, position.lineNumber, 1); + } + } + } + + const touchesWord = (word: IFindWordResult) => { + return (word.start + 1 <= position.column && position.column <= word.end + 1); + }; + const createRangeWithPosition = (startColumn: number, endColumn: number) => { + startColumn = Math.min(startColumn, position.column); + endColumn = Math.max(endColumn, position.column); + return new Range(position.lineNumber, startColumn, position.lineNumber, endColumn); + }; + const deleteWordAndAdjacentWhitespace = (word: IFindWordResult) => { + let startColumn = word.start + 1; + let endColumn = word.end + 1; + let expandedToTheRight = false; + while (endColumn - 1 < lineLength && this._charAtIsWhitespace(lineContent, endColumn - 1)) { + expandedToTheRight = true; + endColumn++; + } + if (!expandedToTheRight) { + while (startColumn > 1 && this._charAtIsWhitespace(lineContent, startColumn - 2)) { + startColumn--; + } + } + return createRangeWithPosition(startColumn, endColumn); + }; + + const prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position); + if (prevWordOnLine && touchesWord(prevWordOnLine)) { + return deleteWordAndAdjacentWhitespace(prevWordOnLine); + } + const nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, position); + if (nextWordOnLine && touchesWord(nextWordOnLine)) { + return deleteWordAndAdjacentWhitespace(nextWordOnLine); + } + if (prevWordOnLine && nextWordOnLine) { + return createRangeWithPosition(prevWordOnLine.end + 1, nextWordOnLine.start + 1); + } + if (prevWordOnLine) { + return createRangeWithPosition(prevWordOnLine.start + 1, prevWordOnLine.end + 1); + } + if (nextWordOnLine) { + return createRangeWithPosition(nextWordOnLine.start + 1, nextWordOnLine.end + 1); + } + + return createRangeWithPosition(1, lineLength + 1); + } + public static _deleteWordPartLeft(model: ICursorSimpleModel, selection: Selection): Range { if (!selection.isEmpty()) { return selection; diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index 487e6d4f..a3a3b3de 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -24,6 +24,7 @@ export namespace EditorContextKeys { export const textInputFocus = new RawContextKey('textInputFocus', false); export const readOnly = new RawContextKey('editorReadonly', false); + export const inDiffEditor = new RawContextKey('inDiffEditor', false); export const columnSelection = new RawContextKey('editorColumnSelection', false); export const writable = readOnly.toNegated(); export const hasNonEmptySelection = new RawContextKey('editorHasSelection', false); diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index fd5c9ccb..8f4d53a8 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -695,6 +695,11 @@ export interface ITextModel { */ getEOL(): string; + /** + * Get the end of line sequence predominantly used in the text buffer. + */ + getEndOfLineSequence(): EndOfLineSequence; + /** * Get the minimum legal column for line at `lineNumber` */ @@ -850,7 +855,12 @@ export interface ITextModel { /** * @internal */ - hasSemanticTokens(): boolean; + hasCompleteSemanticTokens(): boolean; + + /** + * @internal + */ + hasSomeSemanticTokens(): boolean; /** * Flush all tokenization state. @@ -1089,12 +1099,17 @@ export interface ITextModel { detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void; /** - * Push a stack element onto the undo stack. This acts as an undo/redo point. - * The idea is to use `pushEditOperations` to edit the model and then to - * `pushStackElement` to create an undo/redo stop point. + * Close the current undo-redo element. + * This offers a way to create an undo/redo stop point. */ pushStackElement(): void; + /** + * Open the current undo-redo element. + * This offers a way to remove the current undo/redo stop point. + */ + popStackElement(): void; + /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. @@ -1138,7 +1153,7 @@ export interface ITextModel { _applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; /** - * Undo edit operations until the first previous stop point created by `pushStackElement`. + * Undo edit operations until the previous undo/redo point. * The inverse edit operations will be pushed on the redo stack. * @internal */ @@ -1151,7 +1166,7 @@ export interface ITextModel { canUndo(): boolean; /** - * Redo edit operations until the next stop point created by `pushStackElement`. + * Redo edit operations until the next undo/redo point. * The inverse edit operations will be pushed on the undo stack. * @internal */ diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 0928d7d0..d06f447b 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -199,6 +199,12 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement { } } + public open(): void { + if (!(this._data instanceof SingleModelEditStackData)) { + this._data = SingleModelEditStackData.deserialize(this._data); + } + } + public undo(): void { if (URI.isUri(this.model)) { // don't have a model @@ -315,6 +321,10 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement { this._isOpen = false; } + public open(): void { + // cannot reopen + } + public undo(): void { this._isOpen = false; @@ -386,6 +396,13 @@ export class EditStack { } } + public popStackElement(): void { + const lastElement = this._undoRedoService.getLastElement(this._model.uri); + if (isEditStackElement(lastElement)) { + lastElement.open(); + } + } + public clear(): void { this._undoRedoService.removeElements(this._model.uri); } diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/model/mirrorTextModel.ts index 6a127255..9e14ea63 100644 --- a/src/vs/editor/common/model/mirrorTextModel.ts +++ b/src/vs/editor/common/model/mirrorTextModel.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { splitLines } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; @@ -131,7 +132,7 @@ export class MirrorTextModel { // Nothing to insert return; } - let insertLines = insertText.split(/\r\n|\r|\n/); + let insertLines = splitLines(insertText); if (insertLines.length === 1) { // Inserting text on one line this._setLineText(position.lineNumber - 1, diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 62ab2929..2b73835b 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -12,7 +12,7 @@ import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTex import { SearchData } from 'vs/editor/common/model/textModelSearch'; import { countEOL, StringEOL } from 'vs/editor/common/model/tokensStore'; import { TextChange } from 'vs/editor/common/model/textChange'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IValidatedEditOperation { sortIndex: number; @@ -32,26 +32,24 @@ export interface IReverseSingleEditOperation extends IValidEditOperation { sortIndex: number; } -export class PieceTreeTextBuffer implements ITextBuffer, IDisposable { - private readonly _pieceTree: PieceTreeBase; +export class PieceTreeTextBuffer extends Disposable implements ITextBuffer { + private _pieceTree: PieceTreeBase; private readonly _BOM: string; private _mightContainRTL: boolean; private _mightContainUnusualLineTerminators: boolean; private _mightContainNonBasicASCII: boolean; - private readonly _onDidChangeContent: Emitter = new Emitter(); + private readonly _onDidChangeContent: Emitter = this._register(new Emitter()); public readonly onDidChangeContent: Event = this._onDidChangeContent.event; constructor(chunks: StringBuffer[], BOM: string, eol: '\r\n' | '\n', containsRTL: boolean, containsUnusualLineTerminators: boolean, isBasicASCII: boolean, eolNormalized: boolean) { + super(); this._BOM = BOM; this._mightContainNonBasicASCII = !isBasicASCII; this._mightContainRTL = containsRTL; this._mightContainUnusualLineTerminators = containsUnusualLineTerminators; this._pieceTree = new PieceTreeBase(chunks, eol, eolNormalized); } - dispose(): void { - this._onDidChangeContent.dispose(); - } // #region TextBuffer public equals(other: ITextBuffer): boolean { diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 24f25a4e..35dd4664 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -387,6 +387,9 @@ export class TextModel extends Disposable implements model.ITextModel { this._isDisposed = true; super.dispose(); this._isDisposing = false; + // Manually release reference to previous text buffer to avoid large leaks + // in case someone leaks a TextModel reference + this._buffer = createTextBuffer('', this._options.defaultEOL); } private _assertNotDisposed(): void { @@ -830,6 +833,15 @@ export class TextModel extends Disposable implements model.ITextModel { return this._buffer.getEOL(); } + public getEndOfLineSequence(): model.EndOfLineSequence { + this._assertNotDisposed(); + return ( + this._buffer.getEOL() === '\n' + ? model.EndOfLineSequence.LF + : model.EndOfLineSequence.CRLF + ); + } + public getLineMinColumn(lineNumber: number): number { this._assertNotDisposed(); return 1; @@ -1213,6 +1225,10 @@ export class TextModel extends Disposable implements model.ITextModel { this._commandManager.pushStackElement(); } + public popStackElement(): void { + this._commandManager.popStackElement(); + } + public pushEOL(eol: model.EndOfLineSequence): void { const currentEOL = (this.getEOL() === '\n' ? model.EndOfLineSequence.LF : model.EndOfLineSequence.CRLF); if (currentEOL === eol) { @@ -1868,12 +1884,16 @@ export class TextModel extends Disposable implements model.ITextModel { }); } - public hasSemanticTokens(): boolean { + public hasCompleteSemanticTokens(): boolean { return this._tokens2.isComplete(); } + public hasSomeSemanticTokens(): boolean { + return !this._tokens2.isEmpty(); + } + public setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[]): void { - if (this.hasSemanticTokens()) { + if (this.hasCompleteSemanticTokens()) { return; } const changedRange = this._tokens2.setPartial(range, tokens); diff --git a/src/vs/editor/common/model/tokensStore.ts b/src/vs/editor/common/model/tokensStore.ts index a49ef27a..bbec0e36 100644 --- a/src/vs/editor/common/model/tokensStore.ts +++ b/src/vs/editor/common/model/tokensStore.ts @@ -884,6 +884,10 @@ export class TokensStore2 { this._isComplete = false; } + public isEmpty(): boolean { + return (this._pieces.length === 0); + } + public set(pieces: MultilineTokens2[] | null, isComplete: boolean): void { this._pieces = pieces || []; this._isComplete = isComplete; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 0ab1352c..fe0d7335 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -19,7 +19,7 @@ import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationReg import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { iconRegistry, Codicon } from 'vs/base/common/codicons'; - +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; /** * Open ended enum at runtime * @internal @@ -819,17 +819,32 @@ export interface DocumentHighlightProvider { } /** - * The rename provider interface defines the contract between extensions and - * the live-rename feature. + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. */ -export interface OnTypeRenameProvider { - - wordPattern?: RegExp; +export interface LinkedEditingRangeProvider { /** - * Provide a list of ranges that can be live-renamed together. + * Provide a list of ranges that can be edited together. */ - provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ ranges: IRange[]; wordPattern?: RegExp; }>; + provideLinkedEditingRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid contents. + */ +export interface LinkedEditingRanges { + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap + */ + ranges: IRange[]; + + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + wordPattern?: RegExp; } /** @@ -1359,7 +1374,10 @@ export interface WorkspaceEditMetadata { needsConfirmation: boolean; label: string; description?: string; - iconPath?: { id: string } | URI | { light: URI, dark: URI }; + /** + * @internal + */ + iconPath?: ThemeIcon | URI | { light: URI, dark: URI }; } export interface WorkspaceFileEditOptions { @@ -1367,6 +1385,10 @@ export interface WorkspaceFileEditOptions { ignoreIfNotExists?: boolean; ignoreIfExists?: boolean; recursive?: boolean; + copy?: boolean; + folder?: boolean; + skipTrashBin?: boolean; + maxSize?: number; } export interface WorkspaceFileEdit { @@ -1715,7 +1737,7 @@ export const DocumentHighlightProviderRegistry = new LanguageFeatureRegistry(); +export const LinkedEditingRangeProviderRegistry = new LanguageFeatureRegistry(); /** * @internal diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 295fbdc0..a383c57e 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -294,17 +294,32 @@ export class StandardAutoClosingPairConditional { * @internal */ export class AutoClosingPairs { + // it is useful to be able to get pairs using either end of open and close - public readonly autoClosingPairsOpen: Map; - public readonly autoClosingPairsClose: Map; + /** Key is first character of open */ + public readonly autoClosingPairsOpenByStart: Map; + /** Key is last character of open */ + public readonly autoClosingPairsOpenByEnd: Map; + /** Key is first character of close */ + public readonly autoClosingPairsCloseByStart: Map; + /** Key is last character of close */ + public readonly autoClosingPairsCloseByEnd: Map; + /** Key is close. Only has pairs that are a single character */ + public readonly autoClosingPairsCloseSingleChar: Map; constructor(autoClosingPairs: StandardAutoClosingPairConditional[]) { - this.autoClosingPairsOpen = new Map(); - this.autoClosingPairsClose = new Map(); + this.autoClosingPairsOpenByStart = new Map(); + this.autoClosingPairsOpenByEnd = new Map(); + this.autoClosingPairsCloseByStart = new Map(); + this.autoClosingPairsCloseByEnd = new Map(); + this.autoClosingPairsCloseSingleChar = new Map(); for (const pair of autoClosingPairs) { - appendEntry(this.autoClosingPairsOpen, pair.open.charAt(pair.open.length - 1), pair); - if (pair.close.length === 1) { - appendEntry(this.autoClosingPairsClose, pair.close, pair); + appendEntry(this.autoClosingPairsOpenByStart, pair.open.charAt(0), pair); + appendEntry(this.autoClosingPairsOpenByEnd, pair.open.charAt(pair.open.length - 1), pair); + appendEntry(this.autoClosingPairsCloseByStart, pair.close.charAt(0), pair); + appendEntry(this.autoClosingPairsCloseByEnd, pair.close.charAt(pair.close.length - 1), pair); + if (pair.close.length === 1 && pair.open.length === 1) { + appendEntry(this.autoClosingPairsCloseSingleChar, pair.close, pair); } } } diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 2d7600d3..ccd20e37 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -622,6 +622,12 @@ export class LanguageConfigurationRegistryImpl { return null; } const scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); + + if (scopedLineTokens.firstCharOffset) { + // this line has mixed languages and indentation rules will not work + return null; + } + const indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); if (!indentRulesSupport) { return null; diff --git a/src/vs/editor/common/modes/supports/indentRules.ts b/src/vs/editor/common/modes/supports/indentRules.ts index 687115bd..3241358c 100644 --- a/src/vs/editor/common/modes/supports/indentRules.ts +++ b/src/vs/editor/common/modes/supports/indentRules.ts @@ -12,6 +12,14 @@ export const enum IndentConsts { UNINDENT_MASK = 0b00001000, } +function resetGlobalRegex(reg: RegExp) { + if (reg.global) { + reg.lastIndex = 0; + } + + return true; +} + export class IndentRulesSupport { private readonly _indentationRules: IndentationRule; @@ -22,7 +30,7 @@ export class IndentRulesSupport { public shouldIncrease(text: string): boolean { if (this._indentationRules) { - if (this._indentationRules.increaseIndentPattern && this._indentationRules.increaseIndentPattern.test(text)) { + if (this._indentationRules.increaseIndentPattern && resetGlobalRegex(this._indentationRules.increaseIndentPattern) && this._indentationRules.increaseIndentPattern.test(text)) { return true; } // if (this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) { @@ -33,14 +41,14 @@ export class IndentRulesSupport { } public shouldDecrease(text: string): boolean { - if (this._indentationRules && this._indentationRules.decreaseIndentPattern && this._indentationRules.decreaseIndentPattern.test(text)) { + if (this._indentationRules && this._indentationRules.decreaseIndentPattern && resetGlobalRegex(this._indentationRules.decreaseIndentPattern) && this._indentationRules.decreaseIndentPattern.test(text)) { return true; } return false; } public shouldIndentNextLine(text: string): boolean { - if (this._indentationRules && this._indentationRules.indentNextLinePattern && this._indentationRules.indentNextLinePattern.test(text)) { + if (this._indentationRules && this._indentationRules.indentNextLinePattern && resetGlobalRegex(this._indentationRules.indentNextLinePattern) && this._indentationRules.indentNextLinePattern.test(text)) { return true; } @@ -49,7 +57,7 @@ export class IndentRulesSupport { public shouldIgnore(text: string): boolean { // the text matches `unIndentedLinePattern` - if (this._indentationRules && this._indentationRules.unIndentedLinePattern && this._indentationRules.unIndentedLinePattern.test(text)) { + if (this._indentationRules && this._indentationRules.unIndentedLinePattern && resetGlobalRegex(this._indentationRules.unIndentedLinePattern) && this._indentationRules.unIndentedLinePattern.test(text)) { return true; } diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts index dd64e79d..c4866641 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -101,7 +101,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens function _tokenizeToString(text: string, tokenizationSupport: IReducedTokenizationSupport): string { let result = `
`; - let lines = text.split(/\r\n|\r|\n/); + let lines = strings.splitLines(text); let currentState = tokenizationSupport.getInitialState(); for (let i = 0, len = lines.length; i < len; i++) { let line = lines[i]; diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 69592e73..68a3d125 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -530,36 +530,30 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { private static readonly _suggestionsLimit = 10000; - public async textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise<{ words: string[], duration: number } | null> { - const model = this._getModel(modelUrl); - if (!model) { - return null; - } + public async textualSuggest(modelUrls: string[], leadingWord: string | undefined, wordDef: string, wordDefFlags: string): Promise<{ words: string[], duration: number } | null> { const sw = new StopWatch(true); - const words: string[] = []; - const seen = new Set(); const wordDefRegExp = new RegExp(wordDef, wordDefFlags); + const seen = new Set(); - const wordAt = model.getWordAtPosition(position, wordDefRegExp); - if (wordAt) { - seen.add(model.getValueInRange(wordAt)); - } - - for (let word of model.words(wordDefRegExp)) { - if (seen.has(word)) { + outer: for (let url of modelUrls) { + const model = this._getModel(url); + if (!model) { continue; } - seen.add(word); - if (!isNaN(Number(word))) { - continue; - } - words.push(word); - if (seen.size > EditorSimpleWorker._suggestionsLimit) { - break; + + for (let word of model.words(wordDefRegExp)) { + if (word === leadingWord || !isNaN(Number(word))) { + continue; + } + seen.add(word); + if (seen.size > EditorSimpleWorker._suggestionsLimit) { + break outer; + } } } - return { words, duration: sw.elapsed() }; + + return { words: Array.from(seen), duration: sw.elapsed() }; } diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index 5976df3c..dc6a0f70 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IntervalTimer } from 'vs/base/common/async'; +import { IntervalTimer, timeout } from 'vs/base/common/async'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/base/common/worker/simpleWorker'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; -import { IPosition, Position } from 'vs/editor/common/core/position'; +import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { IChange } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; @@ -105,7 +105,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker const sw = StopWatch.create(true); const result = this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits)); result.finally(() => this._logService.trace('FORMAT#computeMoreMinimalEdits', resource.toString(true), sw.elapsed())); - return result; + return Promise.race([result, timeout(1000).then(() => edits)]); } else { return Promise.resolve(undefined); @@ -148,20 +148,47 @@ class WordBasedCompletionItemProvider implements modes.CompletionItemProvider { } async provideCompletionItems(model: ITextModel, position: Position): Promise { - const { wordBasedSuggestions } = this._configurationService.getValue<{ wordBasedSuggestions?: boolean }>(model.uri, position, 'editor'); - if (!wordBasedSuggestions) { + type WordBasedSuggestionsConfig = { + wordBasedSuggestions?: boolean, + wordBasedSuggestionsMode?: 'currentDocument' | 'matchingDocuments' | 'allDocuments' + }; + const config = this._configurationService.getValue(model.uri, position, 'editor'); + if (!config.wordBasedSuggestions) { return undefined; } - if (!canSyncModel(this._modelService, model.uri)) { - return undefined; // File too large + + const models: URI[] = []; + if (config.wordBasedSuggestionsMode === 'currentDocument') { + // only current file and only if not too large + if (canSyncModel(this._modelService, model.uri)) { + models.push(model.uri); + } + } else { + // either all files or files of same language + for (const candidate of this._modelService.getModels()) { + if (!canSyncModel(this._modelService, candidate.uri)) { + continue; + } + if (candidate === model) { + models.unshift(candidate.uri); + + } else if (config.wordBasedSuggestionsMode === 'allDocuments' || candidate.getLanguageIdentifier().id === model.getLanguageIdentifier().id) { + models.push(candidate.uri); + } + } } + if (models.length === 0) { + return undefined; // File too large, no other files + } + + const wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); const word = model.getWordAtPosition(position); const replace = !word ? Range.fromPositions(position) : new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); const insert = replace.setEndPosition(position.lineNumber, position.column); const client = await this._workerManager.withWorker(); - const data = await client.textualSuggest(model.uri, position); + const data = await client.textualSuggest(models, word?.word, wordDefRegExp); if (!data) { return undefined; } @@ -463,17 +490,11 @@ export class EditorWorkerClient extends Disposable { }); } - public textualSuggest(resource: URI, position: IPosition): Promise<{ words: string[], duration: number } | null> { - return this._withSyncedResources([resource]).then(proxy => { - let model = this._modelService.getModel(resource); - if (!model) { - return null; - } - let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id); - let wordDef = wordDefRegExp.source; - let wordDefFlags = regExpFlags(wordDefRegExp); - return proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags); - }); + public async textualSuggest(resources: URI[], leadingWord: string | undefined, wordDefRegExp: RegExp): Promise<{ words: string[], duration: number } | null> { + const proxy = await this._withSyncedResources(resources); + const wordDef = wordDefRegExp.source; + const wordDefFlags = regExpFlags(wordDefRegExp); + return proxy.textualSuggest(resources.map(r => r.toString()), leadingWord, wordDef, wordDefFlags); } computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null> { diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index b20a0b7c..60aca88a 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -93,6 +93,6 @@ export function detectModeId(modelService: IModelService, modeService: IModeServ return modeService.getModeIdByFilepathOrFirstLine(resource); } -export function cssEscape(val: string): string { - return val.replace(/\s/g, '\\$&'); // make sure to not introduce CSS classes from files that contain whitespace +export function cssEscape(str: string): string { + return str.replace(/[\11\12\14\15\40]/g, '/'); // HTML class names can not contain certain whitespace characters, use / instead, which doesn't exist in file names. } diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 7836fa5d..86672768 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -790,6 +790,10 @@ class ModelSemanticColoring extends Disposable { } const provider = this._getSemanticColoringProvider(); if (!provider) { + if (this._currentDocumentResponse) { + // there are semantic tokens set + this._model.setSemanticTokens(null, false); + } return; } this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource(); diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 01bf8eab..8c772e3c 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -179,113 +179,119 @@ export enum EditorOption { automaticLayout = 9, autoSurround = 10, codeLens = 11, - colorDecorators = 12, - columnSelection = 13, - comments = 14, - contextmenu = 15, - copyWithSyntaxHighlighting = 16, - cursorBlinking = 17, - cursorSmoothCaretAnimation = 18, - cursorStyle = 19, - cursorSurroundingLines = 20, - cursorSurroundingLinesStyle = 21, - cursorWidth = 22, - disableLayerHinting = 23, - disableMonospaceOptimizations = 24, - dragAndDrop = 25, - emptySelectionClipboard = 26, - extraEditorClassName = 27, - fastScrollSensitivity = 28, - find = 29, - fixedOverflowWidgets = 30, - folding = 31, - foldingStrategy = 32, - foldingHighlight = 33, - unfoldOnClickAfterEndOfLine = 34, - fontFamily = 35, - fontInfo = 36, - fontLigatures = 37, - fontSize = 38, - fontWeight = 39, - formatOnPaste = 40, - formatOnType = 41, - glyphMargin = 42, - gotoLocation = 43, - hideCursorInOverviewRuler = 44, - highlightActiveIndentGuide = 45, - hover = 46, - inDiffEditor = 47, - letterSpacing = 48, - lightbulb = 49, - lineDecorationsWidth = 50, - lineHeight = 51, - lineNumbers = 52, - lineNumbersMinChars = 53, - links = 54, - matchBrackets = 55, - minimap = 56, - mouseStyle = 57, - mouseWheelScrollSensitivity = 58, - mouseWheelZoom = 59, - multiCursorMergeOverlapping = 60, - multiCursorModifier = 61, - multiCursorPaste = 62, - occurrencesHighlight = 63, - overviewRulerBorder = 64, - overviewRulerLanes = 65, - padding = 66, - parameterHints = 67, - peekWidgetDefaultFocus = 68, - definitionLinkOpensInPeek = 69, - quickSuggestions = 70, - quickSuggestionsDelay = 71, - readOnly = 72, - renameOnType = 73, - renderControlCharacters = 74, - renderIndentGuides = 75, - renderFinalNewline = 76, - renderLineHighlight = 77, - renderLineHighlightOnlyWhenFocus = 78, - renderValidationDecorations = 79, - renderWhitespace = 80, - revealHorizontalRightPadding = 81, - roundedSelection = 82, - rulers = 83, - scrollbar = 84, - scrollBeyondLastColumn = 85, - scrollBeyondLastLine = 86, - scrollPredominantAxis = 87, - selectionClipboard = 88, - selectionHighlight = 89, - selectOnLineNumbers = 90, - showFoldingControls = 91, - showUnused = 92, - snippetSuggestions = 93, - smoothScrolling = 94, - stopRenderingLineAfter = 95, - suggest = 96, - suggestFontSize = 97, - suggestLineHeight = 98, - suggestOnTriggerCharacters = 99, - suggestSelection = 100, - tabCompletion = 101, - tabIndex = 102, - unusualLineTerminators = 103, - useTabStops = 104, - wordSeparators = 105, - wordWrap = 106, - wordWrapBreakAfterCharacters = 107, - wordWrapBreakBeforeCharacters = 108, - wordWrapColumn = 109, - wordWrapMinified = 110, - wrappingIndent = 111, - wrappingStrategy = 112, - showDeprecated = 113, - editorClassName = 114, - pixelRatio = 115, - tabFocusMode = 116, - layoutInfo = 117, - wrappingInfo = 118 + codeLensFontFamily = 12, + codeLensFontSize = 13, + colorDecorators = 14, + columnSelection = 15, + comments = 16, + contextmenu = 17, + copyWithSyntaxHighlighting = 18, + cursorBlinking = 19, + cursorSmoothCaretAnimation = 20, + cursorStyle = 21, + cursorSurroundingLines = 22, + cursorSurroundingLinesStyle = 23, + cursorWidth = 24, + disableLayerHinting = 25, + disableMonospaceOptimizations = 26, + dragAndDrop = 27, + emptySelectionClipboard = 28, + extraEditorClassName = 29, + fastScrollSensitivity = 30, + find = 31, + fixedOverflowWidgets = 32, + folding = 33, + foldingStrategy = 34, + foldingHighlight = 35, + unfoldOnClickAfterEndOfLine = 36, + fontFamily = 37, + fontInfo = 38, + fontLigatures = 39, + fontSize = 40, + fontWeight = 41, + formatOnPaste = 42, + formatOnType = 43, + glyphMargin = 44, + gotoLocation = 45, + hideCursorInOverviewRuler = 46, + highlightActiveIndentGuide = 47, + hover = 48, + inDiffEditor = 49, + letterSpacing = 50, + lightbulb = 51, + lineDecorationsWidth = 52, + lineHeight = 53, + lineNumbers = 54, + lineNumbersMinChars = 55, + linkedEditing = 56, + links = 57, + matchBrackets = 58, + minimap = 59, + mouseStyle = 60, + mouseWheelScrollSensitivity = 61, + mouseWheelZoom = 62, + multiCursorMergeOverlapping = 63, + multiCursorModifier = 64, + multiCursorPaste = 65, + occurrencesHighlight = 66, + overviewRulerBorder = 67, + overviewRulerLanes = 68, + padding = 69, + parameterHints = 70, + peekWidgetDefaultFocus = 71, + definitionLinkOpensInPeek = 72, + quickSuggestions = 73, + quickSuggestionsDelay = 74, + readOnly = 75, + renameOnType = 76, + renderControlCharacters = 77, + renderIndentGuides = 78, + renderFinalNewline = 79, + renderLineHighlight = 80, + renderLineHighlightOnlyWhenFocus = 81, + renderValidationDecorations = 82, + renderWhitespace = 83, + revealHorizontalRightPadding = 84, + roundedSelection = 85, + rulers = 86, + scrollbar = 87, + scrollBeyondLastColumn = 88, + scrollBeyondLastLine = 89, + scrollPredominantAxis = 90, + selectionClipboard = 91, + selectionHighlight = 92, + selectOnLineNumbers = 93, + showFoldingControls = 94, + showUnused = 95, + snippetSuggestions = 96, + smartSelect = 97, + smoothScrolling = 98, + stickyTabStops = 99, + stopRenderingLineAfter = 100, + suggest = 101, + suggestFontSize = 102, + suggestLineHeight = 103, + suggestOnTriggerCharacters = 104, + suggestSelection = 105, + tabCompletion = 106, + tabIndex = 107, + unusualLineTerminators = 108, + useTabStops = 109, + wordSeparators = 110, + wordWrap = 111, + wordWrapBreakAfterCharacters = 112, + wordWrapBreakBeforeCharacters = 113, + wordWrapColumn = 114, + wordWrapOverride1 = 115, + wordWrapOverride2 = 116, + wrappingIndent = 117, + wrappingStrategy = 118, + showDeprecated = 119, + editorClassName = 120, + pixelRatio = 121, + tabFocusMode = 122, + layoutInfo = 123, + wrappingInfo = 124 } /** diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index 804d1b97..bcdc69bb 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -11,6 +11,8 @@ import { ScrollType } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; export const enum ViewEventType { + ViewCompositionStart, + ViewCompositionEnd, ViewConfigurationChanged, ViewCursorStateChanged, ViewDecorationsChanged, @@ -29,6 +31,16 @@ export const enum ViewEventType { ViewZonesChanged, } +export class ViewCompositionStartEvent { + public readonly type = ViewEventType.ViewCompositionStart; + constructor() { } +} + +export class ViewCompositionEndEvent { + public readonly type = ViewEventType.ViewCompositionEnd; + constructor() { } +} + export class ViewConfigurationChangedEvent { public readonly type = ViewEventType.ViewConfigurationChanged; @@ -285,7 +297,9 @@ export class ViewZonesChangedEvent { } export type ViewEvent = ( - ViewConfigurationChangedEvent + ViewCompositionStartEvent + | ViewCompositionEndEvent + | ViewConfigurationChangedEvent | ViewCursorStateChangedEvent | ViewDecorationsChangedEvent | ViewFlushedEvent diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 74428ae2..bd72baa1 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -42,6 +42,24 @@ export class LineDecoration { return true; } + public static extractWrapped(arr: LineDecoration[], startOffset: number, endOffset: number): LineDecoration[] { + if (arr.length === 0) { + return arr; + } + const startColumn = startOffset + 1; + const endColumn = endOffset + 1; + const lineLength = endOffset - startOffset; + const r = []; + let rLength = 0; + for (const dec of arr) { + if (dec.endColumn <= startColumn || dec.startColumn >= endColumn) { + continue; + } + r[rLength++] = new LineDecoration(Math.max(1, dec.startColumn - startColumn + 1), Math.min(lineLength + 1, dec.endColumn - startColumn + 1), dec.className, dec.type); + } + return r; + } + public static filter(lineDecorations: InlineDecoration[], lineNumber: number, minLineColumn: number, maxLineColumn: number): LineDecoration[] { if (lineDecorations.length === 0) { return []; diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index ceef064d..a564e9a8 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -546,6 +546,23 @@ export class LinesLayout { return verticalOffset > totalHeight; } + public isInTopPadding(verticalOffset: number): boolean { + if (this._paddingTop === 0) { + return false; + } + this._checkPendingChanges(); + return (verticalOffset < this._paddingTop); + } + + public isInBottomPadding(verticalOffset: number): boolean { + if (this._paddingBottom === 0) { + return false; + } + this._checkPendingChanges(); + const totalHeight = this.getLinesTotalHeight(); + return (verticalOffset >= totalHeight - this._paddingBottom); + } + /** * Find the first line number that is at or after vertical offset `verticalOffset`. * i.e. if getVerticalOffsetForLine(line) is x and getVerticalOffsetForLine(line + 1) is y, then diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 0964eac9..24059d23 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -364,6 +364,13 @@ export class ViewLayout extends Disposable implements IViewLayout { public isAfterLines(verticalOffset: number): boolean { return this._linesLayout.isAfterLines(verticalOffset); } + public isInTopPadding(verticalOffset: number): boolean { + return this._linesLayout.isInTopPadding(verticalOffset); + } + isInBottomPadding(verticalOffset: number): boolean { + return this._linesLayout.isInBottomPadding(verticalOffset); + } + public getLineNumberAtVerticalOffset(verticalOffset: number): number { return this._linesLayout.getLineNumberAtOrAfterVerticalOffset(verticalOffset); } diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 1b75f0f7..49b43c10 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -127,7 +127,7 @@ export class RenderLineInput { this.containsRTL = containsRTL; this.fauxIndentLength = fauxIndentLength; this.lineTokens = lineTokens; - this.lineDecorations = lineDecorations; + this.lineDecorations = lineDecorations.sort(LineDecoration.compare); this.tabSize = tabSize; this.startVisibleColumn = startVisibleColumn; this.spaceWidth = spaceWidth; diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 549496d0..67a3b90f 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -7,8 +7,9 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { ILineBreaksComputer, LineBreakData } from 'vs/editor/common/viewModel/viewModel'; const enum CharacterClass { NONE = 0, diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 3a3eab70..50765f8e 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -12,69 +12,11 @@ import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDe import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, LineBreakData, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { IDisposable } from 'vs/base/common/lifecycle'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; -export class OutputPosition { - outputLineIndex: number; - outputOffset: number; - - constructor(outputLineIndex: number, outputOffset: number) { - this.outputLineIndex = outputLineIndex; - this.outputOffset = outputOffset; - } -} - -export class LineBreakData { - constructor( - public breakOffsets: number[], - public breakOffsetsVisibleColumn: number[], - public wrappedTextIndentLength: number - ) { } - - public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { - if (outputLineIndex === 0) { - return outputOffset; - } else { - return breakOffsets[outputLineIndex - 1] + outputOffset; - } - } - - public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition { - let low = 0; - let high = breakOffsets.length - 1; - let mid = 0; - let midStart = 0; - - while (low <= high) { - mid = low + ((high - low) / 2) | 0; - - const midStop = breakOffsets[mid]; - midStart = mid > 0 ? breakOffsets[mid - 1] : 0; - - if (inputOffset < midStart) { - high = mid - 1; - } else if (inputOffset >= midStop) { - low = mid + 1; - } else { - break; - } - } - - return new OutputPosition(mid, inputOffset - midStart); - } -} - -export interface ILineBreaksComputer { - /** - * Pass in `previousLineBreakData` if the only difference is in breaking columns!!! - */ - addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void; - finalize(): (LineBreakData | null)[]; -} - export interface ILineBreaksComputerFactory { createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer; } @@ -174,6 +116,10 @@ export class CoordinatesConverter implements ICoordinatesConverter { public modelPositionIsVisible(modelPosition: Position): boolean { return this._lines.modelPositionIsVisible(modelPosition.lineNumber, modelPosition.column); } + + public getModelLineViewLineCount(modelLineNumber: number): number { + return this._lines.getModelLineViewLineCount(modelLineNumber); + } } const enum IndentGuideRepeatOption { @@ -473,6 +419,14 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return this.lines[modelLineNumber - 1].isVisible(); } + public getModelLineViewLineCount(modelLineNumber: number): number { + if (modelLineNumber < 1 || modelLineNumber > this.lines.length) { + // invalid arguments + return 1; + } + return this.lines[modelLineNumber - 1].getViewLineCount(); + } + public setTabSize(newTabSize: number): boolean { if (this.tabSize === newTabSize) { return false; @@ -1431,6 +1385,9 @@ export class IdentityCoordinatesConverter implements ICoordinatesConverter { return true; } + public getModelLineViewLineCount(modelLineNumber: number): number { + return 1; + } } export class IdentityLinesCollection implements IViewModelLinesCollection { diff --git a/src/vs/editor/common/viewModel/viewEventHandler.ts b/src/vs/editor/common/viewModel/viewEventHandler.ts index aad7a0b4..e73ba967 100644 --- a/src/vs/editor/common/viewModel/viewEventHandler.ts +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -33,10 +33,15 @@ export class ViewEventHandler extends Disposable { // --- begin event handlers + public onCompositionStart(e: viewEvents.ViewCompositionStartEvent): boolean { + return false; + } + public onCompositionEnd(e: viewEvents.ViewCompositionEndEvent): boolean { + return false; + } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { return false; } - public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { return false; } @@ -94,6 +99,18 @@ export class ViewEventHandler extends Disposable { switch (e.type) { + case viewEvents.ViewEventType.ViewCompositionStart: + if (this.onCompositionStart(e)) { + shouldRender = true; + } + break; + + case viewEvents.ViewEventType.ViewCompositionEnd: + if (this.onCompositionEnd(e)) { + shouldRender = true; + } + break; + case viewEvents.ViewEventType.ViewConfigurationChanged: if (this.onConfigurationChanged(e)) { shouldRender = true; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 91ab8513..125cb588 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -61,6 +61,8 @@ export interface IViewLayout { getWhitespaces(): IEditorWhitespace[]; isAfterLines(verticalOffset: number): boolean; + isInTopPadding(verticalOffset: number): boolean; + isInBottomPadding(verticalOffset: number): boolean; getLineNumberAtVerticalOffset(verticalOffset: number): number; getVerticalOffsetForLineNumber(lineNumber: number): number; getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null; @@ -82,6 +84,65 @@ export interface ICoordinatesConverter { convertModelPositionToViewPosition(modelPosition: Position): Position; convertModelRangeToViewRange(modelRange: Range): Range; modelPositionIsVisible(modelPosition: Position): boolean; + getModelLineViewLineCount(modelLineNumber: number): number; +} + +export class OutputPosition { + outputLineIndex: number; + outputOffset: number; + + constructor(outputLineIndex: number, outputOffset: number) { + this.outputLineIndex = outputLineIndex; + this.outputOffset = outputOffset; + } +} + +export class LineBreakData { + constructor( + public breakOffsets: number[], + public breakOffsetsVisibleColumn: number[], + public wrappedTextIndentLength: number + ) { } + + public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { + if (outputLineIndex === 0) { + return outputOffset; + } else { + return breakOffsets[outputLineIndex - 1] + outputOffset; + } + } + + public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition { + let low = 0; + let high = breakOffsets.length - 1; + let mid = 0; + let midStart = 0; + + while (low <= high) { + mid = low + ((high - low) / 2) | 0; + + const midStop = breakOffsets[mid]; + midStart = mid > 0 ? breakOffsets[mid - 1] : 0; + + if (inputOffset < midStart) { + high = mid - 1; + } else if (inputOffset >= midStop) { + low = mid + 1; + } else { + break; + } + } + + return new OutputPosition(mid, inputOffset - midStart); + } +} + +export interface ILineBreaksComputer { + /** + * Pass in `previousLineBreakData` if the only difference is in breaking columns!!! + */ + addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void; + finalize(): (LineBreakData | null)[]; } export interface IViewModel extends ICursorSimpleModel { @@ -103,6 +164,8 @@ export interface IViewModel extends ICursorSimpleModel { setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void; tokenizeViewport(): void; setHasFocus(hasFocus: boolean): void; + onCompositionStart(): void; + onCompositionEnd(): void; onDidColorThemeChange(): void; getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; @@ -142,6 +205,7 @@ export interface IViewModel extends ICursorSimpleModel { //#endregion + createLineBreaksComputer(): ILineBreaksComputer; //#region cursor getPrimaryCursorState(): CursorState; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index f7f130f0..932f6917 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -21,7 +21,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as platform from 'vs/base/common/platform'; @@ -153,6 +153,10 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.dispose(); } + public createLineBreaksComputer(): ILineBreaksComputer { + return this._lines.createLineBreaksComputer(); + } + public addViewEventHandler(eventHandler: ViewEventHandler): void { this._eventDispatcher.addViewEventHandler(eventHandler); } @@ -179,6 +183,14 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus)); } + public onCompositionStart(): void { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionStartEvent()); + } + + public onCompositionEnd(): void { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionEndEvent()); + } + public onDidColorThemeChange(): void { this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent()); } diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index a488d79c..25b7687b 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -9,7 +9,6 @@ import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } fr import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; @@ -17,6 +16,7 @@ import * as modes from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types'; import { IProgress, Progress } from 'vs/platform/progress/common/progress'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export const codeActionCommandId = 'editor.action.codeAction'; export const refactorCommandId = 'editor.action.refactor'; @@ -223,8 +223,8 @@ function getDocumentation( return undefined; } -registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { - const { resource, rangeOrSelection, kind, itemResolveCount } = args; +CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (accessor, ...args): Promise> { + const [resource, rangeOrSelection, kind, itemResolveCount] = args; if (!(resource instanceof URI)) { throw illegalArgument(); } diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts index 186eacd2..e4a05011 100644 --- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -35,10 +35,14 @@ class CodeActionAction extends Action { public readonly action: CodeAction, callback: () => Promise, ) { - super(action.command ? action.command.id : action.title, action.title, undefined, !action.disabled, callback); + super(action.command ? action.command.id : action.title, stripNewlines(action.title), undefined, !action.disabled, callback); } } +function stripNewlines(str: string): string { + return str.replace(/\r\n|\r|\n/g, ' '); +} + export interface CodeActionShowOptions { readonly includeDisabledActions: boolean; } @@ -224,3 +228,5 @@ export class CodeActionKeybindingResolver { }, undefined as ResolveCodeActionKeybinding | undefined); } } + + diff --git a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts index a0092488..b8785616 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts @@ -90,6 +90,7 @@ function createCodeActionKeybinding(keycode: KeyCode, command: string, commandAr commandArgs, undefined, false, - null); + null, + false); } diff --git a/src/vs/editor/contrib/codelens/codeLensCache.ts b/src/vs/editor/contrib/codelens/codeLensCache.ts index 5f8c1476..c932cf49 100644 --- a/src/vs/editor/contrib/codelens/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/codeLensCache.ts @@ -9,7 +9,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { CodeLensModel } from 'vs/editor/contrib/codelens/codelens'; import { LRUCache } from 'vs/base/common/map'; import { CodeLensProvider, CodeLensList, CodeLens } from 'vs/editor/common/modes'; -import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { Range } from 'vs/editor/common/core/range'; import { runWhenIdle } from 'vs/base/common/async'; import { once } from 'vs/base/common/functional'; @@ -62,7 +62,7 @@ export class CodeLensCache implements ICodeLensCache { // store lens data on shutdown once(storageService.onWillSaveState)(e => { if (e.reason === WillSaveStateReason.SHUTDOWN) { - storageService.store(key, this._serialize(), StorageScope.WORKSPACE); + storageService.store(key, this._serialize(), StorageScope.WORKSPACE, StorageTarget.MACHINE); } }); } diff --git a/src/vs/editor/contrib/codelens/codelens.ts b/src/vs/editor/contrib/codelens/codelens.ts index fbcfb199..f8a4f09b 100644 --- a/src/vs/editor/contrib/codelens/codelens.ts +++ b/src/vs/editor/contrib/codelens/codelens.ts @@ -7,11 +7,12 @@ import { mergeSort } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { ITextModel } from 'vs/editor/common/model'; import { CodeLensProvider, CodeLensProviderRegistry, CodeLens, CodeLensList } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { assertType } from 'vs/base/common/types'; export interface CodeLensItem { symbol: CodeLens; @@ -79,14 +80,12 @@ export async function getCodeLensModel(model: ITextModel, token: CancellationTok return result; } -registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { +CommandsRegistry.registerCommand('_executeCodeLensProvider', function (accessor, ...args: [URI, number | undefined | null]) { + let [uri, itemResolveCount] = args; + assertType(URI.isUri(uri)); + assertType(typeof itemResolveCount === 'number' || !itemResolveCount); - let { resource, itemResolveCount } = args; - if (!(resource instanceof URI)) { - throw illegalArgument(); - } - - const model = accessor.get(IModelService).getModel(resource); + const model = accessor.get(IModelService).getModel(uri); if (!model) { throw illegalArgument(); } @@ -99,7 +98,7 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { let resolve: Promise[] = []; for (const item of value.lenses) { - if (typeof itemResolveCount === 'undefined' || Boolean(item.symbol.command)) { + if (itemResolveCount === undefined || itemResolveCount === null || Boolean(item.symbol.command)) { result.push(item.symbol); } else if (itemResolveCount-- > 0 && item.provider.resolveCodeLens) { resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol || item.symbol))); diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 360889a8..3fddfdff 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -53,7 +53,7 @@ export class CodeLensContribution implements IEditorContribution { this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); this._disposables.add(this._editor.onDidChangeConfiguration((e) => { - if (e.hasChanged(EditorOption.fontInfo)) { + if (e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.codeLensFontSize) || e.hasChanged(EditorOption.codeLensFontFamily)) { this._updateLensStyle(); } if (e.hasChanged(EditorOption.codeLens)) { @@ -77,21 +77,42 @@ export class CodeLensContribution implements IEditorContribution { this._disposables.dispose(); this._oldCodeLensModels.dispose(); this._currentCodeLensModel?.dispose(); + this._styleElement.remove(); + } + + private _getLayoutInfo() { + let fontSize = this._editor.getOption(EditorOption.codeLensFontSize); + let codeLensHeight: number; + if (!fontSize || fontSize < 5) { + fontSize = (this._editor.getOption(EditorOption.fontSize) * .9) | 0; + codeLensHeight = this._editor.getOption(EditorOption.lineHeight); + } else { + codeLensHeight = (fontSize * Math.max(1.3, this._editor.getOption(EditorOption.lineHeight) / this._editor.getOption(EditorOption.fontSize))) | 0; + } + return { codeLensHeight, fontSize }; } private _updateLensStyle(): void { - const options = this._editor.getOptions(); - const fontInfo = options.get(EditorOption.fontInfo); - const lineHeight = options.get(EditorOption.lineHeight); + const { codeLensHeight, fontSize } = this._getLayoutInfo(); + const fontFamily = this._editor.getOption(EditorOption.codeLensFontFamily); + const editorFontInfo = this._editor.getOption(EditorOption.fontInfo); - const height = Math.round(lineHeight * 1.1); - const fontSize = Math.round(fontInfo.fontSize * 0.9); - const newStyle = ` - .monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;} - .monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; } + let newStyle = ` + .monaco-editor .codelens-decoration.${this._styleClassName} { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontSize * 0.5)}px; font-feature-settings: ${editorFontInfo.fontFeatureSettings} } + .monaco-editor .codelens-decoration.${this._styleClassName} span.codicon { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; } `; + if (fontFamily) { + newStyle += `.monaco-editor .codelens-decoration.${this._styleClassName} { font-family: '${fontFamily}'}`; + } this._styleElement.textContent = newStyle; + + // + this._editor.changeViewZones(accessor => { + for (let lens of this._lenses) { + lens.updateHeight(codeLensHeight, accessor); + } + }); } private _localDispose(): void { @@ -165,7 +186,7 @@ export class CodeLensContribution implements IEditorContribution { // render lenses this._renderCodeLensSymbols(result); - this._resolveCodeLensesInViewportSoon(); + this._resolveCodeLensesInViewport(); }, onUnexpectedError); }, this._getCodeLensModelDelays.get(model)); @@ -199,11 +220,12 @@ export class CodeLensContribution implements IEditorContribution { }); }); - // Compute new `visible` code lenses - this._resolveCodeLensesInViewportSoon(); // Ask for all references again scheduler.schedule(); })); + this._localToDispose.add(this._editor.onDidFocusEditorWidget(() => { + scheduler.schedule(); + })); this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { this._resolveCodeLensesInViewportSoon(); @@ -283,6 +305,7 @@ export class CodeLensContribution implements IEditorContribution { } const scrollState = StableEditorScrollState.capture(this._editor); + const layoutInfo = this._getLayoutInfo(); this._editor.changeDecorations(decorationsAccessor => { this._editor.changeViewZones(viewZoneAccessor => { @@ -304,7 +327,7 @@ export class CodeLensContribution implements IEditorContribution { groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon())); codeLensIndex++; groupsIndex++; } @@ -318,7 +341,7 @@ export class CodeLensContribution implements IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon())); groupsIndex++; } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 215e48f2..f77b2dfc 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -18,20 +18,20 @@ import { renderCodicons } from 'vs/base/browser/codicons'; class CodeLensViewZone implements IViewZone { - readonly heightInLines: number; readonly suppressMouseDown: boolean; readonly domNode: HTMLElement; afterLineNumber: number; + heightInPx: number; private _lastHeight?: number; - private readonly _onHeight: Function; + private readonly _onHeight: () => void; - constructor(afterLineNumber: number, onHeight: Function) { + constructor(afterLineNumber: number, heightInPx: number, onHeight: () => void) { this.afterLineNumber = afterLineNumber; - this._onHeight = onHeight; + this.heightInPx = heightInPx; - this.heightInLines = 1; + this._onHeight = onHeight; this.suppressMouseDown = true; this.domNode = document.createElement('div'); } @@ -179,8 +179,8 @@ export class CodeLensWidget { private readonly _editor: IActiveCodeEditor; private readonly _className: string; - private readonly _viewZone!: CodeLensViewZone; - private readonly _viewZoneId!: string; + private readonly _viewZone: CodeLensViewZone; + private readonly _viewZoneId: string; private _contentWidget?: CodeLensContentWidget; private _decorationIds: string[]; @@ -193,7 +193,8 @@ export class CodeLensWidget { className: string, helper: CodeLensHelper, viewZoneChangeAccessor: IViewZoneChangeAccessor, - updateCallback: Function + heightInPx: number, + updateCallback: () => void ) { this._editor = editor; this._className = className; @@ -224,7 +225,7 @@ export class CodeLensWidget { } }); - this._viewZone = new CodeLensViewZone(range!.startLineNumber - 1, updateCallback); + this._viewZone = new CodeLensViewZone(range!.startLineNumber - 1, heightInPx, updateCallback); this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); if (lenses.length > 0) { @@ -236,7 +237,9 @@ export class CodeLensWidget { private _createContentWidgetIfNecessary(): void { if (!this._contentWidget) { this._contentWidget = new CodeLensContentWidget(this._editor, this._className, this._viewZone.afterLineNumber + 1); - this._editor.addContentWidget(this._contentWidget!); + this._editor.addContentWidget(this._contentWidget); + } else { + this._editor.layoutContentWidget(this._contentWidget); } } @@ -277,6 +280,14 @@ export class CodeLensWidget { }); } + updateHeight(height: number, viewZoneChangeAccessor: IViewZoneChangeAccessor): void { + this._viewZone.heightInPx = height; + viewZoneChangeAccessor.layoutZone(this._viewZoneId); + if (this._contentWidget) { + this._editor.layoutContentWidget(this._contentWidget); + } + } + computeIfNecessary(model: ITextModel): CodeLensItem[] | null { if (!this._viewZone.domNode.hasAttribute('monaco-visible-view-zone')) { return null; diff --git a/src/vs/editor/contrib/colorPicker/color.ts b/src/vs/editor/contrib/colorPicker/color.ts index d6b49012..7a4383b1 100644 --- a/src/vs/editor/contrib/colorPicker/color.ts +++ b/src/vs/editor/contrib/colorPicker/color.ts @@ -6,11 +6,11 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { ColorProviderRegistry, DocumentColorProvider, IColorInformation, IColorPresentation } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export interface IColorData { @@ -36,9 +36,9 @@ export function getColorPresentations(model: ITextModel, colorInfo: IColorInform return Promise.resolve(provider.provideColorPresentations(model, colorInfo, token)); } -registerLanguageCommand('_executeDocumentColorProvider', function (accessor, args) { +CommandsRegistry.registerCommand('_executeDocumentColorProvider', function (accessor, ...args) { - const { resource } = args; + const [resource] = args; if (!(resource instanceof URI)) { throw illegalArgument(); } @@ -62,15 +62,16 @@ registerLanguageCommand('_executeDocumentColorProvider', function (accessor, arg }); -registerLanguageCommand('_executeColorPresentationProvider', function (accessor, args) { +CommandsRegistry.registerCommand('_executeColorPresentationProvider', function (accessor, ...args) { - const { resource, color, range } = args; - if (!(resource instanceof URI) || !Array.isArray(color) || color.length !== 4 || !Range.isIRange(range)) { + const [color, context] = args; + const { uri, range } = context; + if (!(uri instanceof URI) || !Array.isArray(color) || color.length !== 4 || !Range.isIRange(range)) { throw illegalArgument(); } const [red, green, blue, alpha] = color; - const model = accessor.get(IModelService).getModel(resource); + const model = accessor.get(IModelService).getModel(uri); if (!model) { throw illegalArgument(); } diff --git a/src/vs/editor/contrib/colorPicker/colorContributions.ts b/src/vs/editor/contrib/colorPicker/colorContributions.ts new file mode 100644 index 00000000..4a997061 --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/colorContributions.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// import color detector contribution +import 'vs/editor/contrib/colorPicker/colorDetector'; + +import { Disposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ModesHoverController } from 'vs/editor/contrib/hover/hover'; +import { Range } from 'vs/editor/common/core/range'; +import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; + +export class ColorContribution extends Disposable implements IEditorContribution { + + public static readonly ID: string = 'editor.contrib.colorContribution'; + + static readonly RECOMPUTE_TIME = 1000; // ms + + constructor(private readonly _editor: ICodeEditor, + ) { + super(); + this._register(_editor.onMouseDown((e) => this.onMouseDown(e))); + } + + dispose(): void { + super.dispose(); + } + + private onMouseDown(mouseEvent: IEditorMouseEvent) { + const targetType = mouseEvent.target.type; + + if (targetType !== MouseTargetType.CONTENT_TEXT) { + return; + } + + const hoverOnColorDecorator = [...mouseEvent.target.element?.classList.values() || []].find(className => className.startsWith('ced-colorBox')); + if (!hoverOnColorDecorator) { + return; + } + + if (!mouseEvent.target.range) { + return; + } + + const hoverController = this._editor.getContribution(ModesHoverController.ID); + if (!hoverController.contentWidget.isColorPickerVisible()) { + const range = new Range(mouseEvent.target.range.startLineNumber, mouseEvent.target.range.startColumn + 1, mouseEvent.target.range.endLineNumber, mouseEvent.target.range.endColumn + 1); + hoverController.showContentHover(range, HoverStartMode.Delayed, false); + } + } +} + +registerEditorContribution(ColorContribution.ID, ColorContribution); diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 4b678d0e..ccd09304 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -46,13 +46,13 @@ export class ColorDetector extends Disposable implements IEditorContribution { @IConfigurationService private readonly _configurationService: IConfigurationService ) { super(); - this._register(_editor.onDidChangeModel((e) => { + this._register(_editor.onDidChangeModel(() => { this._isEnabled = this.isEnabled(); this.onModelChanged(); })); - this._register(_editor.onDidChangeModelLanguage((e) => this.onModelChanged())); - this._register(ColorProviderRegistry.onDidChange((e) => this.onModelChanged())); - this._register(_editor.onDidChangeConfiguration((e) => { + this._register(_editor.onDidChangeModelLanguage(() => this.onModelChanged())); + this._register(ColorProviderRegistry.onDidChange(() => this.onModelChanged())); + this._register(_editor.onDidChangeConfiguration(() => { let prevIsEnabled = this._isEnabled; this._isEnabled = this.isEnabled(); if (prevIsEnabled !== this._isEnabled) { @@ -110,7 +110,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { return; } - this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { + this._localToDispose.add(this._editor.onDidChangeModelContent(() => { if (!this._timeoutTimer) { this._timeoutTimer = new TimeoutTimer(); this._timeoutTimer.cancelAndSet(() => { diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 4bcdaa39..454b3d33 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -335,7 +335,7 @@ export class ColorPickerWidget extends Widget { body: ColorPickerBody; - constructor(container: Node, private readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { + constructor(container: Node, readonly model: ColorPickerModel, private pixelRatio: number, themeService: IThemeService) { super(); this._register(onDidChangeZoomLevel(() => this.layout())); diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 1dbe337e..d09c10af 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -7,6 +7,7 @@ import * as nls from 'vs/nls'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { Range } from 'vs/editor/common/core/range'; import { ICommand } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; @@ -31,17 +32,38 @@ abstract class CommentLineAction extends EditorAction { const model = editor.getModel(); const commands: ICommand[] = []; - const selections = editor.getSelections(); const modelOptions = model.getOptions(); const commentsOptions = editor.getOption(EditorOption.comments); + const selections = editor.getSelections().map((selection, index) => ({ selection, index, ignoreFirstLine: false })); + selections.sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection)); + + // Remove selections that would result in copying the same line + let prev = selections[0]; + for (let i = 1; i < selections.length; i++) { + const curr = selections[i]; + if (prev.selection.endLineNumber === curr.selection.startLineNumber) { + // these two selections would copy the same line + if (prev.index < curr.index) { + // prev wins + curr.ignoreFirstLine = true; + } else { + // curr wins + prev.ignoreFirstLine = true; + prev = curr; + } + } + } + + for (const selection of selections) { commands.push(new LineCommentCommand( - selection, + selection.selection, modelOptions.tabSize, this._type, commentsOptions.insertSpace, - commentsOptions.ignoreEmptyLines + commentsOptions.ignoreEmptyLines, + selection.ignoreFirstLine )); } diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 8952727a..0b81435a 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -57,13 +57,15 @@ export class LineCommentCommand implements ICommand { private _selectionId: string | null; private _deltaColumn: number; private _moveEndPositionDown: boolean; + private _ignoreFirstLine: boolean; constructor( selection: Selection, tabSize: number, type: Type, insertSpace: boolean, - ignoreEmptyLines: boolean + ignoreEmptyLines: boolean, + ignoreFirstLine?: boolean ) { this._selection = selection; this._tabSize = tabSize; @@ -73,6 +75,7 @@ export class LineCommentCommand implements ICommand { this._deltaColumn = 0; this._moveEndPositionDown = false; this._ignoreEmptyLines = ignoreEmptyLines; + this._ignoreFirstLine = ignoreFirstLine || false; } /** @@ -108,7 +111,7 @@ export class LineCommentCommand implements ICommand { * Analyze lines and decide which lines are relevant and what the toggle should do. * Also, build up several offsets and lengths useful in the generation of editor operations. */ - public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { + public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean, ignoreFirstLine: boolean): IPreflightData { let onlyWhitespaceLines = true; let shouldRemoveComments: boolean; @@ -124,6 +127,12 @@ export class LineCommentCommand implements ICommand { const lineData = lines[i]; const lineNumber = startLineNumber + i; + if (lineNumber === startLineNumber && ignoreFirstLine) { + // first line ignored + lineData.ignore = true; + continue; + } + const lineContent = model.getLineContent(lineNumber); const lineContentStartOffset = strings.firstNonWhitespaceIndex(lineContent); @@ -178,7 +187,7 @@ export class LineCommentCommand implements ICommand { /** * Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments */ - public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number, ignoreEmptyLines: boolean): IPreflightData { + public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number, ignoreEmptyLines: boolean, ignoreFirstLine: boolean): IPreflightData { const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber); if (lines === null) { return { @@ -186,7 +195,7 @@ export class LineCommentCommand implements ICommand { }; } - return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines); + return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines, ignoreFirstLine); } /** @@ -323,6 +332,12 @@ export class LineCommentCommand implements ICommand { let s = this._selection; this._moveEndPositionDown = false; + if (s.startLineNumber === s.endLineNumber && this._ignoreFirstLine) { + builder.addEditOperation(new Range(s.startLineNumber, model.getLineMaxColumn(s.startLineNumber), s.startLineNumber + 1, 1), s.startLineNumber === model.getLineCount() ? '' : '\n'); + this._selectionId = builder.trackSelection(s); + return; + } + if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) { this._moveEndPositionDown = true; s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); @@ -334,7 +349,8 @@ export class LineCommentCommand implements ICommand { model, s.startLineNumber, s.endLineNumber, - this._ignoreEmptyLines + this._ignoreEmptyLines, + this._ignoreFirstLine ); if (data.supported) { diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index 0689b420..2a4329ba 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -91,7 +91,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' ', ' c', '\t\td' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false); if (!r.supported) { throw new Error(`unexpected`); } @@ -122,7 +122,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' rem ', ' !@# c', '\t\t!@#d' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false); if (!r.supported) { throw new Error(`unexpected`); } diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index b4bdd433..b1b4a06b 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -51,6 +51,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); this._register(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); this._register(this._editor.onMouseDrop((e: IPartialEditorMouseEvent) => this._onEditorMouseDrop(e))); + this._register(this._editor.onMouseDropCanceled(() => this._onEditorMouseDropCanceled())); this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); @@ -144,6 +145,16 @@ export class DragAndDropController extends Disposable implements IEditorContribu } } + private _onEditorMouseDropCanceled() { + this._editor.updateOptions({ + mouseStyle: 'text' + }); + + this._removeDecoration(); + this._dragSelection = null; + this._mouseDown = false; + } + private _onEditorMouseDrop(mouseEvent: IPartialEditorMouseEvent): void { if (mouseEvent.target && (this._hitContent(mouseEvent.target) || this._hitMargin(mouseEvent.target)) && mouseEvent.target.position) { let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column); diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index 4f7b574c..9982068e 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -22,22 +22,23 @@ import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/con import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; const SEARCH_STRING_MAX_LENGTH = 524288; -export function getSelectionSearchString(editor: ICodeEditor): string | null { +export function getSelectionSearchString(editor: ICodeEditor, seedSearchStringFromSelection: 'single' | 'multiple' = 'single'): string | null { if (!editor.hasModel()) { return null; } const selection = editor.getSelection(); // if selection spans multiple lines, default search string to empty - if (selection.startLineNumber === selection.endLineNumber) { + + if ((seedSearchStringFromSelection === 'single' && selection.startLineNumber === selection.endLineNumber) + || seedSearchStringFromSelection === 'multiple') { if (selection.isEmpty()) { const wordAtPosition = editor.getConfiguredWordAtPosition(selection.getStartPosition()); if (wordAtPosition) { @@ -61,7 +62,7 @@ export const enum FindStartFocusAction { export interface IFindStartOptions { forceRevealReplace: boolean; - seedSearchStringFromSelection: boolean; + seedSearchStringFromSelection: 'none' | 'single' | 'multiple'; seedSearchStringFromGlobalClipboard: boolean; shouldFocus: FindStartFocusAction; shouldAnimate: boolean; @@ -82,6 +83,10 @@ export class CommonFindController extends Disposable implements IEditorContribut private readonly _clipboardService: IClipboardService; protected readonly _contextKeyService: IContextKeyService; + get editor() { + return this._editor; + } + public static get(editor: ICodeEditor): CommonFindController { return editor.getContribution(CommonFindController.ID); } @@ -122,7 +127,7 @@ export class CommonFindController extends Disposable implements IEditorContribut if (shouldRestartFind) { this._start({ forceRevealReplace: false, - seedSearchStringFromSelection: false && this._editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -163,16 +168,16 @@ export class CommonFindController extends Disposable implements IEditorContribut private saveQueryState(e: FindReplaceStateChangedEvent) { if (e.isRegex) { - this._storageService.store('editor.isRegex', this._state.actualIsRegex, StorageScope.WORKSPACE); + this._storageService.store('editor.isRegex', this._state.actualIsRegex, StorageScope.WORKSPACE, StorageTarget.USER); } if (e.wholeWord) { - this._storageService.store('editor.wholeWord', this._state.actualWholeWord, StorageScope.WORKSPACE); + this._storageService.store('editor.wholeWord', this._state.actualWholeWord, StorageScope.WORKSPACE, StorageTarget.USER); } if (e.matchCase) { - this._storageService.store('editor.matchCase', this._state.actualMatchCase, StorageScope.WORKSPACE); + this._storageService.store('editor.matchCase', this._state.actualMatchCase, StorageScope.WORKSPACE, StorageTarget.USER); } if (e.preserveCase) { - this._storageService.store('editor.preserveCase', this._state.actualPreserveCase, StorageScope.WORKSPACE); + this._storageService.store('editor.preserveCase', this._state.actualPreserveCase, StorageScope.WORKSPACE, StorageTarget.USER); } } @@ -262,7 +267,7 @@ export class CommonFindController extends Disposable implements IEditorContribut this._state.change({ searchString: searchString }, false); } - public highlightFindOptions(): void { + public highlightFindOptions(ignoreWhenVisible: boolean = false): void { // overwritten in subclass } @@ -278,8 +283,8 @@ export class CommonFindController extends Disposable implements IEditorContribut isRevealed: true }; - if (opts.seedSearchStringFromSelection) { - let selectionSearchString = getSelectionSearchString(this._editor); + if (opts.seedSearchStringFromSelection === 'single') { + let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection); if (selectionSearchString) { if (this._state.isRegex) { stateChanges.searchString = strings.escapeRegExpCharacters(selectionSearchString); @@ -287,6 +292,11 @@ export class CommonFindController extends Disposable implements IEditorContribut stateChanges.searchString = selectionSearchString; } } + } else if (opts.seedSearchStringFromSelection === 'multiple' && !opts.updateSearchScope) { + let selectionSearchString = getSelectionSearchString(this._editor, opts.seedSearchStringFromSelection); + if (selectionSearchString) { + stateChanges.searchString = selectionSearchString; + } } if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) { @@ -404,7 +414,6 @@ export class FindController extends CommonFindController implements IFindControl @IThemeService private readonly _themeService: IThemeService, @INotificationService private readonly _notificationService: INotificationService, @IStorageService _storageService: IStorageService, - @IStorageKeysSyncRegistryService private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IClipboardService clipboardService: IClipboardService, ) { super(editor, _contextKeyService, _storageService, clipboardService); @@ -449,11 +458,11 @@ export class FindController extends CommonFindController implements IFindControl } } - public highlightFindOptions(): void { + public highlightFindOptions(ignoreWhenVisible: boolean = false): void { if (!this._widget) { this._createFindWidget(); } - if (this._state.isRevealed) { + if (this._state.isRevealed && !ignoreWhenVisible) { this._widget!.highlightFindOptions(); } else { this._findOptionsWidget!.highlightFindOptions(); @@ -461,9 +470,17 @@ export class FindController extends CommonFindController implements IFindControl } private _createFindWidget() { - this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService, this._storageKeysSyncRegistryService)); + this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService)); this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService)); } + + saveViewState(): any { + return this._widget?.getViewState(); + } + + restoreViewState(state: any): void { + this._widget?.setViewState(state); + } } export class StartFindAction extends MultiEditorAction { @@ -493,7 +510,7 @@ export class StartFindAction extends MultiEditorAction { if (controller) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard, shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: true, @@ -523,12 +540,12 @@ export class StartFindWithSelectionAction extends EditorAction { }); } - public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + public async run(accessor: ServicesAccessor | null, editor: ICodeEditor): Promise { let controller = CommonFindController.get(editor); if (controller) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: true, + seedSearchStringFromSelection: 'multiple', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, @@ -546,7 +563,7 @@ export abstract class MatchFindAction extends EditorAction { if (controller && !this._run(controller)) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: true, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, @@ -578,7 +595,13 @@ export class NextMatchFindAction extends MatchFindAction { } protected _run(controller: CommonFindController): boolean { - return controller.moveToNextMatch(); + const result = controller.moveToNextMatch(); + if (result) { + controller.editor.pushUndoStop(); + return true; + } + + return false; } } @@ -599,7 +622,13 @@ export class NextMatchFindAction2 extends MatchFindAction { } protected _run(controller: CommonFindController): boolean { - return controller.moveToNextMatch(); + const result = controller.moveToNextMatch(); + if (result) { + controller.editor.pushUndoStop(); + return true; + } + + return false; } } @@ -659,7 +688,7 @@ export abstract class SelectionMatchFindAction extends EditorAction { if (!this._run(controller)) { await controller.start({ forceRevealReplace: false, - seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection, + seedSearchStringFromSelection: editor.getOption(EditorOption.find).seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, @@ -765,7 +794,7 @@ export class StartFindReplaceAction extends MultiEditorAction { if (controller) { await controller.start({ forceRevealReplace: true, - seedSearchStringFromSelection: seedSearchStringFromSelection, + seedSearchStringFromSelection: seedSearchStringFromSelection ? 'single' : 'none', seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection, shouldFocus: shouldFocus, shouldAnimate: true, diff --git a/src/vs/editor/contrib/find/findOptionsWidget.ts b/src/vs/editor/contrib/find/findOptionsWidget.ts index d541e0d0..660eed85 100644 --- a/src/vs/editor/contrib/find/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/findOptionsWidget.ts @@ -213,7 +213,7 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-editor .findOptionsWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-editor .findOptionsWidget { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } const hcBorder = theme.getColor(contrastBorder); diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 21fbb006..597af6c3 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -31,23 +31,22 @@ import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contri import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon, widgetClose } from 'vs/platform/theme/common/iconRegistry'; -const findSelectionIcon = registerIcon('find-selection', Codicon.selection); -const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight); -const findExpandedIcon = registerIcon('find-expanded', Codicon.chevronDown); +const findSelectionIcon = registerIcon('find-selection', Codicon.selection, nls.localize('findSelectionIcon', 'Icon for \'Find in Selection\' in the editor find widget.')); +const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight, nls.localize('findCollapsedIcon', 'Icon to indicate that the editor find widget is collapsed.')); +const findExpandedIcon = registerIcon('find-expanded', Codicon.chevronDown, nls.localize('findExpandedIcon', 'Icon to indicate that the editor find widget is expanded.')); -export const findCloseIcon = registerIcon('find-close', Codicon.close); -export const findReplaceIcon = registerIcon('find-replace', Codicon.replace); -export const findReplaceAllIcon = registerIcon('find-replace-all', Codicon.replaceAll); -export const findPreviousMatchIcon = registerIcon('find-previous-match', Codicon.arrowUp); -export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDown); +export const findReplaceIcon = registerIcon('find-replace', Codicon.replace, nls.localize('findReplaceIcon', 'Icon for \'Replace\' in the editor find widget.')); +export const findReplaceAllIcon = registerIcon('find-replace-all', Codicon.replaceAll, nls.localize('findReplaceAllIcon', 'Icon for \'Replace All\' in the editor find widget.')); +export const findPreviousMatchIcon = registerIcon('find-previous-match', Codicon.arrowUp, nls.localize('findPreviousMatchIcon', 'Icon for \'Find Previous\' in the editor find widget.')); +export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDown, nls.localize('findNextMatchIcon', 'Icon for \'Find Next\' in the editor find widget.')); export interface IFindController { replace(): void; @@ -164,7 +163,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL themeService: IThemeService, storageService: IStorageService, notificationService: INotificationService, - storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); this._codeEditor = codeEditor; @@ -176,7 +174,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._storageService = storageService; this._notificationService = notificationService; - storageKeysSyncRegistryService.registerStorageKey({ key: ctrlEnterReplaceAllWarningPromptedKey, version: 1 }); this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL); this._isVisible = false; @@ -228,7 +225,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL if (this._isVisible) { let globalBufferTerm = await this._controller.getGlobalBufferTerm(); if (globalBufferTerm && globalBufferTerm !== this._state.searchString) { - this._state.change({ searchString: globalBufferTerm }, true); + this._state.change({ searchString: globalBufferTerm }, false); this._findInput.select(); } } @@ -383,7 +380,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } private _delayedUpdateHistory() { - this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)); + this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)).then(undefined, onUnexpectedError); } private _updateHistory() { @@ -487,7 +484,15 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._toggleReplaceBtn.setEnabled(this._isVisible && canReplace); } + private _revealTimeouts: any[] = []; + private _reveal(): void { + this._revealTimeouts.forEach(e => { + clearTimeout(e); + }); + + this._revealTimeouts = []; + if (!this._isVisible) { this._isVisible = true; @@ -512,15 +517,15 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._tryUpdateWidgetWidth(); this._updateButtons(); - setTimeout(() => { + this._revealTimeouts.push(setTimeout(() => { this._domNode.classList.add('visible'); this._domNode.setAttribute('aria-hidden', 'false'); - }, 0); + }, 0)); // validate query again as it's being dismissed when we hide the find widget. - setTimeout(() => { + this._revealTimeouts.push(setTimeout(() => { this._findInput.validate(); - }, 200); + }, 200)); this._codeEditor.layoutOverlayWidget(this); @@ -555,6 +560,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } private _hide(focusTheEditor: boolean): void { + this._revealTimeouts.forEach(e => { + clearTimeout(e); + }); + + this._revealTimeouts = []; + if (this._isVisible) { this._isVisible = false; @@ -571,7 +582,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } } - private _layoutViewZone() { + private _layoutViewZone(targetScrollTop?: number) { const addExtraSpaceOnTop = this._codeEditor.getOption(EditorOption.find).addExtraSpaceOnTop; if (!addExtraSpaceOnTop) { @@ -591,7 +602,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL viewZone.heightInPx = this._getHeight(); this._viewZoneId = accessor.addZone(viewZone); // scroll top adjust to make sure the editor doesn't scroll when adding viewzone at the beginning. - this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + viewZone.heightInPx); + this._codeEditor.setScrollTop(targetScrollTop || this._codeEditor.getScrollTop() + viewZone.heightInPx); }); } @@ -880,7 +891,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL ); this._ctrlEnterReplaceAllWarningPrompted = true; - this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL); + this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL, StorageTarget.USER); } @@ -1006,7 +1017,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Previous button this._prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction), - className: findPreviousMatchIcon.classNames, + icon: findPreviousMatchIcon, onTrigger: () => { this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError); } @@ -1015,7 +1026,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Next button this._nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction), - className: findNextMatchIcon.classNames, + icon: findNextMatchIcon, onTrigger: () => { this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError); } @@ -1033,7 +1044,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Toggle selection button this._toggleSelectionFind = this._register(new Checkbox({ - icon: findSelectionIcon, + icon: ThemeIcon.asCSSIcon(findSelectionIcon), title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand), isChecked: false })); @@ -1066,7 +1077,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Close button this._closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand), - className: findCloseIcon.classNames, + icon: widgetClose, onTrigger: () => { this._state.change({ isRevealed: false, searchScope: null }, false); }, @@ -1130,7 +1141,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Replace one button this._replaceBtn = this._register(new SimpleButton({ label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction), - className: findReplaceIcon.classNames, + icon: findReplaceIcon, onTrigger: () => { this._controller.replace(); }, @@ -1145,7 +1156,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL // Replace all button this._replaceAllBtn = this._register(new SimpleButton({ label: NLS_REPLACE_ALL_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceAllAction), - className: findReplaceAllIcon.classNames, + icon: findReplaceAllIcon, onTrigger: () => { this._controller.replaceAll(); } @@ -1255,11 +1266,35 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL const value = this._codeEditor.getOption(EditorOption.accessibilitySupport); this._findInput.setFocusInputOnOptionClick(value !== AccessibilitySupport.Enabled); } + + getViewState() { + let widgetViewZoneVisible = false; + if (this._viewZone && this._viewZoneId) { + widgetViewZoneVisible = this._viewZone.heightInPx > this._codeEditor.getScrollTop(); + } + + return { + widgetViewZoneVisible, + scrollTop: this._codeEditor.getScrollTop() + }; + } + + setViewState(state?: { widgetViewZoneVisible: boolean; scrollTop: number }) { + if (!state) { + return; + } + + if (state.widgetViewZoneVisible) { + // we should add the view zone + this._layoutViewZone(state.scrollTop); + } + } } export interface ISimpleButtonOpts { readonly label: string; - readonly className: string; + readonly className?: string; + readonly icon?: ThemeIcon; readonly onTrigger: () => void; readonly onKeyDown?: (e: IKeyboardEvent) => void; } @@ -1273,10 +1308,18 @@ export class SimpleButton extends Widget { super(); this._opts = opts; + let className = 'button'; + if (this._opts.className) { + className = className + ' ' + this._opts.className; + } + if (this._opts.icon) { + className = className + ' ' + ThemeIcon.asClassName(this._opts.icon); + } + this._domNode = document.createElement('div'); this._domNode.title = this._opts.label; this._domNode.tabIndex = 0; - this._domNode.className = 'button ' + this._opts.className; + this._domNode.className = className; this._domNode.setAttribute('role', 'button'); this._domNode.setAttribute('aria-label', this._opts.label); @@ -1318,11 +1361,11 @@ export class SimpleButton extends Widget { public setExpanded(expanded: boolean): void { this._domNode.setAttribute('aria-expanded', String(!!expanded)); if (expanded) { - this._domNode.classList.remove(...findCollapsedIcon.classNames.split(' ')); - this._domNode.classList.add(...findExpandedIcon.classNames.split(' ')); + this._domNode.classList.remove(...ThemeIcon.asClassNameArray(findCollapsedIcon)); + this._domNode.classList.add(...ThemeIcon.asClassNameArray(findExpandedIcon)); } else { - this._domNode.classList.remove(...findExpandedIcon.classNames.split(' ')); - this._domNode.classList.add(...findCollapsedIcon.classNames.split(' ')); + this._domNode.classList.remove(...ThemeIcon.asClassNameArray(findExpandedIcon)); + this._domNode.classList.add(...ThemeIcon.asClassNameArray(findCollapsedIcon)); } } } @@ -1345,7 +1388,7 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-editor .find-widget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-editor .find-widget { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } const findMatchHighlightBorder = theme.getColor(editorFindMatchHighlightBorder); diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index ee0c4862..8d9216ea 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -12,7 +12,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController'; +import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction, StartFindWithSelectionAction } from 'vs/editor/contrib/find/findController'; import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { withAsyncTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -61,14 +61,20 @@ suite('FindController', async () => { let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeStorage: Event.None, + onDidChangeTarget: Event.None, + onDidChangeValue: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], - getNumber: (key: string) => undefined, + getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - remove: () => undefined - } as any); + remove: () => undefined, + isNew: () => false, + flush: () => { return Promise.resolve(); }, + keys: () => [], + logStorage: () => { }, + migrate: () => { throw new Error(); } + } as IStorageService); if (platform.isMacintosh) { serviceCollection.set(IClipboardService, { @@ -272,7 +278,7 @@ suite('FindController', async () => { findController.setSearchString(testRegexString); await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: false, @@ -298,7 +304,7 @@ suite('FindController', async () => { let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -428,6 +434,59 @@ suite('FindController', async () => { findController.dispose(); }); }); + + test('issue #47400, CMD+E supports feeding multiple line of text into the find widget', async () => { + await withAsyncTestCodeEditor([ + 'ABC', + 'ABC', + 'XYZ', + 'ABC', + 'ABC' + ], { serviceCollection: serviceCollection }, async (editor) => { + clipboardState = ''; + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + let startFindAction = new StartFindAction(); + + // change selection + editor.setSelection(new Selection(1, 1, 1, 1)); + + // cmd+f - open find widget + await startFindAction.run(null, editor); + + editor.setSelection(new Selection(1, 1, 2, 4)); + let startFindWithSelectionAction = new StartFindWithSelectionAction(); + await startFindWithSelectionAction.run(null, editor); + let findState = findController.getState(); + + assert.deepEqual(findState.searchString.split(/\r\n|\r|\n/g), ['ABC', 'ABC']); + + editor.setSelection(new Selection(3, 1, 3, 1)); + await startFindWithSelectionAction.run(null, editor); + + findController.dispose(); + }); + }); + + test('issue #109756, CMD+E with empty cursor should always work', async () => { + await withAsyncTestCodeEditor([ + 'ABC', + 'ABC', + 'XYZ', + 'ABC', + 'ABC' + ], { serviceCollection: serviceCollection }, async (editor) => { + clipboardState = ''; + let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); + editor.setSelection(new Selection(1, 2, 1, 2)); + + let startFindWithSelectionAction = new StartFindWithSelectionAction(); + startFindWithSelectionAction.run(null, editor); + + let findState = findController.getState(); + assert.deepEqual(findState.searchString, 'ABC'); + findController.dispose(); + }); + }); }); suite('FindController query options persistence', async () => { @@ -438,14 +497,20 @@ suite('FindController query options persistence', async () => { let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeStorage: Event.None, + onDidChangeTarget: Event.None, + onDidChangeValue: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], - getNumber: (key: string) => undefined, + getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - remove: () => undefined - } as any); + remove: () => undefined, + isNew: () => false, + flush: () => { return Promise.resolve(); }, + keys: () => [], + logStorage: () => { }, + migrate: () => { throw new Error(); } + } as IStorageService); test('matchCase', async () => { await withAsyncTestCodeEditor([ @@ -524,9 +589,9 @@ suite('FindController query options persistence', async () => { ], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => { // clipboardState = ''; let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); - const findConfig = { + const findConfig: IFindStartOptions = { forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -558,7 +623,7 @@ suite('FindController query options persistence', async () => { await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -582,7 +647,7 @@ suite('FindController query options persistence', async () => { await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, @@ -607,7 +672,7 @@ suite('FindController query options persistence', async () => { await findController.start({ forceRevealReplace: false, - seedSearchStringFromSelection: false, + seedSearchStringFromSelection: 'none', seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 0bfa5f68..1d48e061 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -32,7 +32,7 @@ import { InitializingRangeProvider, ID_INIT_PROVIDER } from 'vs/editor/contrib/f import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor, editorSelectionBackground, transparent, iconForeground } from 'vs/platform/theme/common/colorRegistry'; const CONTEXT_FOLDING_ENABLED = new RawContextKey('foldingEnabled', false); @@ -916,8 +916,8 @@ registerThemingParticipant((theme, collector) => { const editorFoldColor = theme.getColor(editorFoldForeground); if (editorFoldColor) { collector.addRule(` - .monaco-editor .cldr${foldingExpandedIcon.cssSelector}, - .monaco-editor .cldr${foldingCollapsedIcon.cssSelector} { + .monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingExpandedIcon)}, + .monaco-editor .cldr${ThemeIcon.asCSSSelector(foldingCollapsedIcon)} { color: ${editorFoldColor} !important; } `); diff --git a/src/vs/editor/contrib/folding/foldingDecorations.ts b/src/vs/editor/contrib/folding/foldingDecorations.ts index c34e2c21..22601d12 100644 --- a/src/vs/editor/contrib/folding/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/foldingDecorations.ts @@ -7,18 +7,20 @@ import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationsChangeA import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IDecorationProvider } from 'vs/editor/contrib/folding/foldingModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; - -export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown); -export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight); +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown, localize('foldingExpandedIcon', 'Icon for expanded ranges in the editor glyph margin.')); +export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight, localize('foldingCollapsedIcon', 'Icon for collapsed ranges in the editor glyph margin.')); export class FoldingDecorationProvider implements IDecorationProvider { private static readonly COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, afterContentClassName: 'inline-folded', isWholeLine: true, - firstLineDecorationClassName: foldingCollapsedIcon.classNames + firstLineDecorationClassName: ThemeIcon.asClassName(foldingCollapsedIcon) }); private static readonly COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION = ModelDecorationOptions.register({ @@ -26,19 +28,19 @@ export class FoldingDecorationProvider implements IDecorationProvider { afterContentClassName: 'inline-folded', className: 'folded-background', isWholeLine: true, - firstLineDecorationClassName: foldingCollapsedIcon.classNames + firstLineDecorationClassName: ThemeIcon.asClassName(foldingCollapsedIcon) }); private static readonly EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, isWholeLine: true, - firstLineDecorationClassName: foldingExpandedIcon.classNames + firstLineDecorationClassName: ThemeIcon.asClassName(foldingExpandedIcon) }); private static readonly EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, isWholeLine: true, - firstLineDecorationClassName: 'alwaysShowFoldIcons ' + foldingExpandedIcon.classNames + firstLineDecorationClassName: 'alwaysShowFoldIcons ' + ThemeIcon.asClassName(foldingExpandedIcon) }); private static readonly HIDDEN_RANGE_DECORATION = ModelDecorationOptions.register({ diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index bedb20cd..cdfdac89 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -215,13 +215,12 @@ class FormatDocumentAction extends EditorAction { alias: 'Format Document', precondition: ContextKeyExpr.and(EditorContextKeys.notInCompositeEditor, EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib }, contextMenuOpts: { - when: EditorContextKeys.hasDocumentFormattingProvider, group: '1_modification', order: 1.3 } @@ -249,12 +248,12 @@ class FormatSelectionAction extends EditorAction { alias: 'Format Selection', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentSelectionFormattingProvider), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), weight: KeybindingWeight.EditorContrib }, contextMenuOpts: { - when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), + when: EditorContextKeys.hasNonEmptySelection, group: '1_modification', order: 1.31 } diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 001ed3f2..259773d9 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -20,9 +20,10 @@ import { MarkerNavigationWidget } from './gotoErrorWidget'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { MenuId } from 'vs/platform/actions/common/actions'; import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMarkerNavigationService, MarkerList } from 'vs/editor/contrib/gotoError/markerNavigationService'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export class MarkerController implements IEditorContribution { @@ -199,7 +200,7 @@ export class NextMarkerAction extends MarkerNavigationAction { menuOpts: { menuId: MarkerNavigationWidget.TitleMenu, title: NextMarkerAction.LABEL, - icon: registerIcon('marker-navigation-next', Codicon.chevronDown), + icon: registerIcon('marker-navigation-next', Codicon.chevronDown, nls.localize('nextMarkerIcon', 'Icon for goto next marker.')), group: 'navigation', order: 1 } @@ -224,7 +225,7 @@ class PrevMarkerAction extends MarkerNavigationAction { menuOpts: { menuId: MarkerNavigationWidget.TitleMenu, title: NextMarkerAction.LABEL, - icon: registerIcon('marker-navigation-previous', Codicon.chevronUp), + icon: registerIcon('marker-navigation-previous', Codicon.chevronUp, nls.localize('previousMarkerIcon', 'Icon for goto previous marker.')), group: 'navigation', order: 2 } diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 66bdc31f..10a503f6 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -30,6 +30,7 @@ import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { splitLines } from 'vs/base/common/strings'; class MessageWidget { @@ -102,7 +103,7 @@ class MessageWidget { } } - const lines = message.split(/\r\n|\r|\n/g); + const lines = splitLines(message); this._lines = lines.length; this._longestLineLength = 0; for (const line of lines) { @@ -296,7 +297,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { protected _fillHead(container: HTMLElement): void { super._fillHead(container); - this._disposables.add(this._actionbarWidget!.actionRunner.onDidBeforeRun(e => this.editor.focus())); + this._disposables.add(this._actionbarWidget!.actionRunner.onBeforeRun(e => this.editor.focus())); const actions: IAction[] = []; const menu = this._menuService.createMenu(MarkerNavigationWidget.TitleMenu, this._contextKeyService); diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 232aab8d..83503350 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -162,6 +162,9 @@ abstract class SymbolNavigationAction extends EditorAction { if (!range) { range = reference.range; } + if (!range) { + return undefined; + } const targetEditor = await editorService.openCodeEditor({ resource: reference.uri, diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index b7942a37..ead7b894 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -10,7 +10,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextKey, IContextKeyService, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ReferencesModel, OneReference } from '../referencesModel'; @@ -101,7 +101,7 @@ export abstract class ReferencesController implements IEditorContribution { this._disposables.add(this._widget.onDidClose(() => { modelPromise.cancel(); if (this._widget) { - this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL); + this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL, StorageTarget.MACHINE); this._widget = undefined; } this.closeWidget(); @@ -117,17 +117,17 @@ export abstract class ReferencesController implements IEditorContribution { if (event.source !== 'editor' || !this._configurationService.getValue('editor.stablePeek')) { // when stable peek is configured we don't close // the peek window on selecting the editor - this.openReference(element, false); + this.openReference(element, false, false); } break; case 'side': - this.openReference(element, true); + this.openReference(element, true, false); break; case 'goto': if (peekMode) { this._gotoReference(element); } else { - this.openReference(element, false); + this.openReference(element, false, true); } break; } @@ -285,7 +285,7 @@ export abstract class ReferencesController implements IEditorContribution { }); } - openReference(ref: Location, sideBySide: boolean): void { + openReference(ref: Location, sideBySide: boolean, pinned: boolean): void { // clear stage if (!sideBySide) { this.closeWidget(); @@ -294,7 +294,7 @@ export abstract class ReferencesController implements IEditorContribution { const { uri, range } = ref; this._editorService.openCodeEditor({ resource: uri, - options: { selection: range } + options: { selection: range, pinned } }, this._editor, sideBySide); } } @@ -404,7 +404,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const listService = accessor.get(IListService); const focus = listService.lastFocusedList?.getFocus(); if (Array.isArray(focus) && focus[0] instanceof OneReference) { - withController(accessor, controller => controller.openReference(focus[0], true)); + withController(accessor, controller => controller.openReference(focus[0], true, true)); } } }); @@ -413,6 +413,6 @@ CommandsRegistry.registerCommand('openReference', (accessor) => { const listService = accessor.get(IListService); const focus = listService.lastFocusedList?.getFocus(); if (Array.isArray(focus) && focus[0] instanceof OneReference) { - withController(accessor, controller => controller.openReference(focus[0], false)); + withController(accessor, controller => controller.openReference(focus[0], false, true)); } }); diff --git a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts index 5c4bbe4d..334928f3 100644 --- a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts +++ b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts @@ -41,10 +41,20 @@ export class OneReference { } get ariaMessage(): string { - return localize( - 'aria.oneReference', "symbol in {0} on line {1} at column {2}", - basename(this.uri), this.range.startLineNumber, this.range.startColumn - ); + + const preview = this.parent.getPreview(this)?.preview(this.range); + + if (!preview) { + return localize( + 'aria.oneReference', "symbol in {0} on line {1} at column {2}", + basename(this.uri), this.range.startLineNumber, this.range.startColumn + ); + } else { + return localize( + 'aria.oneReference.preview', "symbol in {0} on line {1} at column {2}, {3}", + basename(this.uri), this.range.startLineNumber, this.range.startColumn, preview.value + ); + } } } diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 92af0fba..c57fbadb 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -162,6 +162,15 @@ export class ModesHoverController implements IEditorContribution { return; } + + if ( + !this._isHoverSticky && targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ModesContentHoverWidget.ID + && this._contentWidget.value?.isColorPickerVisible() + ) { + // though the hover is not sticky, the color picker needs to. + return; + } + if (this._isHoverSticky && targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === ModesGlyphHoverWidget.ID) { // mouse moved on top of overlay hover widget return; diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 2cb7e793..ad2d6b27 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -31,12 +31,12 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/gotoError'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { IIdentifiedSingleEditOperation, TrackedRangeStickiness } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Constants } from 'vs/base/common/uint'; import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -251,6 +251,23 @@ export class ModesContentHoverWidget extends ContentHoverWidget { })); this._register(TokenizationRegistry.onDidChange((e) => { if (this.isVisible && this._lastRange && this._messages.length > 0) { + this._messages = this._messages.map(msg => { + // If a color hover is visible, we need to update the message that + // created it so that the color matches the last chosen color + if (msg instanceof ColorHover && !!this._lastRange?.intersectRanges(msg.range) && this._colorPicker?.model.color) { + const color = this._colorPicker.model.color; + const newColor = { + red: color.rgba.r / 255, + green: color.rgba.g / 255, + blue: color.rgba.b / 255, + alpha: color.rgba.a + }; + return new ColorHover(msg.range, newColor, msg.provider); + } else { + return msg; + } + }); + this._hover.contentsDomNode.textContent = ''; this._renderMessages(this._lastRange, this._messages); } @@ -406,15 +423,17 @@ export class ModesContentHoverWidget extends ContentHoverWidget { model.presentation.textEdit.range.endLineNumber, model.presentation.textEdit.range.endColumn ); - newRange = newRange.setEndPosition(newRange.endLineNumber, newRange.startColumn + model.presentation.textEdit.text.length); + const trackedRange = this._editor.getModel()!._setTrackedRange(null, newRange, TrackedRangeStickiness.GrowsOnlyWhenTypingAfter); + this._editor.pushUndoStop(); + this._editor.executeEdits('colorpicker', textEdits); + newRange = this._editor.getModel()!._getTrackedRange(trackedRange) || newRange; } else { textEdits = [{ identifier: null, range, text: model.presentation.label, forceMoveMarkers: false }]; newRange = range.setEndPosition(range.endLineNumber, range.startColumn + model.presentation.label.length); + this._editor.pushUndoStop(); + this._editor.executeEdits('colorpicker', textEdits); } - this._editor.pushUndoStop(); - this._editor.executeEdits('colorpicker', textEdits); - if (model.presentation.additionalTextEdits) { textEdits = [...model.presentation.additionalTextEdits as IIdentifiedSingleEditOperation[]]; this._editor.executeEdits('colorpicker', textEdits); @@ -461,7 +480,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { const markdownHoverElement = $('div.hover-row.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents')); const renderer = markdownDisposeables.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService)); - markdownDisposeables.add(renderer.onDidRenderCodeBlock(() => { + markdownDisposeables.add(renderer.onDidRenderAsync(() => { hoverContentsElement.className = 'hover-contents code-hover-contents'; this._hover.onContentsChanged(); })); @@ -559,6 +578,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { return hoverElement; } + private recentMarkerCodeActionsInfo: { marker: IMarker, hasCodeActions: boolean } | undefined = undefined; private renderMarkerStatusbar(markerHover: MarkerHover): HTMLElement { const hoverElement = $('div.hover-row.status-bar'); const disposables = new DisposableStore(); @@ -577,24 +597,28 @@ export class ModesContentHoverWidget extends ContentHoverWidget { if (!this._editor.getOption(EditorOption.readOnly)) { const quickfixPlaceholderElement = dom.append(actionsElement, $('div')); - quickfixPlaceholderElement.style.opacity = '0'; - quickfixPlaceholderElement.style.transition = 'opacity 0.2s'; - setTimeout(() => quickfixPlaceholderElement.style.opacity = '1', 200); - quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."); - disposables.add(toDisposable(() => quickfixPlaceholderElement.remove())); - + if (this.recentMarkerCodeActionsInfo) { + if (IMarkerData.makeKey(this.recentMarkerCodeActionsInfo.marker) === IMarkerData.makeKey(markerHover.marker)) { + if (!this.recentMarkerCodeActionsInfo.hasCodeActions) { + quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); + } + } else { + this.recentMarkerCodeActionsInfo = undefined; + } + } + const updatePlaceholderDisposable = disposables.add(disposableTimeout(() => quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."), 64)); const codeActionsPromise = this.getCodeActions(markerHover.marker); disposables.add(toDisposable(() => codeActionsPromise.cancel())); codeActionsPromise.then(actions => { - quickfixPlaceholderElement.style.transition = ''; - quickfixPlaceholderElement.style.opacity = '1'; + updatePlaceholderDisposable.dispose(); + this.recentMarkerCodeActionsInfo = { marker: markerHover.marker, hasCodeActions: actions.validActions.length > 0 }; - if (!actions.validActions.length) { + if (!this.recentMarkerCodeActionsInfo.hasCodeActions) { actions.dispose(); quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); return; } - quickfixPlaceholderElement.remove(); + quickfixPlaceholderElement.style.display = 'none'; let showing = false; disposables.add(toDisposable(() => { diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index 11c8b67f..720c8e76 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -84,7 +84,7 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu } if (currentLineText !== adjustedLineContent) { - indentEdits.push(EditOperation.replace(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, indentSize, insertSpaces))); + indentEdits.push(EditOperation.replaceMove(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(globalIndent, indentSize, insertSpaces))); } } else { globalIndent = strings.getLeadingWhitespace(currentLineText); @@ -115,7 +115,7 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu } if (oldIndentation !== idealIndentForNextLine) { - indentEdits.push(EditOperation.replace(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces))); + indentEdits.push(EditOperation.replaceMove(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), TextModel.normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces))); } // calculate idealIndentForNextLine @@ -472,7 +472,6 @@ export class AutoIndentOnPaste implements IEditorContribution { } const autoIndent = this.editor.getOption(EditorOption.autoIndent); const { tabSize, indentSize, insertSpaces } = model.getOptions(); - this.editor.pushUndoStop(); let textEdits: TextEdit[] = []; let indentConverter = { @@ -583,9 +582,12 @@ export class AutoIndentOnPaste implements IEditorContribution { } } - let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!); - this.editor.executeCommand('autoIndentOnPaste', cmd); - this.editor.pushUndoStop(); + if (textEdits.length > 0) { + this.editor.pushUndoStop(); + let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!); + this.editor.executeCommand('autoIndentOnPaste', cmd); + this.editor.pushUndoStop(); + } } private shouldIgnoreLine(model: ITextModel, lineNumber: number): boolean { diff --git a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts index 6b76a75b..d8885141 100644 --- a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts @@ -12,15 +12,17 @@ export class CopyLinesCommand implements ICommand { private readonly _selection: Selection; private readonly _isCopyingDown: boolean; + private readonly _noop: boolean; private _selectionDirection: SelectionDirection; private _selectionId: string | null; private _startLineNumberDelta: number; private _endLineNumberDelta: number; - constructor(selection: Selection, isCopyingDown: boolean) { + constructor(selection: Selection, isCopyingDown: boolean, noop?: boolean) { this._selection = selection; this._isCopyingDown = isCopyingDown; + this._noop = noop || false; this._selectionDirection = SelectionDirection.LTR; this._selectionId = null; this._startLineNumberDelta = 0; @@ -51,10 +53,14 @@ export class CopyLinesCommand implements ICommand { } } - if (!this._isCopyingDown) { - builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + sourceText); + if (this._noop) { + builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber + 1, 1), s.endLineNumber === model.getLineCount() ? '' : '\n'); } else { - builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), sourceText + '\n'); + if (!this._isCopyingDown) { + builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + sourceText); + } else { + builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), sourceText + '\n'); + } } this._selectionId = builder.trackSelection(s); diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index bc7fd660..9c365d50 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -63,10 +63,7 @@ abstract class AbstractCopyLinesAction extends EditorAction { const commands: ICommand[] = []; for (const selection of selections) { - if (selection.ignore) { - continue; - } - commands.push(new CopyLinesCommand(selection.selection, this.down)); + commands.push(new CopyLinesCommand(selection.selection, this.down, selection.ignore)); } editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts index d9600938..681c8f02 100644 --- a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { CompleteEnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { IIndentConverter, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; @@ -135,7 +135,8 @@ export class MoveLinesCommand implements ICommand { // to s.startLineNumber builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n'); - let ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber, insertingText); + let ret = this.matchEnterRuleMovingDown(model, indentConverter, tabSize, s.startLineNumber, movingLineNumber, insertingText); + // check if the line being moved before matches onEnter rules, if so let's adjust the indentation by onEnter rules. if (ret !== null) { if (ret !== 0) { @@ -229,31 +230,7 @@ export class MoveLinesCommand implements ICommand { }; } - private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) { - let validPrecedingLine = oneLineAbove; - while (validPrecedingLine >= 1) { - // ship empty lines as empty lines just inherit indentation - let lineContent; - if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) { - lineContent = oneLineAboveText; - } else { - lineContent = model.getLineContent(validPrecedingLine); - } - - let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); - if (nonWhitespaceIdx >= 0) { - break; - } - validPrecedingLine--; - } - - if (validPrecedingLine < 1 || line > model.getLineCount()) { - return null; - } - - let maxColumn = model.getLineMaxColumn(validPrecedingLine); - let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); - + private parseEnterResult(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, enter: CompleteEnterAction | null) { if (enter) { let enterPrefix = enter.indentation; @@ -283,6 +260,72 @@ export class MoveLinesCommand implements ICommand { return null; } + /** + * + * @param model + * @param indentConverter + * @param tabSize + * @param line the line moving down + * @param futureAboveLineNumber the line which will be at the `line` position + * @param futureAboveLineText + */ + private matchEnterRuleMovingDown(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, futureAboveLineNumber: number, futureAboveLineText: string) { + if (strings.lastNonWhitespaceIndex(futureAboveLineText) >= 0) { + // break + let maxColumn = model.getLineMaxColumn(futureAboveLineNumber); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(futureAboveLineNumber, maxColumn, futureAboveLineNumber, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } else { + // go upwards, starting from `line - 1` + let validPrecedingLine = line - 1; + while (validPrecedingLine >= 1) { + let lineContent = model.getLineContent(validPrecedingLine); + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); + + if (nonWhitespaceIdx >= 0) { + break; + } + + validPrecedingLine--; + } + + if (validPrecedingLine < 1 || line > model.getLineCount()) { + return null; + } + + let maxColumn = model.getLineMaxColumn(validPrecedingLine); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } + } + + private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) { + let validPrecedingLine = oneLineAbove; + while (validPrecedingLine >= 1) { + // ship empty lines as empty lines just inherit indentation + let lineContent; + if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) { + lineContent = oneLineAboveText; + } else { + lineContent = model.getLineContent(validPrecedingLine); + } + + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); + if (nonWhitespaceIdx >= 0) { + break; + } + validPrecedingLine--; + } + + if (validPrecedingLine < 1 || line > model.getLineCount()) { + return null; + } + + let maxColumn = model.getLineMaxColumn(validPrecedingLine); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } + private trimLeft(str: string) { return str.replace(/^\s+/, ''); } diff --git a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts index dd0a86ac..e8bdc29c 100644 --- a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts @@ -329,6 +329,7 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { mode.dispose(); }); + test('move line should still work as before if there is no indentation rules', () => { testMoveLinesUpWithIndentCommand( null!, @@ -351,3 +352,55 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { ); }); }); + +class EnterRulesMode extends MockMode { + private static readonly _id = new LanguageIdentifier('moveLinesEnterMode', 8); + constructor() { + super(EnterRulesMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + indentationRules: { + decreaseIndentPattern: /^\s*\[$/, + increaseIndentPattern: /^\s*\]$/, + }, + brackets: [ + ['{', '}'] + ] + })); + } +} + +suite('Editor - contrib - Move Lines Command honors onEnter Rules', () => { + + test('issue #54829. move block across block', () => { + let mode = new EnterRulesMode(); + + testMoveLinesDownWithIndentCommand( + mode.getLanguageIdentifier(), + + [ + 'if (true) {', + ' if (false) {', + ' if (1) {', + ' console.log(\'b\');', + ' }', + ' console.log(\'a\');', + ' }', + '}' + ], + new Selection(3, 9, 5, 10), + [ + 'if (true) {', + ' if (false) {', + ' console.log(\'a\');', + ' if (1) {', + ' console.log(\'b\');', + ' }', + ' }', + '}' + ], + new Selection(4, 9, 6, 10), + ); + + mode.dispose(); + }); +}); diff --git a/src/vs/editor/contrib/rename/onTypeRename.ts b/src/vs/editor/contrib/linkedEditing/linkedEditing.ts similarity index 81% rename from src/vs/editor/contrib/rename/onTypeRename.ts rename to src/vs/editor/contrib/linkedEditing/linkedEditing.ts index 105dfb2d..44146081 100644 --- a/src/vs/editor/contrib/rename/onTypeRename.ts +++ b/src/vs/editor/contrib/linkedEditing/linkedEditing.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/onTypeRename'; import * as nls from 'vs/nls'; import { registerEditorContribution, registerModelAndPositionCommand, EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import * as arrays from 'vs/base/common/arrays'; @@ -15,7 +14,7 @@ import { Position, IPosition } from 'vs/editor/common/core/position'; import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { OnTypeRenameProviderRegistry } from 'vs/editor/common/modes'; +import { LinkedEditingRangeProviderRegistry, LinkedEditingRanges } from 'vs/editor/common/modes'; import { first, createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -31,19 +30,21 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import { Color } from 'vs/base/common/color'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey('onTypeRenameInputVisible', false); +export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey('LinkedEditingInputVisible', false); -export class OnTypeRenameContribution extends Disposable implements IEditorContribution { +const DECORATION_CLASS_NAME = 'linked-editing-decoration'; - public static readonly ID = 'editor.contrib.onTypeRename'; +export class LinkedEditingContribution extends Disposable implements IEditorContribution { + + public static readonly ID = 'editor.contrib.linkedEditing'; private static readonly DECORATION = ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges, - className: 'on-type-rename-decoration' + className: DECORATION_CLASS_NAME }); - static get(editor: ICodeEditor): OnTypeRenameContribution { - return editor.getContribution(OnTypeRenameContribution.ID); + static get(editor: ICodeEditor): LinkedEditingContribution { + return editor.getContribution(LinkedEditingContribution.ID); } private _debounceDuration = 200; @@ -92,11 +93,11 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._register(this._editor.onDidChangeModel(() => this.reinitialize())); this._register(this._editor.onDidChangeConfiguration(e => { - if (e.hasChanged(EditorOption.renameOnType)) { + if (e.hasChanged(EditorOption.linkedEditing) || e.hasChanged(EditorOption.renameOnType)) { this.reinitialize(); } })); - this._register(OnTypeRenameProviderRegistry.onDidChange(() => this.reinitialize())); + this._register(LinkedEditingRangeProviderRegistry.onDidChange(() => this.reinitialize())); this._register(this._editor.onDidChangeModelLanguage(() => this.reinitialize())); this.reinitialize(); @@ -104,7 +105,7 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr private reinitialize() { const model = this._editor.getModel(); - const isEnabled = model !== null && this._editor.getOption(EditorOption.renameOnType) && OnTypeRenameProviderRegistry.has(model); + const isEnabled = model !== null && (this._editor.getOption(EditorOption.linkedEditing) || this._editor.getOption(EditorOption.renameOnType)) && LinkedEditingRangeProviderRegistry.has(model); if (isEnabled === this._enabled) { return; } @@ -219,9 +220,10 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } try { + this._editor.popUndoStop(); this._ignoreChangeEvent = true; const prevEditOperationType = this._editor._getViewModel().getPrevEditOperationType(); - this._editor.executeEdits('onTypeRename', edits); + this._editor.executeEdits('linkedEditing', edits); this._editor._getViewModel().setPrevEditOperationType(prevEditOperationType); } finally { this._ignoreChangeEvent = false; @@ -282,7 +284,7 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr this._currentRequestModelVersion = modelVersionId; const request = createCancelablePromise(async token => { try { - const response = await getOnTypeRenameRanges(model, position, token); + const response = await getLinkedEditingRanges(model, position, token); if (request !== this._currentRequest) { return; } @@ -312,12 +314,12 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr } if (!foundReferenceRange) { - // Cannot do on type rename if the ranges are not where the cursor is... + // Cannot do linked editing if the ranges are not where the cursor is... this.clearRanges(); return; } - const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: OnTypeRenameContribution.DECORATION })); + const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: LinkedEditingContribution.DECORATION })); this._visibleContextKey.set(true); this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations); } catch (err) { @@ -361,12 +363,12 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr // } } -export class OnTypeRenameAction extends EditorAction { +export class LinkedEditingAction extends EditorAction { constructor() { super({ - id: 'editor.action.onTypeRename', - label: nls.localize('onTypeRename.label', "On Type Rename Symbol"), - alias: 'On Type Rename Symbol', + id: 'editor.action.linkedEditing', + label: nls.localize('linkedEditing.label', "Start Linked Editing"), + alias: 'Start Linked Editing', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasRenameProvider), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -397,7 +399,7 @@ export class OnTypeRenameAction extends EditorAction { } run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { - const controller = OnTypeRenameContribution.get(editor); + const controller = LinkedEditingContribution.get(editor); if (controller) { return Promise.resolve(controller.updateRanges(true)); } @@ -405,9 +407,9 @@ export class OnTypeRenameAction extends EditorAction { } } -const OnTypeRenameCommand = EditorCommand.bindToContribution(OnTypeRenameContribution.get); -registerEditorCommand(new OnTypeRenameCommand({ - id: 'cancelOnTypeRenameInput', +const LinkedEditingCommand = EditorCommand.bindToContribution(LinkedEditingContribution.get); +registerEditorCommand(new LinkedEditingCommand({ + id: 'cancelLinkedEditingInput', precondition: CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE, handler: x => x.clearRanges(), kbOpts: { @@ -419,45 +421,31 @@ registerEditorCommand(new OnTypeRenameCommand({ })); -export function getOnTypeRenameRanges(model: ITextModel, position: Position, token: CancellationToken): Promise<{ - ranges: IRange[], - wordPattern?: RegExp -} | undefined | null> { - const orderedByScore = OnTypeRenameProviderRegistry.ordered(model); +function getLinkedEditingRanges(model: ITextModel, position: Position, token: CancellationToken): Promise { + const orderedByScore = LinkedEditingRangeProviderRegistry.ordered(model); - // in order of score ask the occurrences provider + // in order of score ask the linked editing range provider // until someone response with a good result - // (good = none empty array) - return first<{ - ranges: IRange[], - wordPattern?: RegExp - } | undefined>(orderedByScore.map(provider => () => { - return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((res) => { - if (!res) { - return undefined; - } - - return { - ranges: res.ranges, - wordPattern: res.wordPattern || provider.wordPattern - }; - }, (err) => { - onUnexpectedExternalError(err); + // (good = not null) + return first(orderedByScore.map(provider => async () => { + try { + return await provider.provideLinkedEditingRanges(model, position, token); + } catch (e) { + onUnexpectedExternalError(e); return undefined; - }); - + } }), result => !!result && arrays.isNonEmptyArray(result?.ranges)); } -export const editorOnTypeRenameBackground = registerColor('editor.onTypeRenameBackground', { dark: Color.fromHex('#f00').transparent(0.3), light: Color.fromHex('#f00').transparent(0.3), hc: Color.fromHex('#f00').transparent(0.3) }, nls.localize('editorOnTypeRenameBackground', 'Background color when the editor auto renames on type.')); +export const editorLinkedEditingBackground = registerColor('editor.linkedEditingBackground', { dark: Color.fromHex('#f00').transparent(0.3), light: Color.fromHex('#f00').transparent(0.3), hc: Color.fromHex('#f00').transparent(0.3) }, nls.localize('editorLinkedEditingBackground', 'Background color when the editor auto renames on type.')); registerThemingParticipant((theme, collector) => { - const editorOnTypeRenameBackgroundColor = theme.getColor(editorOnTypeRenameBackground); - if (editorOnTypeRenameBackgroundColor) { - collector.addRule(`.monaco-editor .on-type-rename-decoration { background: ${editorOnTypeRenameBackgroundColor}; border-left-color: ${editorOnTypeRenameBackgroundColor}; }`); + const editorLinkedEditingBackgroundColor = theme.getColor(editorLinkedEditingBackground); + if (editorLinkedEditingBackgroundColor) { + collector.addRule(`.monaco-editor .${DECORATION_CLASS_NAME} { background: ${editorLinkedEditingBackgroundColor}; border-left-color: ${editorLinkedEditingBackgroundColor}; }`); } }); -registerModelAndPositionCommand('_executeRenameOnTypeProvider', (model, position) => getOnTypeRenameRanges(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeLinkedEditingProvider', (model, position) => getLinkedEditingRanges(model, position, CancellationToken.None)); -registerEditorContribution(OnTypeRenameContribution.ID, OnTypeRenameContribution); -registerEditorAction(OnTypeRenameAction); +registerEditorContribution(LinkedEditingContribution.ID, LinkedEditingContribution); +registerEditorAction(LinkedEditingAction); diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/linkedEditing/test/linkedEditing.test..ts similarity index 91% rename from src/vs/editor/contrib/rename/test/onTypeRename.test.ts rename to src/vs/editor/contrib/linkedEditing/test/linkedEditing.test..ts index 05dba5e4..9b00cb4b 100644 --- a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts +++ b/src/vs/editor/contrib/linkedEditing/test/linkedEditing.test..ts @@ -10,12 +10,13 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Handler } from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; -import { OnTypeRenameContribution } from 'vs/editor/contrib/rename/onTypeRename'; +import { LinkedEditingContribution } from 'vs/editor/contrib/linkedEditing/linkedEditing'; import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { ITextModel } from 'vs/editor/common/model'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; const mockFile = URI.parse('test:somefile.ttt'); const mockFileSelector = { scheme: 'test' }; @@ -29,7 +30,12 @@ interface TestEditor { redo(): void; } -suite('On type rename', () => { +const languageIdentifier = new modes.LanguageIdentifier('linkedEditingTestLangage', 74); +LanguageConfigurationRegistry.register(languageIdentifier, { + wordPattern: /[a-zA-Z]+/ +}); + +suite('linked editing', () => { const disposables = new DisposableStore(); setup(() => { @@ -42,8 +48,8 @@ suite('On type rename', () => { function createMockEditor(text: string | string[]): ITestCodeEditor { const model = typeof text === 'string' - ? createTextModel(text, undefined, undefined, mockFile) - : createTextModel(text.join('\n'), undefined, undefined, mockFile); + ? createTextModel(text, undefined, languageIdentifier, mockFile) + : createTextModel(text.join('\n'), undefined, languageIdentifier, mockFile); const editor = createTestCodeEditor({ model }); disposables.add(model); @@ -55,18 +61,16 @@ suite('On type rename', () => { function testCase( name: string, - initialState: { text: string | string[], responseWordPattern?: RegExp, providerWordPattern?: RegExp }, + initialState: { text: string | string[], responseWordPattern?: RegExp }, operations: (editor: TestEditor) => Promise, expectedEndText: string | string[] ) { test(name, async () => { - disposables.add(modes.OnTypeRenameProviderRegistry.register(mockFileSelector, { - wordPattern: initialState.providerWordPattern, - provideOnTypeRenameRanges(model: ITextModel, pos: IPosition) { + disposables.add(modes.LinkedEditingRangeProviderRegistry.register(mockFileSelector, { + provideLinkedEditingRanges(model: ITextModel, pos: IPosition) { const wordAtPos = model.getWordAtPosition(pos); if (wordAtPos) { const matches = model.findMatches(wordAtPos.word, false, false, true, USUAL_WORD_SEPARATORS, false); - assert.ok(matches.length > 0); return { ranges: matches.map(m => m.range), wordPattern: initialState.responseWordPattern }; } return { ranges: [], wordPattern: initialState.responseWordPattern }; @@ -74,25 +78,25 @@ suite('On type rename', () => { })); const editor = createMockEditor(initialState.text); - editor.updateOptions({ renameOnType: true }); - const ontypeRenameContribution = editor.registerAndInstantiateContribution( - OnTypeRenameContribution.ID, - OnTypeRenameContribution + editor.updateOptions({ linkedEditing: true }); + const linkedEditingContribution = editor.registerAndInstantiateContribution( + LinkedEditingContribution.ID, + LinkedEditingContribution ); - ontypeRenameContribution.setDebounceDuration(0); + linkedEditingContribution.setDebounceDuration(0); const testEditor: TestEditor = { setPosition(pos: Position) { editor.setPosition(pos); - return ontypeRenameContribution.currentUpdateTriggerPromise; + return linkedEditingContribution.currentUpdateTriggerPromise; }, setSelection(sel: IRange) { editor.setSelection(sel); - return ontypeRenameContribution.currentUpdateTriggerPromise; + return linkedEditingContribution.currentUpdateTriggerPromise; }, trigger(source: string | null | undefined, handlerId: string, payload: any) { editor.trigger(source, handlerId, payload); - return ontypeRenameContribution.currentSyncTriggerPromise; + return linkedEditingContribution.currentSyncTriggerPromise; }, undo() { CoreEditingCommands.Undo.runEditorCommand(null, editor, null); @@ -247,7 +251,7 @@ suite('On type rename', () => { // testCase('Selection insert - across two boundary', state, async (editor) => { // const pos = new Position(1, 2); // await editor.setPosition(pos); - // await ontypeRenameContribution.updateLinkedUI(pos); + // await linkedEditingContribution.updateLinkedUI(pos); // await editor.setSelection(new Range(1, 4, 1, 9)); // await editor.trigger('keyboard', Handler.Type, { text: 'i' }); // }, ''); @@ -299,7 +303,7 @@ suite('On type rename', () => { const state3 = { ...state, - providerWordPattern: /[a-yA-Y]+/ + responseWordPattern: /[a-yA-Y]+/ }; testCase('Breakout with stop pattern - insert', state3, async (editor) => { @@ -334,7 +338,6 @@ suite('On type rename', () => { const state4 = { ...state, - providerWordPattern: /[a-yA-Y]+/, responseWordPattern: /[a-eA-E]+/ }; @@ -380,7 +383,7 @@ suite('On type rename', () => { // testCase('Delete - left all', state, async (editor) => { // const pos = new Position(1, 3); // await editor.setPosition(pos); - // await ontypeRenameContribution.updateLinkedUI(pos); + // await linkedEditingContribution.updateLinkedUI(pos); // await editor.trigger('keyboard', 'deleteAllLeft', {}); // }, '>'); @@ -390,7 +393,7 @@ suite('On type rename', () => { // testCase('Delete - left all then undo', state, async (editor) => { // const pos = new Position(1, 5); // await editor.setPosition(pos); - // await ontypeRenameContribution.updateLinkedUI(pos); + // await linkedEditingContribution.updateLinkedUI(pos); // await editor.trigger('keyboard', 'deleteAllLeft', {}); // editor.undo(); // }, '>'); diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 283e8ef3..04e8b8aa 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -47,7 +47,17 @@ function getHoverMessage(link: Link, useMetaKey: boolean): MarkdownString { : nls.localize('links.navigate.kb.alt', "alt + click"); if (link.url) { - const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString()}) (${kb})`); + let nativeLabel = ''; + if (/^command:/i.test(link.url.toString())) { + // Don't show complete command arguments in the native tooltip + const match = link.url.toString().match(/^command:([^?#]+)/); + if (match) { + const commandId = match[1]; + const nativeLabelText = nls.localize('tooltip.explanation', "Execute command {0}", commandId); + nativeLabel = ` "${nativeLabelText}"`; + } + } + const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString()}${nativeLabel}) (${kb})`); return hoverMessage; } else { return new MarkdownString().appendText(`${label} (${kb})`); diff --git a/src/vs/editor/contrib/message/messageController.css b/src/vs/editor/contrib/message/messageController.css index a3910415..924349d1 100644 --- a/src/vs/editor/contrib/message/messageController.css +++ b/src/vs/editor/contrib/message/messageController.css @@ -8,6 +8,12 @@ z-index: 10000; } +.monaco-editor .monaco-editor-overlaymessage.below { + padding-bottom: 0; + padding-top: 8px; + z-index: 10000; +} + @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } @@ -37,3 +43,13 @@ border-width: 8px; position: absolute; } + +.monaco-editor .monaco-editor-overlaymessage:not(.below) .anchor.top, +.monaco-editor .monaco-editor-overlaymessage.below .anchor.below { + display: none; +} + +.monaco-editor .monaco-editor-overlaymessage.below .anchor.top { + display: inherit; + top: -8px; +} diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 6bec24fd..319f0d52 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -7,7 +7,7 @@ import 'vs/css!./messageController'; import * as nls from 'vs/nls'; import { TimeoutTimer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; @@ -20,7 +20,7 @@ import { inputValidationInfoBorder, inputValidationInfoBackground, inputValidati import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; -export class MessageController extends Disposable implements IEditorContribution { +export class MessageController implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; @@ -32,21 +32,24 @@ export class MessageController extends Disposable implements IEditorContribution private readonly _editor: ICodeEditor; private readonly _visible: IContextKey; - private readonly _messageWidget = this._register(new MutableDisposable()); - private readonly _messageListeners = this._register(new DisposableStore()); + private readonly _messageWidget = new MutableDisposable(); + private readonly _messageListeners = new DisposableStore(); + private readonly _editorListener: IDisposable; constructor( editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService ) { - super(); + this._editor = editor; this._visible = MessageController.MESSAGE_VISIBLE.bindTo(contextKeyService); - this._register(this._editor.onDidAttemptReadOnlyEdit(() => this._onDidAttemptReadOnlyEdit())); + this._editorListener = this._editor.onDidAttemptReadOnlyEdit(() => this._onDidAttemptReadOnlyEdit()); } dispose(): void { - super.dispose(); + this._editorListener.dispose(); + this._messageListeners.dispose(); + this._messageWidget.dispose(); this._visible.reset(); } @@ -150,14 +153,18 @@ class MessageWidget implements IContentWidget { this._domNode = document.createElement('div'); this._domNode.classList.add('monaco-editor-overlaymessage'); + const anchorTop = document.createElement('div'); + anchorTop.classList.add('anchor', 'top'); + this._domNode.appendChild(anchorTop); + const message = document.createElement('div'); message.classList.add('message'); message.textContent = text; this._domNode.appendChild(message); - const anchor = document.createElement('div'); - anchor.classList.add('anchor'); - this._domNode.appendChild(anchor); + const anchorBottom = document.createElement('div'); + anchorBottom.classList.add('anchor', 'below'); + this._domNode.appendChild(anchorBottom); this._editor.addContentWidget(this); this._domNode.classList.add('fadeIn'); @@ -178,6 +185,11 @@ class MessageWidget implements IContentWidget { getPosition(): IContentWidgetPosition { return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW] }; } + + afterRender(position: ContentWidgetPositionPreference | null): void { + this._domNode.classList.toggle('below', position === ContentWidgetPositionPreference.BELOW); + } + } registerEditorContribution(MessageController.ID, MessageController); @@ -186,7 +198,8 @@ registerThemingParticipant((theme, collector) => { const border = theme.getColor(inputValidationInfoBorder); if (border) { let borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1; - collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: ${border}; }`); + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor.below { border-top-color: ${border}; }`); + collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor.top { border-bottom-color: ${border}; }`); collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { border: ${borderWidth}px solid ${border}; }`); } const background = theme.getColor(inputValidationInfoBackground); diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 6a7c295e..3606348f 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -1016,6 +1016,11 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut }); this.decorations = this.editor.deltaDecorations(this.decorations, decorations); + + const currentFindState = CommonFindController.get(this.editor).getState(); + if (currentFindState.isRegex || currentFindState.matchCase || currentFindState.wholeWord) { + CommonFindController.get(this.editor).highlightFindOptions(true); + } } private static readonly _SELECTION_HIGHLIGHT_OVERVIEW = ModelDecorationOptions.register({ diff --git a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts index 8b25ecac..6e1e0a9b 100644 --- a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts @@ -61,7 +61,8 @@ suite('Multicursor selection', () => { let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeStorage: Event.None, + onDidChangeValue: Event.None, + onDidChangeTarget: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], @@ -70,8 +71,9 @@ suite('Multicursor selection', () => { remove: (key) => undefined, logStorage: () => undefined, migrate: (toWorkspace) => Promise.resolve(undefined), - flush: () => undefined, - isNew: () => true + flush: () => Promise.resolve(undefined), + isNew: () => true, + keys: () => [] } as IStorageService); test('issue #8817: Cursor position changes when you cancel multicursor', () => { diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 2d0d4bcd..54b7a807 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -20,17 +20,18 @@ import * as nls from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { editorHoverBackground, editorHoverBorder, textCodeBlockBackground, textLinkForeground, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { assertIsDefined } from 'vs/base/common/types'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const $ = dom.$; -const parameterHintsNextIcon = registerIcon('parameter-hints-next', Codicon.chevronDown); -const parameterHintsPreviousIcon = registerIcon('parameter-hints-previous', Codicon.chevronUp); +const parameterHintsNextIcon = registerIcon('parameter-hints-next', Codicon.chevronDown, nls.localize('parameterHintsNextIcon', 'Icon for show next parameter hint.')); +const parameterHintsPreviousIcon = registerIcon('parameter-hints-previous', Codicon.chevronUp, nls.localize('parameterHintsPreviousIcon', 'Icon for show previous parameter hint.')); export class ParameterHintsWidget extends Disposable implements IContentWidget { @@ -84,9 +85,9 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { wrapper.tabIndex = -1; const controls = dom.append(wrapper, $('.controls')); - const previous = dom.append(controls, $('.button' + parameterHintsPreviousIcon.cssSelector)); + const previous = dom.append(controls, $('.button' + ThemeIcon.asCSSSelector(parameterHintsPreviousIcon))); const overloads = dom.append(controls, $('.overloads')); - const next = dom.append(controls, $('.button' + parameterHintsNextIcon.cssSelector)); + const next = dom.append(controls, $('.button' + ThemeIcon.asCSSSelector(parameterHintsNextIcon))); const onPreviousClick = stop(domEvent(previous, 'click')); this._register(onPreviousClick(this.previous, this)); diff --git a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts index 8aaf09f8..d513ef4b 100644 --- a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts +++ b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts @@ -5,12 +5,15 @@ import { first } from 'vs/base/common/async'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { Position } from 'vs/editor/common/core/position'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { URI } from 'vs/base/common/uri'; +import { assertType } from 'vs/base/common/types'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export const Context = { Visible: new RawContextKey('parameterHintsVisible', false), @@ -32,17 +35,29 @@ export function provideSignatureHelp( })); } -registerDefaultLanguageCommand('_executeSignatureHelpProvider', async (model, position, args) => { - const result = await provideSignatureHelp(model, position, { - triggerKind: modes.SignatureHelpTriggerKind.Invoke, - isRetrigger: false, - triggerCharacter: args['triggerCharacter'] - }, CancellationToken.None); +CommandsRegistry.registerCommand('_executeSignatureHelpProvider', async (accessor, ...args: [URI, IPosition, string?]) => { + const [uri, position, triggerCharacter] = args; + assertType(URI.isUri(uri)); + assertType(Position.isIPosition(position)); + assertType(typeof triggerCharacter === 'string' || !triggerCharacter); - if (!result) { - return undefined; + const ref = await accessor.get(ITextModelService).createModelReference(uri); + try { + + const result = await provideSignatureHelp(ref.object.textEditorModel, Position.lift(position), { + triggerKind: modes.SignatureHelpTriggerKind.Invoke, + isRetrigger: false, + triggerCharacter, + }, CancellationToken.None); + + if (!result) { + return undefined; + } + + setTimeout(() => result.dispose(), 0); + return result.value; + + } finally { + ref.dispose(); } - - setTimeout(() => result.dispose(), 0); - return result.value; }); diff --git a/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts index 039dfbd3..ef8cf91b 100644 --- a/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts @@ -26,6 +26,20 @@ export interface IEditorNavigationQuickAccessOptions { canAcceptInBackground?: boolean; } +export interface IQuickAccessTextEditorContext { + + /** + * The current active editor. + */ + readonly editor: IEditor; + + /** + * If defined, allows to restore the original view state + * the text editor had before quick access opened. + */ + restoreViewState?: () => void; +} + /** * A reusable quick access provider for the editor with support * for adding decorations for navigating in the currently active file @@ -69,6 +83,7 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu // With text control const editor = this.activeTextEditorControl; if (editor && this.canProvideWithTextEditor(editor)) { + const context: IQuickAccessTextEditorContext = { editor }; // Restore any view state if this picker was closed // without actually going to a line @@ -84,18 +99,20 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState()); })); - disposables.add(once(token.onCancellationRequested)(() => { + context.restoreViewState = () => { if (lastKnownEditorViewState && editor === this.activeTextEditorControl) { editor.restoreViewState(lastKnownEditorViewState); } - })); + }; + + disposables.add(once(token.onCancellationRequested)(() => context.restoreViewState?.())); } // Clean up decorations on dispose disposables.add(toDisposable(() => this.clearDecorations(editor))); // Ask subclass for entries - disposables.add(this.provideWithTextEditor(editor, picker, token)); + disposables.add(this.provideWithTextEditor(context, picker, token)); } // Without text control @@ -116,14 +133,14 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu /** * Subclasses to implement to provide picks for the picker when an editor is active. */ - protected abstract provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable; + protected abstract provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable; /** * Subclasses to implement to provide picks for the picker when no editor is active. */ protected abstract provideWithoutTextEditor(picker: IQuickPick, token: CancellationToken): IDisposable; - protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { + protected gotoLocation({ editor }: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { editor.setSelection(options.range); editor.revealRangeInCenter(options.range, ScrollType.Smooth); if (!options.preserveFocus) { diff --git a/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts index 0c17d7c0..2885cc4f 100644 --- a/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts @@ -9,7 +9,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { IRange } from 'vs/editor/common/core/range'; -import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; +import { AbstractEditorNavigationQuickAccessProvider, IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; import { IPosition } from 'vs/editor/common/core/position'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; @@ -33,7 +33,8 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor return Disposable.None; } - protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + protected provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable { + const editor = context.editor; const disposables = new DisposableStore(); // Goto line once picked @@ -44,7 +45,7 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor return; } - this.gotoLocation(editor, { range: this.toRange(item.lineNumber, item.column), keyMods: picker.keyMods, preserveFocus: event.inBackground }); + this.gotoLocation(context, { range: this.toRange(item.lineNumber, item.column), keyMods: picker.keyMods, preserveFocus: event.inBackground }); if (!event.inBackground) { picker.hide(); diff --git a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts index ca85c3ce..9fb3e3d3 100644 --- a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts @@ -7,10 +7,10 @@ import { localize } from 'vs/nls'; import { IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IEditor, ScrollType } from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; +import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions, IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes'; import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { trim, format } from 'vs/base/common/strings'; @@ -48,7 +48,8 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit return Disposable.None; } - protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + protected provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable { + const editor = context.editor; const model = this.getModel(editor); if (!model) { return Disposable.None; @@ -56,16 +57,16 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit // Provide symbols from model if available in registry if (DocumentSymbolProviderRegistry.has(model)) { - return this.doProvideWithEditorSymbols(editor, model, picker, token); + return this.doProvideWithEditorSymbols(context, model, picker, token); } // Otherwise show an entry for a model without registry // But give a chance to resolve the symbols at a later // point if possible - return this.doProvideWithoutEditorSymbols(editor, model, picker, token); + return this.doProvideWithoutEditorSymbols(context, model, picker, token); } - private doProvideWithoutEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + private doProvideWithoutEditorSymbols(context: IQuickAccessTextEditorContext, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { const disposables = new DisposableStore(); // Generic pick for not having any symbol information @@ -82,7 +83,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit return; } - disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token)); + disposables.add(this.doProvideWithEditorSymbols(context, model, picker, token)); })(); return disposables; @@ -116,14 +117,15 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit return symbolProviderRegistryPromise; } - private doProvideWithEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + private doProvideWithEditorSymbols(context: IQuickAccessTextEditorContext, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { + const editor = context.editor; const disposables = new DisposableStore(); // Goto symbol once picked disposables.add(picker.onDidAccept(event => { const [item] = picker.selectedItems; if (item && item.range) { - this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods, preserveFocus: event.inBackground }); + this.gotoLocation(context, { range: item.range.selection, keyMods: picker.keyMods, preserveFocus: event.inBackground }); if (!event.inBackground) { picker.hide(); @@ -134,7 +136,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit // Goto symbol side by side if enabled disposables.add(picker.onDidTriggerItemButton(({ item }) => { if (item && item.range) { - this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods, forceSideBySide: true }); + this.gotoLocation(context, { range: item.range.selection, keyMods: picker.keyMods, forceSideBySide: true }); picker.hide(); } diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 214d00ff..41547308 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -358,7 +358,7 @@ registerModelAndPositionCommand('_executeDocumentRenameProvider', function (mode }); -//todo@joh use editor options world +//todo@jrieken use editor options world Registry.as(Extensions.Configuration).registerConfiguration({ id: 'editor', properties: { diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index c794e4cc..a73fab12 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -100,7 +100,7 @@ export class RenameInputField implements IContentWidget { const widgetShadowColor = theme.getColor(widgetShadow); this._domNode.style.backgroundColor = String(theme.getColor(editorWidgetBackground) ?? ''); - this._domNode.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; + this._domNode.style.boxShadow = widgetShadowColor ? ` 0 0 8px 2px ${widgetShadowColor}` : ''; this._domNode.style.color = String(theme.getColor(inputForeground) ?? ''); this._input.style.backgroundColor = String(theme.getColor(inputBackground) ?? ''); diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 714a27d7..713d8503 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -23,9 +23,7 @@ import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSe import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; -import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; class SelectionRanges { @@ -50,49 +48,36 @@ class SelectionRanges { class SmartSelectController implements IEditorContribution { - public static readonly ID = 'editor.contrib.smartSelectController'; + static readonly ID = 'editor.contrib.smartSelectController'; static get(editor: ICodeEditor): SmartSelectController { return editor.getContribution(SmartSelectController.ID); } - private readonly _editor: ICodeEditor; - private _state?: SelectionRanges[]; private _selectionListener?: IDisposable; private _ignoreSelection: boolean = false; - constructor( - editor: ICodeEditor, - @ITextResourceConfigurationService private readonly _configService: ITextResourceConfigurationService, - ) { - this._editor = editor; - } + constructor(private readonly _editor: ICodeEditor) { } dispose(): void { this._selectionListener?.dispose(); } - run(forward: boolean): Promise | void { + async run(forward: boolean): Promise { if (!this._editor.hasModel()) { return; } const selections = this._editor.getSelections(); const model = this._editor.getModel(); - if (!modes.SelectionRangeRegistry.has(model)) { return; } - - let promise: Promise = Promise.resolve(undefined); - if (!this._state) { - const selectLeadingAndTrailingWhitespace = this._configService.getValue(model.uri, 'editor.smartSelect.selectLeadingAndTrailingWhitespace') ?? true; - - promise = provideSelectionRanges(model, selections.map(s => s.getPosition()), { selectLeadingAndTrailingWhitespace }, CancellationToken.None).then(ranges => { + await provideSelectionRanges(model, selections.map(s => s.getPosition()), this._editor.getOption(EditorOption.smartSelect), CancellationToken.None).then(ranges => { if (!arrays.isNonEmptyArray(ranges) || ranges.length !== selections.length) { // invalid result return; @@ -125,21 +110,18 @@ class SmartSelectController implements IEditorContribution { }); } - return promise.then(() => { - if (!this._state) { - // no state - return; - } - this._state = this._state.map(state => state.mov(forward)); - const selections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition())); - this._ignoreSelection = true; - try { - this._editor.setSelections(selections); - } finally { - this._ignoreSelection = false; - } - - }); + if (!this._state) { + // no state + return; + } + this._state = this._state.map(state => state.mov(forward)); + const newSelections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition())); + this._ignoreSelection = true; + try { + this._editor.setSelections(newSelections); + } finally { + this._ignoreSelection = false; + } } } @@ -186,21 +168,6 @@ class GrowSelectionAction extends AbstractSmartSelect { } } -//todo@jrieken use proper editor config instead. however, to keep the number -// of changes low use the quick config definition -Registry.as(Extensions.Configuration).registerConfiguration({ - id: 'editor', - properties: { - 'editor.smartSelect.selectLeadingAndTrailingWhitespace': { - scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, - description: nls.localize('selectLeadingAndTrailingWhitespace', "Weather leading and trailing whitespace should always be selected."), - default: true, - type: 'boolean' - } - } -}); - - // renamed command id CommandsRegistry.registerCommandAlias('editor.action.smartSelect.grow', 'editor.action.smartSelect.expand'); @@ -241,7 +208,7 @@ export interface SelectionRangesOptions { selectLeadingAndTrailingWhitespace: boolean } -export function provideSelectionRanges(model: ITextModel, positions: Position[], options: SelectionRangesOptions, token: CancellationToken): Promise { +export async function provideSelectionRanges(model: ITextModel, positions: Position[], options: SelectionRangesOptions, token: CancellationToken): Promise { const providers = modes.SelectionRangeRegistry.all(model); @@ -271,66 +238,65 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], }, onUnexpectedExternalError)); } - return Promise.all(work).then(() => { + await Promise.all(work); - return allRawRanges.map(oneRawRanges => { + return allRawRanges.map(oneRawRanges => { - if (oneRawRanges.length === 0) { - return []; + if (oneRawRanges.length === 0) { + return []; + } + + // sort all by start/end position + oneRawRanges.sort((a, b) => { + if (Position.isBefore(a.getStartPosition(), b.getStartPosition())) { + return 1; + } else if (Position.isBefore(b.getStartPosition(), a.getStartPosition())) { + return -1; + } else if (Position.isBefore(a.getEndPosition(), b.getEndPosition())) { + return -1; + } else if (Position.isBefore(b.getEndPosition(), a.getEndPosition())) { + return 1; + } else { + return 0; } - - // sort all by start/end position - oneRawRanges.sort((a, b) => { - if (Position.isBefore(a.getStartPosition(), b.getStartPosition())) { - return 1; - } else if (Position.isBefore(b.getStartPosition(), a.getStartPosition())) { - return -1; - } else if (Position.isBefore(a.getEndPosition(), b.getEndPosition())) { - return -1; - } else if (Position.isBefore(b.getEndPosition(), a.getEndPosition())) { - return 1; - } else { - return 0; - } - }); - - // remove ranges that don't contain the former range or that are equal to the - // former range - let oneRanges: Range[] = []; - let last: Range | undefined; - for (const range of oneRawRanges) { - if (!last || (Range.containsRange(range, last) && !Range.equalsRange(range, last))) { - oneRanges.push(range); - last = range; - } - } - - if (!options.selectLeadingAndTrailingWhitespace) { - return oneRanges; - } - - // add ranges that expand trivia at line starts and ends whenever a range - // wraps onto the a new line - let oneRangesWithTrivia: Range[] = [oneRanges[0]]; - for (let i = 1; i < oneRanges.length; i++) { - const prev = oneRanges[i - 1]; - const cur = oneRanges[i]; - if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { - // add line/block range without leading/failing whitespace - const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); - if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) { - oneRangesWithTrivia.push(rangeNoWhitespace); - } - // add line/block range - const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); - if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) { - oneRangesWithTrivia.push(rangeFull); - } - } - oneRangesWithTrivia.push(cur); - } - return oneRangesWithTrivia; }); + + // remove ranges that don't contain the former range or that are equal to the + // former range + let oneRanges: Range[] = []; + let last: Range | undefined; + for (const range of oneRawRanges) { + if (!last || (Range.containsRange(range, last) && !Range.equalsRange(range, last))) { + oneRanges.push(range); + last = range; + } + } + + if (!options.selectLeadingAndTrailingWhitespace) { + return oneRanges; + } + + // add ranges that expand trivia at line starts and ends whenever a range + // wraps onto the a new line + let oneRangesWithTrivia: Range[] = [oneRanges[0]]; + for (let i = 1; i < oneRanges.length; i++) { + const prev = oneRanges[i - 1]; + const cur = oneRanges[i]; + if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { + // add line/block range without leading/failing whitespace + const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); + if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) { + oneRangesWithTrivia.push(rangeNoWhitespace); + } + // add line/block range + const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); + if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) { + oneRangesWithTrivia.push(rangeFull); + } + } + oneRangesWithTrivia.push(cur); + } + return oneRangesWithTrivia; }); } diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 7f91f835..90abafa2 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -24,6 +24,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer'; +import { CharCode } from 'vs/base/common/charCode'; registerThemingParticipant((theme, collector) => { @@ -38,10 +39,6 @@ registerThemingParticipant((theme, collector) => { export class OneSnippet { - private readonly _editor: IActiveCodeEditor; - private readonly _snippet: TextmateSnippet; - private readonly _offset: number; - private _placeholderDecorations?: Map; private _placeholderGroups: Placeholder[][]; _placeholderGroupsIdx: number; @@ -54,12 +51,11 @@ export class OneSnippet { inactiveFinal: ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'finish-snippet-placeholder' }), }; - constructor(editor: IActiveCodeEditor, snippet: TextmateSnippet, offset: number) { - this._editor = editor; - this._snippet = snippet; - this._offset = offset; - - this._placeholderGroups = groupBy(snippet.placeholders, Placeholder.compareByIndex); + constructor( + private readonly _editor: IActiveCodeEditor, private readonly _snippet: TextmateSnippet, + private readonly _offset: number, private readonly _snippetLineLeadingWhitespace: string + ) { + this._placeholderGroups = groupBy(_snippet.placeholders, Placeholder.compareByIndex); this._placeholderGroupsIdx = -1; } @@ -113,8 +109,12 @@ export class OneSnippet { const id = this._placeholderDecorations!.get(placeholder)!; const range = this._editor.getModel().getDecorationRange(id)!; const currentValue = this._editor.getModel().getValueInRange(range); - - operations.push(EditOperation.replaceMove(range, placeholder.transform.resolve(currentValue))); + const transformedValueLines = placeholder.transform.resolve(currentValue).split(/\r\n|\r|\n/); + // fix indentation for transformed lines + for (let i = 1; i < transformedValueLines.length; i++) { + transformedValueLines[i] = this._editor.getModel().normalizeIndentation(this._snippetLineLeadingWhitespace + transformedValueLines[i]); + } + operations.push(EditOperation.replace(range, transformedValueLines.join(this._editor.getModel().getEOL()))); } } if (operations.length > 0) { @@ -300,7 +300,7 @@ export class OneSnippet { }); } - public getEnclosingRange(): Range | undefined { + getEnclosingRange(): Range | undefined { let result: Range | undefined; const model = this._editor.getModel(); for (const decorationId of this._placeholderDecorations!.values()) { @@ -333,32 +333,53 @@ const _defaultOptions: ISnippetSessionInsertOptions = { export class SnippetSession { - static adjustWhitespace(model: ITextModel, position: IPosition, snippet: TextmateSnippet, adjustIndentation: boolean, adjustNewlines: boolean): void { + static adjustWhitespace(model: ITextModel, position: IPosition, snippet: TextmateSnippet, adjustIndentation: boolean, adjustNewlines: boolean): string { const line = model.getLineContent(position.lineNumber); const lineLeadingWhitespace = getLeadingWhitespace(line, 0, position.column - 1); + // the snippet as inserted + let snippetTextString: string | undefined; + snippet.walk(marker => { - if (marker instanceof Text && !(marker.parent instanceof Choice)) { - // adjust indentation of text markers, except for choise elements - // which get adjusted when being selected - const lines = marker.value.split(/\r\n|\r|\n/); + // all text elements that are not inside choice + if (!(marker instanceof Text) || marker.parent instanceof Choice) { + return true; + } - if (adjustIndentation) { - for (let i = 1; i < lines.length; i++) { - let templateLeadingWhitespace = getLeadingWhitespace(lines[i]); - lines[i] = model.normalizeIndentation(lineLeadingWhitespace + templateLeadingWhitespace) + lines[i].substr(templateLeadingWhitespace.length); + const lines = marker.value.split(/\r\n|\r|\n/); + + if (adjustIndentation) { + // adjust indentation of snippet test + // -the snippet-start doesn't get extra-indented (lineLeadingWhitespace), only normalized + // -all N+1 lines get extra-indented and normalized + // -the text start get extra-indented and normalized when following a linebreak + const offset = snippet.offset(marker); + if (offset === 0) { + // snippet start + lines[0] = model.normalizeIndentation(lines[0]); + + } else { + // check if text start is after a linebreak + snippetTextString = snippetTextString ?? snippet.toString(); + let prevChar = snippetTextString.charCodeAt(offset - 1); + if (prevChar === CharCode.LineFeed || prevChar === CharCode.CarriageReturn) { + lines[0] = model.normalizeIndentation(lineLeadingWhitespace + lines[0]); } } - - if (adjustNewlines) { - const newValue = lines.join(model.getEOL()); - if (newValue !== marker.value) { - marker.parent.replace(marker, [new Text(newValue)]); - } + for (let i = 1; i < lines.length; i++) { + lines[i] = model.normalizeIndentation(lineLeadingWhitespace + lines[i]); } } + + const newValue = lines.join(model.getEOL()); + if (newValue !== marker.value) { + marker.parent.replace(marker, [new Text(newValue)]); + snippetTextString = undefined; + } return true; }); + + return lineLeadingWhitespace; } static adjustSelection(model: ITextModel, selection: Selection, overwriteBefore: number, overwriteAfter: number): Selection { @@ -443,7 +464,7 @@ export class SnippetSession { // happens when being asked for (default) or when this is a secondary // cursor and the leading whitespace is different const start = snippetSelection.getStartPosition(); - SnippetSession.adjustWhitespace( + const snippetLineLeadingWhitespace = SnippetSession.adjustWhitespace( model, start, snippet, adjustWhitespace || (idx > 0 && firstLineFirstNonWhitespace !== model.getLineFirstNonWhitespaceColumn(selection.positionLineNumber)), true @@ -467,7 +488,7 @@ export class SnippetSession { // the one with lowest start position edits[idx] = EditOperation.replace(snippetSelection, snippet.toString()); edits[idx].identifier = { major: idx, minor: 0 }; // mark the edit so only our undo edits will be used to generate end cursors - snippets[idx] = new OneSnippet(editor, snippet, offset); + snippets[idx] = new OneSnippet(editor, snippet, offset, snippetLineLeadingWhitespace); } return { edits, snippets }; diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index cd5d505b..bdfe96e7 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -10,7 +10,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { Selection } from 'vs/editor/common/core/selection'; import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snippetParser'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, splitLines } from 'vs/base/common/strings'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -111,7 +111,7 @@ export class SelectionBasedVariableResolver implements VariableResolver { return false; } if (marker instanceof Text) { - varLeadingWhitespace = getLeadingWhitespace(marker.value.split(/\r\n|\r|\n/).pop()!); + varLeadingWhitespace = getLeadingWhitespace(splitLines(marker.value).pop()!); } return true; }); diff --git a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts index c430c263..7ec29476 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts @@ -441,11 +441,18 @@ suite('SnippetController2', function () { }); test('leading TAB by snippets won\'t replace by spaces #101870', function () { - this.skip(); const ctrl = new SnippetController2(editor, logService, contextKeys); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('\tHello World\n\tNew Line'); assert.strictEqual(model.getValue(), ' Hello World\n New Line'); }); + + test('leading TAB by snippets won\'t replace by spaces #101870 (part 2)', function () { + const ctrl = new SnippetController2(editor, logService, contextKeys); + model.setValue(''); + model.updateOptions({ insertSpaces: true, tabSize: 4 }); + ctrl.insert('\tHello World\n\tNew Line\n${1:\tmore}'); + assert.strictEqual(model.getValue(), ' Hello World\n New Line\n more'); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index f72727cc..458555cb 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -561,6 +561,36 @@ suite('SnippetSession', function () { assertSelections(editor, new Selection(2, 1, 2, 1)); }); + test('Snippet tab stop selection issue #96545, snippets, transform adjacent to previous placeholder', function () { + editor.getModel()!.setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, '${1:{}${2:fff}${1/{/}/}'); + session.insert(); + + assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6)); + session.next(); + + assert.equal(model.getValue(), '{fff}'); + assertSelections(editor, new Selection(1, 2, 1, 5)); + editor.trigger('test', 'type', { text: 'ggg' }); + session.next(); + + assert.equal(model.getValue(), '{ggg}'); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 6, 1, 6)); + }); + + test('Snippet tab stop selection issue #96545', function () { + editor.getModel().setValue(''); + const session = new SnippetSession(editor, '${1:{}${2:fff}${1/[\\{]/}/}$0'); + session.insert(); + assert.equal(editor.getModel().getValue(), '{fff{'); + + assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6)); + session.next(); + assertSelections(editor, new Selection(1, 2, 1, 5)); + }); + test('Snippet placeholder index incorrect after using 2+ snippets in a row that each end with a placeholder, #30769', function () { editor.getModel()!.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -652,4 +682,52 @@ suite('SnippetSession', function () { new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: undefined }).insert(); assert.equal(model.getValue(), 'console.far'); }); + + test('Tabs don\'t get replaced with spaces in snippet transformations #103818', function () { + const model = editor.getModel()!; + model.setValue('\n{\n \n}'); + model.updateOptions({ insertSpaces: true, tabSize: 2 }); + editor.setSelections([new Selection(1, 1, 1, 1), new Selection(3, 6, 3, 6)]); + const session = new SnippetSession(editor, [ + 'function animate () {', + '\tvar ${1:a} = 12;', + '\tconsole.log(${1/(.*)/\n\t\t$1\n\t/})', + '}' + ].join('\n')); + + session.insert(); + + assert.strictEqual(model.getValue(), [ + 'function animate () {', + ' var a = 12;', + ' console.log(a)', + '}', + '{', + ' function animate () {', + ' var a = 12;', + ' console.log(a)', + ' }', + '}', + ].join('\n')); + + editor.trigger('test', 'type', { text: 'bbb' }); + session.next(); + + assert.strictEqual(model.getValue(), [ + 'function animate () {', + ' var bbb = 12;', + ' console.log(', + ' bbb', + ' )', + '}', + '{', + ' function animate () {', + ' var bbb = 12;', + ' console.log(', + ' bbb', + ' )', + ' }', + '}', + ].join('\n')); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 0bb4cfe3..8e939a5a 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -9,10 +9,11 @@ import { Selection } from 'vs/editor/common/core/selection'; import { SelectionBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, ClipboardBasedVariableResolver, TimeBasedVariableResolver, WorkspaceBasedVariableResolver } from 'vs/editor/contrib/snippet/snippetVariables'; import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/snippet/snippetParser'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; import { mock } from 'vs/base/test/common/mock'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; suite('Snippet Variables Resolver', function () { diff --git a/src/vs/editor/contrib/suggest/resizable.ts b/src/vs/editor/contrib/suggest/resizable.ts index 25e1dcb3..382f8828 100644 --- a/src/vs/editor/contrib/suggest/resizable.ts +++ b/src/vs/editor/contrib/suggest/resizable.ts @@ -6,7 +6,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Dimension } from 'vs/base/browser/dom'; -import { Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; +import { Orientation, OrthogonalEdge, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; export interface IResizeEvent { @@ -43,8 +43,8 @@ export class ResizableHTMLElement { this.domNode = document.createElement('div'); this._eastSash = new Sash(this.domNode, { getVerticalSashLeft: () => this._size.width }, { orientation: Orientation.VERTICAL }); this._westSash = new Sash(this.domNode, { getVerticalSashLeft: () => 0 }, { orientation: Orientation.VERTICAL }); - this._northSash = new Sash(this.domNode, { getHorizontalSashTop: () => 0 }, { orientation: Orientation.HORIZONTAL }); - this._southSash = new Sash(this.domNode, { getHorizontalSashTop: () => this._size.height }, { orientation: Orientation.HORIZONTAL }); + this._northSash = new Sash(this.domNode, { getHorizontalSashTop: () => 0 }, { orientation: Orientation.HORIZONTAL, orthogonalEdge: OrthogonalEdge.North }); + this._southSash = new Sash(this.domNode, { getHorizontalSashTop: () => this._size.height }, { orientation: Orientation.HORIZONTAL, orthogonalEdge: OrthogonalEdge.South }); this._northSash.orthogonalStartSash = this._westSash; this._northSash.orthogonalEndSash = this._eastSash; diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 650701de..d6c466f3 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -6,7 +6,6 @@ import { onUnexpectedExternalError, canceled, isPromiseCanceledError } from 'vs/base/common/errors'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; import * as modes from 'vs/editor/common/modes'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -18,6 +17,10 @@ import { isDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifec import { MenuId } from 'vs/platform/actions/common/actions'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { StopWatch } from 'vs/base/common/stopwatch'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { assertType } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; export const Context = { Visible: new RawContextKey('suggestWidgetVisible', false), @@ -341,31 +344,42 @@ export function getSuggestionComparator(snippetConfig: SnippetSortOrder): (a: Co return _snippetComparators.get(snippetConfig)!; } -registerDefaultLanguageCommand('_executeCompletionItemProvider', async (model, position, args) => { - - const result: modes.CompletionList = { - incomplete: false, - suggestions: [] - }; - - const resolving: Promise[] = []; - const maxItemsToResolve = args['maxItemsToResolve'] || 0; - - const completions = await provideSuggestionItems(model, position); - for (const item of completions.items) { - if (resolving.length < maxItemsToResolve) { - resolving.push(item.resolve(CancellationToken.None)); - } - result.incomplete = result.incomplete || item.container.incomplete; - result.suggestions.push(item.completion); - } +CommandsRegistry.registerCommand('_executeCompletionItemProvider', async (accessor, ...args: [URI, IPosition, string?, number?]) => { + const [uri, position, triggerCharacter, maxItemsToResolve] = args; + assertType(URI.isUri(uri)); + assertType(Position.isIPosition(position)); + assertType(typeof triggerCharacter === 'string' || !triggerCharacter); + assertType(typeof maxItemsToResolve === 'number' || !maxItemsToResolve); + const ref = await accessor.get(ITextModelService).createModelReference(uri); try { - await Promise.all(resolving); - return result; + + const result: modes.CompletionList = { + incomplete: false, + suggestions: [] + }; + + const resolving: Promise[] = []; + const completions = await provideSuggestionItems(ref.object.textEditorModel, Position.lift(position), undefined, { triggerCharacter, triggerKind: triggerCharacter ? modes.CompletionTriggerKind.TriggerCharacter : modes.CompletionTriggerKind.Invoke }); + for (const item of completions.items) { + if (resolving.length < (maxItemsToResolve ?? 0)) { + resolving.push(item.resolve(CancellationToken.None)); + } + result.incomplete = result.incomplete || item.container.incomplete; + result.suggestions.push(item.completion); + } + + try { + await Promise.all(resolving); + return result; + } finally { + setTimeout(() => completions.disposable.dispose(), 100); + } + } finally { - setTimeout(() => completions.disposable.dispose(), 100); + ref.dispose(); } + }); interface SuggestController extends IEditorContribution { diff --git a/src/vs/editor/contrib/suggest/suggestMemory.ts b/src/vs/editor/contrib/suggest/suggestMemory.ts index b2c98b60..dfb29363 100644 --- a/src/vs/editor/contrib/suggest/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/suggestMemory.ts @@ -5,7 +5,7 @@ import { LRUCache, TernarySearchTree } from 'vs/base/common/map'; -import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ITextModel } from 'vs/editor/common/model'; import { IPosition } from 'vs/editor/common/core/position'; import { CompletionItemKind, completionKindFromString } from 'vs/editor/common/modes'; @@ -82,8 +82,7 @@ export class LRUMemory extends Memory { private _seq = 0; memorize(model: ITextModel, pos: IPosition, item: CompletionItem): void { - const { label } = item.completion; - const key = `${model.getLanguageIdentifier().language}/${label}`; + const key = `${model.getLanguageIdentifier().language}/${item.textLabel}`; this._cache.set(key, { touch: this._seq++, type: item.completion.kind, @@ -111,7 +110,7 @@ export class LRUMemory extends Memory { // consider only top items break; } - const key = `${model.getLanguageIdentifier().language}/${items[i].completion.label}`; + const key = `${model.getLanguageIdentifier().language}/${items[i].textLabel}`; const item = this._cache.peek(key); if (item && item.touch > seq && item.type === items[i].completion.kind && item.insertText === items[i].completion.insertText) { seq = item.touch; @@ -295,7 +294,7 @@ export class SuggestMemoryService implements ISuggestMemoryService { const share = this._configService.getValue('editor.suggest.shareSuggestSelections'); const scope = share ? StorageScope.GLOBAL : StorageScope.WORKSPACE; const raw = JSON.stringify(this._strategy); - this._storageService.store(`${SuggestMemoryService._storagePrefix}/${this._strategy.name}`, raw, scope); + this._storageService.store(`${SuggestMemoryService._storagePrefix}/${this._strategy.name}`, raw, scope, StorageTarget.MACHINE); } } } diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 6cad7c8b..9a88e1cc 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -20,7 +20,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { isLowSurrogate, isHighSurrogate } from 'vs/base/common/strings'; +import { isLowSurrogate, isHighSurrogate, getLeadingWhitespace } from 'vs/base/common/strings'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ILogService } from 'vs/platform/log/common/log'; @@ -486,8 +486,14 @@ export class SuggestModel implements IDisposable { }).catch(onUnexpectedError); } + private _telemetryGate: number = 0; + private _reportDurationsTelemetry(durations: CompletionDurations): void { + if (this._telemetryGate++ % 230 !== 0) { + return; + } + setTimeout(() => { type Durations = { data: string; }; type DurationsClassification = { data: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' } }; @@ -553,7 +559,8 @@ export class SuggestModel implements IDisposable { return; } - if (ctx.leadingWord.startColumn < this._context.leadingWord.startColumn) { + if (getLeadingWhitespace(ctx.leadingLineContent) !== getLeadingWhitespace(this._context.leadingLineContent)) { + // cancel IntelliSense when line start changes // happens when the current word gets outdented this.cancel(); return; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index a62c3c68..02f6233f 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -11,7 +11,7 @@ import * as strings from 'vs/base/common/strings'; import * as dom from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IListEvent, IListMouseEvent, IListGestureEvent } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -22,7 +22,7 @@ import { CompletionModel } from './completionModel'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -32,6 +32,7 @@ import { getAriaId, ItemRenderer } from './suggestWidgetRenderer'; import { ResizableHTMLElement } from './resizable'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IPosition } from 'vs/editor/common/core/position'; +import { clamp } from 'vs/base/common/numbers'; /** * Suggest widget colors @@ -82,7 +83,7 @@ class PersistedWidgetSize { } store(size: dom.Dimension) { - this._service.store(this._key, JSON.stringify(size), StorageScope.GLOBAL); + this._service.store(this._key, JSON.stringify(size), StorageScope.GLOBAL, StorageTarget.MACHINE); } reset(): void { @@ -95,49 +96,48 @@ export class SuggestWidget implements IDisposable { private static LOADING_MESSAGE: string = nls.localize('suggestWidget.loading', "Loading..."); private static NO_SUGGESTIONS_MESSAGE: string = nls.localize('suggestWidget.noSuggestions', "No suggestions."); - private state: State = State.Hidden; - private isAuto: boolean = false; - private loadingTimeout: IDisposable = Disposable.None; - private currentSuggestionDetails?: CancelablePromise; - private focusedItem?: CompletionItem; - private ignoreFocusEvents: boolean = false; - private completionModel?: CompletionModel; + private _state: State = State.Hidden; + private _isAuto: boolean = false; + private _loadingTimeout?: IDisposable; + private _currentSuggestionDetails?: CancelablePromise; + private _focusedItem?: CompletionItem; + private _ignoreFocusEvents: boolean = false; + private _completionModel?: CompletionModel; private _cappedHeight?: { wanted: number, capped: number }; + private _explainMode: boolean = false; readonly element: ResizableHTMLElement; - private readonly messageElement: HTMLElement; - private readonly listElement: HTMLElement; - private readonly list: List; - private readonly status: SuggestWidgetStatus; + private readonly _messageElement: HTMLElement; + private readonly _listElement: HTMLElement; + private readonly _list: List; + private readonly _status: SuggestWidgetStatus; private readonly _details: SuggestDetailsOverlay; private readonly _contentWidget: SuggestContentWidget; - - private readonly ctxSuggestWidgetVisible: IContextKey; - private readonly ctxSuggestWidgetDetailsVisible: IContextKey; - private readonly ctxSuggestWidgetMultipleSuggestions: IContextKey; - - private readonly showTimeout = new TimeoutTimer(); - private readonly _disposables = new DisposableStore(); - private readonly _persistedSize: PersistedWidgetSize; - private readonly onDidSelectEmitter = new Emitter(); - private readonly onDidFocusEmitter = new Emitter(); - private readonly onDidHideEmitter = new Emitter(); - private readonly onDidShowEmitter = new Emitter(); + private readonly _ctxSuggestWidgetVisible: IContextKey; + private readonly _ctxSuggestWidgetDetailsVisible: IContextKey; + private readonly _ctxSuggestWidgetMultipleSuggestions: IContextKey; - readonly onDidSelect: Event = this.onDidSelectEmitter.event; - readonly onDidFocus: Event = this.onDidFocusEmitter.event; - readonly onDidHide: Event = this.onDidHideEmitter.event; - readonly onDidShow: Event = this.onDidShowEmitter.event; + private readonly _showTimeout = new TimeoutTimer(); + private readonly _disposables = new DisposableStore(); - private detailsFocusBorderColor?: string; - private detailsBorderColor?: string; - private explainMode: boolean = false; + private readonly _onDidSelect = new Emitter(); + private readonly _onDidFocus = new Emitter(); + private readonly _onDidHide = new Emitter(); + private readonly _onDidShow = new Emitter(); + + readonly onDidSelect: Event = this._onDidSelect.event; + readonly onDidFocus: Event = this._onDidFocus.event; + readonly onDidHide: Event = this._onDidHide.event; + readonly onDidShow: Event = this._onDidShow.event; private readonly _onDetailsKeydown = new Emitter(); - public readonly onDetailsKeyDown: Event = this._onDetailsKeydown.event; + readonly onDetailsKeyDown: Event = this._onDetailsKeydown.event; + + private _detailsFocusBorderColor?: string; + private _detailsBorderColor?: string; constructor( private readonly editor: ICodeEditor, @@ -152,47 +152,55 @@ export class SuggestWidget implements IDisposable { this._contentWidget = new SuggestContentWidget(this, editor); this._persistedSize = new PersistedWidgetSize(_storageService, editor); - let persistedSize: dom.Dimension | undefined; - let currentSize: dom.Dimension | undefined; - let persistHeight = false; - let persistWidth = false; + class ResizeState { + constructor( + readonly persistedSize: dom.Dimension | undefined, + readonly currentSize: dom.Dimension, + public persistHeight = false, + public persistWidth = false, + ) { } + } + + let state: ResizeState | undefined; this._disposables.add(this.element.onDidWillResize(() => { this._contentWidget.lockPreference(); - persistedSize = this._persistedSize.restore(); - currentSize = this.element.size; + state = new ResizeState(this._persistedSize.restore(), this.element.size); })); this._disposables.add(this.element.onDidResize(e => { this._resize(e.dimension.width, e.dimension.height); - persistHeight = persistHeight || !!e.north || !!e.south; - persistWidth = persistWidth || !!e.east || !!e.west; - if (e.done) { + if (state) { + state.persistHeight = state.persistHeight || !!e.north || !!e.south; + state.persistWidth = state.persistWidth || !!e.east || !!e.west; + } + + if (!e.done) { + return; + } + + if (state) { // only store width or height value that have changed and also // only store changes that are above a certain threshold - const threshold = Math.floor(this.getLayoutInfo().itemHeight / 2); + const { itemHeight, defaultSize } = this.getLayoutInfo(); + const threshold = Math.round(itemHeight / 2); let { width, height } = this.element.size; - if (persistedSize && currentSize) { - if (!persistHeight || Math.abs(currentSize.height - height) <= threshold) { - height = persistedSize.height; - } - if (!persistWidth || Math.abs(currentSize.width - width) <= threshold) { - width = persistedSize.width; - } + if (!state.persistHeight || Math.abs(state.currentSize.height - height) <= threshold) { + height = state.persistedSize?.height ?? defaultSize.height; + } + if (!state.persistWidth || Math.abs(state.currentSize.width - width) <= threshold) { + width = state.persistedSize?.width ?? defaultSize.width; } this._persistedSize.store(new dom.Dimension(width, height)); - - // reset working state - this._contentWidget.unlockPreference(); - persistedSize = undefined; - currentSize = undefined; - persistHeight = false; - persistWidth = false; } + + // reset working state + this._contentWidget.unlockPreference(); + state = undefined; })); - this.messageElement = dom.append(this.element.domNode, dom.$('.message')); - this.listElement = dom.append(this.element.domNode, dom.$('.tree')); + this._messageElement = dom.append(this.element.domNode, dom.$('.message')); + this._listElement = dom.append(this.element.domNode, dom.$('.tree')); const details = instantiationService.createInstance(SuggestDetailsWidget, this.editor); details.onDidClose(this.toggleDetails, this, this._disposables); @@ -205,7 +213,7 @@ export class SuggestWidget implements IDisposable { this._disposables.add(renderer); this._disposables.add(renderer.onDidToggleDetails(() => this.toggleDetails())); - this.list = new List('SuggestWidget', this.listElement, { + this._list = new List('SuggestWidget', this._listElement, { getHeight: (_element: CompletionItem): number => this.getLayoutInfo().itemHeight, getTemplateId: (_element: CompletionItem): string => 'suggestion' }, [renderer], { @@ -232,22 +240,22 @@ export class SuggestWidget implements IDisposable { } }); - this.status = instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode); + this._status = instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode); const applyStatusBarStyle = () => this.element.domNode.classList.toggle('with-status-bar', this.editor.getOption(EditorOption.suggest).showStatusBar); applyStatusBarStyle(); - this._disposables.add(attachListStyler(this.list, _themeService, { + this._disposables.add(attachListStyler(this._list, _themeService, { listInactiveFocusBackground: editorSuggestWidgetSelectedBackground, listInactiveFocusOutline: activeContrastBorder })); - this._disposables.add(_themeService.onDidColorThemeChange(t => this.onThemeChange(t))); - this.onThemeChange(_themeService.getColorTheme()); + this._disposables.add(_themeService.onDidColorThemeChange(t => this._onThemeChange(t))); + this._onThemeChange(_themeService.getColorTheme()); - this._disposables.add(this.list.onMouseDown(e => this.onListMouseDownOrTap(e))); - this._disposables.add(this.list.onTap(e => this.onListMouseDownOrTap(e))); - this._disposables.add(this.list.onDidChangeSelection(e => this.onListSelection(e))); - this._disposables.add(this.list.onDidChangeFocus(e => this.onListFocus(e))); - this._disposables.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged())); + this._disposables.add(this._list.onMouseDown(e => this._onListMouseDownOrTap(e))); + this._disposables.add(this._list.onTap(e => this._onListMouseDownOrTap(e))); + this._disposables.add(this._list.onDidChangeSelection(e => this._onListSelection(e))); + this._disposables.add(this._list.onDidChangeFocus(e => this._onListFocus(e))); + this._disposables.add(this.editor.onDidChangeCursorSelection(() => this._onCursorSelectionChanged())); this._disposables.add(this.editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.suggest)) { applyStatusBarStyle(); @@ -255,31 +263,31 @@ export class SuggestWidget implements IDisposable { } })); - this.ctxSuggestWidgetVisible = SuggestContext.Visible.bindTo(_contextKeyService); - this.ctxSuggestWidgetDetailsVisible = SuggestContext.DetailsVisible.bindTo(_contextKeyService); - this.ctxSuggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(_contextKeyService); + this._ctxSuggestWidgetVisible = SuggestContext.Visible.bindTo(_contextKeyService); + this._ctxSuggestWidgetDetailsVisible = SuggestContext.DetailsVisible.bindTo(_contextKeyService); + this._ctxSuggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(_contextKeyService); this._disposables.add(dom.addStandardDisposableListener(this._details.widget.domNode, 'keydown', e => { this._onDetailsKeydown.fire(e); })); - this._disposables.add(this.editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(e))); + this._disposables.add(this.editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); } dispose(): void { this._details.widget.dispose(); this._details.dispose(); - this.list.dispose(); - this.status.dispose(); + this._list.dispose(); + this._status.dispose(); this._disposables.dispose(); - this.loadingTimeout.dispose(); - this.showTimeout.dispose(); + this._loadingTimeout?.dispose(); + this._showTimeout.dispose(); this._contentWidget.dispose(); this.element.dispose(); } - private onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { + private _onEditorMouseDown(mouseEvent: IEditorMouseEvent): void { if (this._details.widget.domNode.contains(mouseEvent.target.element)) { // Clicking inside details this._details.widget.domNode.focus(); @@ -291,13 +299,13 @@ export class SuggestWidget implements IDisposable { } } - private onCursorSelectionChanged(): void { - if (this.state !== State.Hidden) { + private _onCursorSelectionChanged(): void { + if (this._state !== State.Hidden) { this._contentWidget.layout(); } } - private onListMouseDownOrTap(e: IListMouseEvent | IListGestureEvent): void { + private _onListMouseDownOrTap(e: IListMouseEvent | IListGestureEvent): void { if (typeof e.element === 'undefined' || typeof e.index === 'undefined') { return; } @@ -306,83 +314,78 @@ export class SuggestWidget implements IDisposable { e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); - this.select(e.element, e.index); + this._select(e.element, e.index); } - private onListSelection(e: IListEvent): void { - if (!e.elements.length) { - return; + private _onListSelection(e: IListEvent): void { + if (e.elements.length) { + this._select(e.elements[0], e.indexes[0]); } - - this.select(e.elements[0], e.indexes[0]); } - private select(item: CompletionItem, index: number): void { - const completionModel = this.completionModel; - - if (!completionModel) { - return; + private _select(item: CompletionItem, index: number): void { + const completionModel = this._completionModel; + if (completionModel) { + this._onDidSelect.fire({ item, index, model: completionModel }); + this.editor.focus(); } - - this.onDidSelectEmitter.fire({ item, index, model: completionModel }); - this.editor.focus(); } - private onThemeChange(theme: IColorTheme) { + private _onThemeChange(theme: IColorTheme) { const backgroundColor = theme.getColor(editorSuggestWidgetBackground); if (backgroundColor) { this.element.domNode.style.backgroundColor = backgroundColor.toString(); - this.messageElement.style.backgroundColor = backgroundColor.toString(); + this._messageElement.style.backgroundColor = backgroundColor.toString(); this._details.widget.domNode.style.backgroundColor = backgroundColor.toString(); } const borderColor = theme.getColor(editorSuggestWidgetBorder); if (borderColor) { this.element.domNode.style.borderColor = borderColor.toString(); - this.messageElement.style.borderColor = borderColor.toString(); - this.status.element.style.borderTopColor = borderColor.toString(); + this._messageElement.style.borderColor = borderColor.toString(); + this._status.element.style.borderTopColor = borderColor.toString(); this._details.widget.domNode.style.borderColor = borderColor.toString(); - this.detailsBorderColor = borderColor.toString(); + this._detailsBorderColor = borderColor.toString(); } const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { - this.detailsFocusBorderColor = focusBorderColor.toString(); + this._detailsFocusBorderColor = focusBorderColor.toString(); } this._details.widget.borderWidth = theme.type === 'hc' ? 2 : 1; } - private onListFocus(e: IListEvent): void { - if (this.ignoreFocusEvents) { + private _onListFocus(e: IListEvent): void { + if (this._ignoreFocusEvents) { return; } if (!e.elements.length) { - if (this.currentSuggestionDetails) { - this.currentSuggestionDetails.cancel(); - this.currentSuggestionDetails = undefined; - this.focusedItem = undefined; + if (this._currentSuggestionDetails) { + this._currentSuggestionDetails.cancel(); + this._currentSuggestionDetails = undefined; + this._focusedItem = undefined; } this.editor.setAriaOptions({ activeDescendant: undefined }); return; } - if (!this.completionModel) { + if (!this._completionModel) { return; } const item = e.elements[0]; const index = e.indexes[0]; - if (item !== this.focusedItem) { + if (item !== this._focusedItem) { - this.currentSuggestionDetails?.cancel(); - this.currentSuggestionDetails = undefined; + this._currentSuggestionDetails?.cancel(); + this._currentSuggestionDetails = undefined; - this.focusedItem = item; + this._focusedItem = item; - this.list.reveal(index); + this._list.reveal(index); - this.currentSuggestionDetails = createCancelablePromise(async token => { + this._currentSuggestionDetails = createCancelablePromise(async token => { const loading = disposableTimeout(() => { if (this._isDetailsVisible()) { this.showDetails(true); @@ -394,16 +397,16 @@ export class SuggestWidget implements IDisposable { return result; }); - this.currentSuggestionDetails.then(() => { - if (index >= this.list.length || item !== this.list.element(index)) { + this._currentSuggestionDetails.then(() => { + if (index >= this._list.length || item !== this._list.element(index)) { return; } // item can have extra information, so re-render - this.ignoreFocusEvents = true; - this.list.splice(index, 1, [item]); - this.list.setFocus([index]); - this.ignoreFocusEvents = false; + this._ignoreFocusEvents = true; + this._list.splice(index, 1, [item]); + this._list.setFocus([index]); + this._ignoreFocusEvents = false; if (this._isDetailsVisible()) { this.showDetails(false); @@ -416,63 +419,63 @@ export class SuggestWidget implements IDisposable { } // emit an event - this.onDidFocusEmitter.fire({ item, index, model: this.completionModel }); + this._onDidFocus.fire({ item, index, model: this._completionModel }); } private _setState(state: State): void { - if (this.state === state) { + if (this._state === state) { return; } - this.state = state; + this._state = state; this.element.domNode.classList.toggle('frozen', state === State.Frozen); this.element.domNode.classList.remove('message'); switch (state) { case State.Hidden: - dom.hide(this.messageElement, this.listElement, this.status.element); + dom.hide(this._messageElement, this._listElement, this._status.element); this._details.hide(true); this._contentWidget.hide(); - this.ctxSuggestWidgetVisible.reset(); - this.ctxSuggestWidgetMultipleSuggestions.reset(); + this._ctxSuggestWidgetVisible.reset(); + this._ctxSuggestWidgetMultipleSuggestions.reset(); this.element.domNode.classList.remove('visible'); - this.list.splice(0, this.list.length); - this.focusedItem = undefined; + this._list.splice(0, this._list.length); + this._focusedItem = undefined; this._cappedHeight = undefined; - this.explainMode = false; + this._explainMode = false; break; case State.Loading: this.element.domNode.classList.add('message'); - this.messageElement.textContent = SuggestWidget.LOADING_MESSAGE; - dom.hide(this.listElement, this.status.element); - dom.show(this.messageElement); + this._messageElement.textContent = SuggestWidget.LOADING_MESSAGE; + dom.hide(this._listElement, this._status.element); + dom.show(this._messageElement); this._details.hide(); this._show(); - this.focusedItem = undefined; + this._focusedItem = undefined; break; case State.Empty: this.element.domNode.classList.add('message'); - this.messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE; - dom.hide(this.listElement, this.status.element); - dom.show(this.messageElement); + this._messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE; + dom.hide(this._listElement, this._status.element); + dom.show(this._messageElement); this._details.hide(); this._show(); - this.focusedItem = undefined; + this._focusedItem = undefined; break; case State.Open: - dom.hide(this.messageElement); - dom.show(this.listElement, this.status.element); + dom.hide(this._messageElement); + dom.show(this._listElement, this._status.element); this._show(); break; case State.Frozen: - dom.hide(this.messageElement); - dom.show(this.listElement, this.status.element); + dom.hide(this._messageElement); + dom.show(this._listElement, this._status.element); this._show(); break; case State.Details: - dom.hide(this.messageElement); - dom.show(this.listElement, this.status.element); + dom.hide(this._messageElement); + dom.show(this._listElement, this._status.element); this._details.show(); this._show(); break; @@ -482,176 +485,176 @@ export class SuggestWidget implements IDisposable { private _show(): void { this._contentWidget.show(); this._layout(this._persistedSize.restore()); - this.ctxSuggestWidgetVisible.set(true); + this._ctxSuggestWidgetVisible.set(true); - this.showTimeout.cancelAndSet(() => { + this._showTimeout.cancelAndSet(() => { this.element.domNode.classList.add('visible'); - this.onDidShowEmitter.fire(this); + this._onDidShow.fire(this); }, 100); } showTriggered(auto: boolean, delay: number) { - if (this.state !== State.Hidden) { + if (this._state !== State.Hidden) { return; } this._contentWidget.setPosition(this.editor.getPosition()); - this.isAuto = !!auto; + this._isAuto = !!auto; - if (!this.isAuto) { - this.loadingTimeout = disposableTimeout(() => this._setState(State.Loading), delay); + if (!this._isAuto) { + this._loadingTimeout = disposableTimeout(() => this._setState(State.Loading), delay); } } showSuggestions(completionModel: CompletionModel, selectionIndex: number, isFrozen: boolean, isAuto: boolean): void { this._contentWidget.setPosition(this.editor.getPosition()); - this.loadingTimeout.dispose(); + this._loadingTimeout?.dispose(); - this.currentSuggestionDetails?.cancel(); - this.currentSuggestionDetails = undefined; + this._currentSuggestionDetails?.cancel(); + this._currentSuggestionDetails = undefined; - if (this.completionModel !== completionModel) { - this.completionModel = completionModel; + if (this._completionModel !== completionModel) { + this._completionModel = completionModel; } - if (isFrozen && this.state !== State.Empty && this.state !== State.Hidden) { + if (isFrozen && this._state !== State.Empty && this._state !== State.Hidden) { this._setState(State.Frozen); return; } - const visibleCount = this.completionModel.items.length; + const visibleCount = this._completionModel.items.length; const isEmpty = visibleCount === 0; - this.ctxSuggestWidgetMultipleSuggestions.set(visibleCount > 1); + this._ctxSuggestWidgetMultipleSuggestions.set(visibleCount > 1); if (isEmpty) { this._setState(isAuto ? State.Hidden : State.Empty); - this.completionModel = undefined; + this._completionModel = undefined; return; } - this.focusedItem = undefined; - this.list.splice(0, this.list.length, this.completionModel.items); + this._focusedItem = undefined; + this._list.splice(0, this._list.length, this._completionModel.items); this._setState(isFrozen ? State.Frozen : State.Open); - this.list.reveal(selectionIndex, 0); - this.list.setFocus([selectionIndex]); + this._list.reveal(selectionIndex, 0); + this._list.setFocus([selectionIndex]); this._layout(this.element.size); // Reset focus border - if (this.detailsBorderColor) { - this._details.widget.domNode.style.borderColor = this.detailsBorderColor; + if (this._detailsBorderColor) { + this._details.widget.domNode.style.borderColor = this._detailsBorderColor; } } selectNextPage(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.pageDown(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusNextPage(); + this._list.focusNextPage(); return true; } } selectNext(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusNext(1, true); + this._list.focusNext(1, true); return true; } } selectLast(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.scrollBottom(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusLast(); + this._list.focusLast(); return true; } } selectPreviousPage(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.pageUp(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusPreviousPage(); + this._list.focusPreviousPage(); return true; } } selectPrevious(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusPrevious(1, true); + this._list.focusPrevious(1, true); return false; } } selectFirst(): boolean { - switch (this.state) { + switch (this._state) { case State.Hidden: return false; case State.Details: this._details.widget.scrollTop(); return true; case State.Loading: - return !this.isAuto; + return !this._isAuto; default: - this.list.focusFirst(); + this._list.focusFirst(); return true; } } getFocusedItem(): ISelectedSuggestion | undefined { - if (this.state !== State.Hidden - && this.state !== State.Empty - && this.state !== State.Loading - && this.completionModel + if (this._state !== State.Hidden + && this._state !== State.Empty + && this._state !== State.Loading + && this._completionModel ) { return { - item: this.list.getFocusedElements()[0], - index: this.list.getFocus()[0], - model: this.completionModel + item: this._list.getFocusedElements()[0], + index: this._list.getFocus()[0], + model: this._completionModel }; } return undefined; } toggleDetailsFocus(): void { - if (this.state === State.Details) { + if (this._state === State.Details) { this._setState(State.Open); - if (this.detailsBorderColor) { - this._details.widget.domNode.style.borderColor = this.detailsBorderColor; + if (this._detailsBorderColor) { + this._details.widget.domNode.style.borderColor = this._detailsBorderColor; } - } else if (this.state === State.Open && this._isDetailsVisible()) { + } else if (this._state === State.Open && this._isDetailsVisible()) { this._setState(State.Details); - if (this.detailsFocusBorderColor) { - this._details.widget.domNode.style.borderColor = this.detailsFocusBorderColor; + if (this._detailsFocusBorderColor) { + this._details.widget.domNode.style.borderColor = this._detailsFocusBorderColor; } } } @@ -659,14 +662,14 @@ export class SuggestWidget implements IDisposable { toggleDetails(): void { if (this._isDetailsVisible()) { // hide details widget - this.ctxSuggestWidgetDetailsVisible.set(false); + this._ctxSuggestWidgetDetailsVisible.set(false); this._setDetailsVisible(false); this._details.hide(); this.element.domNode.classList.remove('shows-details'); - } else if (canExpandCompletionItem(this.list.getFocusedElements()[0]) && (this.state === State.Open || this.state === State.Details || this.state === State.Frozen)) { + } else if (canExpandCompletionItem(this._list.getFocusedElements()[0]) && (this._state === State.Open || this._state === State.Details || this._state === State.Frozen)) { // show details widget (iff possible) - this.ctxSuggestWidgetDetailsVisible.set(true); + this._ctxSuggestWidgetDetailsVisible.set(true); this._setDetailsVisible(true); this.showDetails(false); } @@ -677,7 +680,7 @@ export class SuggestWidget implements IDisposable { if (loading) { this._details.widget.renderLoading(); } else { - this._details.widget.renderItem(this.list.getFocusedElements()[0], this.explainMode); + this._details.widget.renderItem(this._list.getFocusedElements()[0], this._explainMode); } this._positionDetails(); this.editor.focus(); @@ -685,8 +688,8 @@ export class SuggestWidget implements IDisposable { } toggleExplainMode(): void { - if (this.list.getFocusedElements()[0] && this._isDetailsVisible()) { - this.explainMode = !this.explainMode; + if (this._list.getFocusedElements()[0] && this._isDetailsVisible()) { + this._explainMode = !this._explainMode; this.showDetails(false); } } @@ -696,13 +699,13 @@ export class SuggestWidget implements IDisposable { } hideWidget(): void { - this.loadingTimeout.dispose(); + this._loadingTimeout?.dispose(); this._setState(State.Hidden); - this.onDidHideEmitter.fire(this); + this._onDidHide.fire(this); } isFrozen(): boolean { - return this.state === State.Frozen; + return this._state === State.Frozen; } _afterRender(position: ContentWidgetPositionPreference | null) { @@ -712,7 +715,7 @@ export class SuggestWidget implements IDisposable { } return; } - if (this.state === State.Empty || this.state === State.Loading) { + if (this._state === State.Empty || this._state === State.Loading) { // no special positioning when widget isn't showing list return; } @@ -738,12 +741,12 @@ export class SuggestWidget implements IDisposable { const info = this.getLayoutInfo(); // status bar - this.status.element.style.lineHeight = `${info.itemHeight}px`; + this._status.element.style.lineHeight = `${info.itemHeight}px`; - if (this.state === State.Empty || this.state === State.Loading) { + if (this._state === State.Empty || this._state === State.Loading) { // showing a message only height = info.itemHeight + info.borderHeight; - width = 230; + width = info.defaultSize.width / 2; this.element.enableSashes(false, false, false, false); this.element.minSize = this.element.maxSize = new dom.Dimension(width, height); this._contentWidget.setPreference(ContentWidgetPositionPreference.BELOW); @@ -754,16 +757,16 @@ export class SuggestWidget implements IDisposable { // width math const maxWidth = bodyBox.width - info.borderHeight - 2 * info.horizontalPadding; if (width === undefined) { - width = 430; + width = info.defaultSize.width; } if (width > maxWidth) { width = maxWidth; } - const preferredWidth = this.completionModel ? this.completionModel.stats.pLabelLen * info.typicalHalfwidthCharacterWidth : width; + const preferredWidth = this._completionModel ? this._completionModel.stats.pLabelLen * info.typicalHalfwidthCharacterWidth : width; // height math - const fullHeight = info.statusBarHeight + this.list.contentHeight + info.borderHeight; - const preferredHeight = info.statusBarHeight + 12 * info.itemHeight + info.borderHeight; + const fullHeight = info.statusBarHeight + this._list.contentHeight + info.borderHeight; + const preferredHeight = info.defaultSize.height; const minHeight = info.itemHeight + info.statusBarHeight; const editorBox = dom.getDomNodePagePosition(this.editor.getDomNode()); const cursorBox = this.editor.getScrolledVisiblePosition(this.editor.getPosition()); @@ -819,8 +822,8 @@ export class SuggestWidget implements IDisposable { height = Math.min(maxHeight, height); const { statusBarHeight } = this.getLayoutInfo(); - this.list.layout(height - statusBarHeight, width); - this.listElement.style.height = `${height - statusBarHeight}px`; + this._list.layout(height - statusBarHeight, width); + this._listElement.style.height = `${height - statusBarHeight}px`; this.element.layout(height, width); this._contentWidget.layout(); @@ -835,8 +838,8 @@ export class SuggestWidget implements IDisposable { getLayoutInfo() { const fontInfo = this.editor.getOption(EditorOption.fontInfo); - const itemHeight = this.editor.getOption(EditorOption.suggestLineHeight) || fontInfo.lineHeight; - const statusBarHeight = !this.editor.getOption(EditorOption.suggest).showStatusBar || this.state === State.Empty || this.state === State.Loading ? 0 : itemHeight; + const itemHeight = clamp(this.editor.getOption(EditorOption.suggestLineHeight) || fontInfo.lineHeight, 8, 1000); + const statusBarHeight = !this.editor.getOption(EditorOption.suggest).showStatusBar || this._state === State.Empty || this._state === State.Loading ? 0 : itemHeight; const borderWidth = this._details.widget.borderWidth; const borderHeight = 2 * borderWidth; @@ -847,7 +850,8 @@ export class SuggestWidget implements IDisposable { borderHeight, typicalHalfwidthCharacterWidth: fontInfo.typicalHalfwidthCharacterWidth, verticalPadding: 22, - horizontalPadding: 14 + horizontalPadding: 14, + defaultSize: new dom.Dimension(430, statusBarHeight + 12 * itemHeight + borderHeight) }; } @@ -856,7 +860,7 @@ export class SuggestWidget implements IDisposable { } private _setDetailsVisible(value: boolean) { - this._storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL); + this._storageService.store('expandSuggestionDocs', value, StorageScope.GLOBAL, StorageTarget.USER); } } diff --git a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts index 72500145..9eed3c99 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts @@ -91,6 +91,7 @@ export class SuggestDetailsWidget { const lineHeightPx = `${lineHeight}px`; this.domNode.style.fontSize = fontSizePx; + this.domNode.style.lineHeight = lineHeightPx; this.domNode.style.fontWeight = fontWeight; this.domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings; this._type.style.fontFamily = fontFamily; @@ -127,9 +128,10 @@ export class SuggestDetailsWidget { if (explainMode) { let md = ''; - md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || item.completion.label}' with '${item.word}'` : ' (no prefix)'}\n`; + md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name}' with '${item.word}'` : ' (no prefix)'}\n`; md += `distance: ${item.distance}, see localityBonus-setting\n`; md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`; + md += `commit characters: ${item.completion.commitCharacters}\n`; documentation = new MarkdownString().appendCodeblock('empty', md); detail = `Provider: ${item.provider._debugDisplayName}`; } @@ -168,7 +170,7 @@ export class SuggestDetailsWidget { const renderedContents = this._markdownRenderer.render(documentation); this._docs.appendChild(renderedContents.element); this._renderDisposeable.add(renderedContents); - this._renderDisposeable.add(this._markdownRenderer.onDidRenderCodeBlock(() => { + this._renderDisposeable.add(this._markdownRenderer.onDidRenderAsync(() => { this.layout(this._size.width, this._type.clientHeight + this._docs.clientHeight); this._onDidChangeContents.fire(this); })); diff --git a/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts b/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts index 66e6e51c..26cdc77a 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetRenderer.ts @@ -11,7 +11,7 @@ import { IListRenderer } from 'vs/base/browser/ui/list/list'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CompletionItem } from './suggest'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { CompletionItemKind, completionKindToCssClass, CompletionItemTag } from 'vs/editor/common/modes'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -21,32 +21,40 @@ import { URI } from 'vs/base/common/uri'; import { FileKind } from 'vs/platform/files/common/files'; import { flatten } from 'vs/base/common/arrays'; import { canExpandCompletionItem } from './suggestWidgetDetails'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export function getAriaId(index: number): string { return `suggest-aria-id:${index}`; } -export const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight); +export const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight, nls.localize('suggestMoreInfoIcon', 'Icon for more information in the suggest widget.')); -const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; +const _completionItemColor = new class ColorExtractor { -function extractColor(item: CompletionItem, out: string[]): boolean { - const label = typeof item.completion.label === 'string' - ? item.completion.label - : item.completion.label.name; + private static _regexRelaxed = /(#([\da-fA-F]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))/; + private static _regexStrict = new RegExp(`^${ColorExtractor._regexRelaxed.source}$`, 'i'); - if (label.match(colorRegExp)) { - out[0] = label; - return true; + extract(item: CompletionItem, out: string[]): boolean { + if (item.textLabel.match(ColorExtractor._regexStrict)) { + out[0] = item.textLabel; + return true; + } + if (item.completion.detail && item.completion.detail.match(ColorExtractor._regexStrict)) { + out[0] = item.completion.detail; + return true; + } + if (typeof item.completion.documentation === 'string') { + const match = ColorExtractor._regexRelaxed.exec(item.completion.documentation); + if (match && (match.index === 0 || match.index + match[0].length === item.completion.documentation.length)) { + out[0] = match[0]; + return true; + } + } + return false; } - if (typeof item.completion.documentation === 'string' && item.completion.documentation.match(colorRegExp)) { - out[0] = item.completion.documentation; - return true; - } - return false; -} +}; export interface ISuggestionTemplateData { @@ -116,7 +124,7 @@ export class ItemRenderer implements IListRenderer { @@ -165,7 +173,7 @@ export class ItemRenderer implements IListRenderer { const _deleteWordRight = new DeleteWordRight(); const _deleteWordStartRight = new DeleteWordStartRight(); const _deleteWordEndRight = new DeleteWordEndRight(); + const _deleteInsideWord = new DeleteInsideWord(); function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void { command.runEditorCommand(null, editor, null); @@ -88,6 +89,9 @@ suite('WordOperations', () => { function deleteWordEndRight(editor: ICodeEditor): void { runEditorCommand(editor, _deleteWordEndRight); } + function deleteInsideWord(editor: ICodeEditor): void { + _deleteInsideWord.run(null!, editor, null); + } test('cursorWordLeft - simple', () => { const EXPECTED = [ @@ -750,4 +754,119 @@ suite('WordOperations', () => { model.dispose(); mode.dispose(); }); + + test('deleteInsideWord - empty line', () => { + withTestCodeEditor([ + 'Line1', + '', + 'Line2' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(2, 1)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Line1\nLine2'); + }); + }); + + test('deleteInsideWord - in whitespace 1', () => { + withTestCodeEditor([ + 'Just some text.' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 6)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Justsome text.'); + }); + }); + + test('deleteInsideWord - in whitespace 2', () => { + withTestCodeEditor([ + 'Just some text.' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 6)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Justsome text.'); + }); + }); + + test('deleteInsideWord - in whitespace 3', () => { + withTestCodeEditor([ + 'Just "some text.' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 6)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'Just"some text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), '"some text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'some text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'text.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), '.'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); + + test('deleteInsideWord - in non-words', () => { + withTestCodeEditor([ + 'x=3+4+5+6' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 7)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x=3+45+6'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x=3++6'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x=36'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x='); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'x'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); + + test('deleteInsideWord - in words 1', () => { + withTestCodeEditor([ + 'This is interesting' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 7)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This interesting'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); + + test('deleteInsideWord - in words 2', () => { + withTestCodeEditor([ + 'This is interesting' + ], {}, (editor, _) => { + const model = editor.getModel()!; + editor.setPosition(new Position(1, 7)); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This interesting'); + deleteInsideWord(editor); + assert.equal(model.getValue(), 'This'); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + deleteInsideWord(editor); + assert.equal(model.getValue(), ''); + }); + }); }); diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index c7db9649..82dcbddc 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorCommand, ICommandOptions, ServicesAccessor, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import { EditorCommand, ICommandOptions, ServicesAccessor, registerEditorCommand, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; import { CursorState } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; @@ -480,6 +481,36 @@ export class DeleteWordRight extends DeleteWordRightCommand { } } +export class DeleteInsideWord extends EditorAction { + + constructor() { + super({ + id: 'deleteInsideWord', + precondition: EditorContextKeys.writable, + label: nls.localize('deleteInsideWord', "Delete Word"), + alias: 'Delete Word' + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { + if (!editor.hasModel()) { + return; + } + const wordSeparators = getMapForWordSeparators(editor.getOption(EditorOption.wordSeparators)); + const model = editor.getModel(); + const selections = editor.getSelections(); + + const commands = selections.map((sel) => { + const deleteRange = WordOperations.deleteInsideWord(wordSeparators, model, sel); + return new ReplaceCommand(deleteRange, ''); + }); + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } +} + registerEditorCommand(new CursorWordStartLeft()); registerEditorCommand(new CursorWordEndLeft()); registerEditorCommand(new CursorWordLeft()); @@ -502,3 +533,4 @@ registerEditorCommand(new DeleteWordLeft()); registerEditorCommand(new DeleteWordStartRight()); registerEditorCommand(new DeleteWordEndRight()); registerEditorCommand(new DeleteWordRight()); +registerEditorAction(DeleteInsideWord); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index a591facd..90428321 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -14,7 +14,7 @@ import 'vs/editor/contrib/caretOperations/transpose'; import 'vs/editor/contrib/clipboard/clipboard'; import 'vs/editor/contrib/codeAction/codeActionContributions'; import 'vs/editor/contrib/codelens/codelensController'; -import 'vs/editor/contrib/colorPicker/colorDetector'; +import 'vs/editor/contrib/colorPicker/colorContributions'; import 'vs/editor/contrib/comment/comment'; import 'vs/editor/contrib/contextmenu/contextmenu'; import 'vs/editor/contrib/cursorUndo/cursorUndo'; @@ -31,10 +31,10 @@ import 'vs/editor/contrib/hover/hover'; import 'vs/editor/contrib/indentation/indentation'; import 'vs/editor/contrib/inPlaceReplace/inPlaceReplace'; import 'vs/editor/contrib/linesOperations/linesOperations'; +import 'vs/editor/contrib/linkedEditing/linkedEditing'; import 'vs/editor/contrib/links/links'; import 'vs/editor/contrib/multicursor/multicursor'; import 'vs/editor/contrib/parameterHints/parameterHints'; -import 'vs/editor/contrib/rename/onTypeRename'; import 'vs/editor/contrib/rename/rename'; import 'vs/editor/contrib/smartSelect/smartSelect'; import 'vs/editor/contrib/snippet/snippetController2'; diff --git a/src/vs/editor/editor.api.ts b/src/vs/editor/editor.api.ts index 19d57ab2..0e4c48c3 100644 --- a/src/vs/editor/editor.api.ts +++ b/src/vs/editor/editor.api.ts @@ -8,8 +8,6 @@ import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase' import { createMonacoEditorAPI } from 'vs/editor/standalone/browser/standaloneEditor'; import { createMonacoLanguagesAPI } from 'vs/editor/standalone/browser/standaloneLanguages'; -const global: any = self; - // Set defaults for standalone editor EditorOptions.wrappingIndent.defaultValue = WrappingIndent.None; EditorOptions.glyphMargin.defaultValue = false; @@ -34,10 +32,10 @@ export const Token = api.Token; export const editor = api.editor; export const languages = api.languages; -global.monaco = api; +self.monaco = api; -if (typeof global.require !== 'undefined' && typeof global.require.config === 'function') { - global.require.config({ +if (typeof self.require !== 'undefined' && typeof self.require.config === 'function') { + self.require.config({ ignoreDuplicateModules: [ 'vscode-languageserver-types', 'vscode-languageserver-types/main', diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index fdde96ab..22bad13e 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -15,6 +15,8 @@ import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { MonarchTokenizer } from 'vs/editor/standalone/common/monarch/monarchLexer'; +const ttPolicy = window.trustedTypes?.createPolicy('standaloneColorizer', { createHTML: value => value }); + export interface IColorizerOptions { tabSize?: number; } @@ -40,7 +42,8 @@ export class Colorizer { let text = domNode.firstChild ? domNode.firstChild.nodeValue : ''; domNode.className += ' ' + theme; let render = (str: string) => { - domNode.innerHTML = str; + const trustedhtml = ttPolicy ? ttPolicy.createHTML(str) : str; + domNode.innerHTML = trustedhtml as unknown as string; }; return this.colorize(modeService, text || '', mimeType, options).then(render, (err) => console.error(err)); } @@ -54,7 +57,7 @@ export class Colorizer { if (strings.startsWithUTF8BOM(text)) { text = text.substr(1); } - let lines = text.split(/\r\n|\r|\n/); + let lines = strings.splitLines(text); let language = modeService.getModeId(mimeType); if (!language) { return Promise.resolve(_fakeColorize(lines, tabSize)); diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 8248e274..ba51dfd9 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -332,7 +332,8 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { when: when, weight1: 1000, weight2: 0, - extensionId: null + extensionId: null, + isBuiltinExtension: false }); toDispose.add(toDisposable(() => { @@ -380,11 +381,11 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { if (!keybinding) { // This might be a removal keybinding item in user settings => accept it - result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault, null); + result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault, null, false); } else { const resolvedKeybindings = this.resolveKeybinding(keybinding); for (const resolvedKeybinding of resolvedKeybindings) { - result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault, null); + result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault, null, false); } } } diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index e06bd35a..9344ee80 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -113,6 +113,10 @@ export interface IGlobalEditorOptions { * Defaults to true. */ wordBasedSuggestions?: boolean; + /** + * Controls whether word based completions should be included from opened documents of the same language or any language. + */ + wordBasedSuggestionsOnlySameLanguage?: boolean; /** * Controls whether the semanticHighlighting is shown for the languages that support it. * true: semanticHighlighting is enabled for all themes diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index fb2aaac0..6d5fc9c3 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -41,6 +41,7 @@ import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl'; +import { splitLines } from 'vs/base/common/strings'; type Omit = Pick>; @@ -288,7 +289,7 @@ export function tokenize(text: string, languageId: string): Token[][] { modeService.triggerMode(languageId); let tokenizationSupport = getSafeTokenizationSupport(languageId); - let lines = text.split(/\r\n|\r|\n/); + let lines = splitLines(text); let result: Token[][] = []; let state = tokenizationSupport.getInitialState(); for (let i = 0, len = lines.length; i < len; i++) { diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index b95826e9..dbf225a6 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -75,9 +75,11 @@ export function setLanguageConfiguration(languageId: string, configuration: Lang */ export class EncodedTokenizationSupport2Adapter implements modes.ITokenizationSupport { + private readonly _languageIdentifier: modes.LanguageIdentifier; private readonly _actual: EncodedTokensProvider; - constructor(actual: EncodedTokensProvider) { + constructor(languageIdentifier: modes.LanguageIdentifier, actual: EncodedTokensProvider) { + this._languageIdentifier = languageIdentifier; this._actual = actual; } @@ -86,6 +88,9 @@ export class EncodedTokenizationSupport2Adapter implements modes.ITokenizationSu } public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { + if (typeof this._actual.tokenize === 'function') { + return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, <{ tokenize(line: string, state: modes.IState): ILineTokens; }>this._actual, line, state, offsetDelta); + } throw new Error('Not supported!'); } @@ -114,7 +119,7 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { return this._actual.getInitialState(); } - private _toClassicTokens(tokens: IToken[], language: string, offsetDelta: number): Token[] { + private static _toClassicTokens(tokens: IToken[], language: string, offsetDelta: number): Token[] { let result: Token[] = []; let previousStartIndex: number = 0; for (let i = 0, len = tokens.length; i < len; i++) { @@ -137,9 +142,9 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { return result; } - public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { - let actualResult = this._actual.tokenize(line, state); - let tokens = this._toClassicTokens(actualResult.tokens, this._languageIdentifier.language, offsetDelta); + public static adaptTokenize(language: string, actual: { tokenize(line: string, state: modes.IState): ILineTokens; }, line: string, state: modes.IState, offsetDelta: number): TokenizationResult { + let actualResult = actual.tokenize(line, state); + let tokens = TokenizationSupport2Adapter._toClassicTokens(actualResult.tokens, language, offsetDelta); let endState: modes.IState; // try to save an object if possible @@ -152,6 +157,10 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { return new TokenizationResult(tokens, endState); } + public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { + return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, this._actual, line, state, offsetDelta); + } + private _toBinaryTokens(tokens: IToken[], offsetDelta: number): Uint32Array { const languageId = this._languageIdentifier.id; const tokenTheme = this._standaloneThemeService.getColorTheme().tokenTheme; @@ -287,6 +296,10 @@ export interface EncodedTokensProvider { * Tokenize a line given the state at the beginning of the line. */ tokenizeEncoded(line: string, state: modes.IState): IEncodedLineTokens; + /** + * Tokenize a line given the state at the beginning of the line. + */ + tokenize?(line: string, state: modes.IState): ILineTokens; } function isEncodedTokensProvider(provider: TokensProvider | EncodedTokensProvider): provider is EncodedTokensProvider { @@ -307,7 +320,7 @@ export function setTokensProvider(languageId: string, provider: TokensProvider | } const create = (provider: TokensProvider | EncodedTokensProvider) => { if (isEncodedTokensProvider(provider)) { - return new EncodedTokenizationSupport2Adapter(provider); + return new EncodedTokenizationSupport2Adapter(languageIdentifier!, provider); } else { return new TokenizationSupport2Adapter(StaticServices.standaloneThemeService.get(), languageIdentifier!, provider); } @@ -392,10 +405,10 @@ export function registerDocumentHighlightProvider(languageId: string, provider: } /** - * Register an on type rename provider. + * Register an linked editing range provider. */ -export function registerOnTypeRenameProvider(languageId: string, provider: modes.OnTypeRenameProvider): IDisposable { - return modes.OnTypeRenameProviderRegistry.register(languageId, provider); +export function registerLinkedEditingRangeProvider(languageId: string, provider: modes.LinkedEditingRangeProvider): IDisposable { + return modes.LinkedEditingRangeProviderRegistry.register(languageId, provider); } /** @@ -566,7 +579,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { registerHoverProvider: registerHoverProvider, registerDocumentSymbolProvider: registerDocumentSymbolProvider, registerDocumentHighlightProvider: registerDocumentHighlightProvider, - registerOnTypeRenameProvider: registerOnTypeRenameProvider, + registerLinkedEditingRangeProvider: registerLinkedEditingRangeProvider, registerDefinitionProvider: registerDefinitionProvider, registerImplementationProvider: registerImplementationProvider, registerTypeDefinitionProvider: registerTypeDefinitionProvider, diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 6baca469..4a0e6c93 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -54,7 +54,6 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; import { StandaloneQuickInputServiceImpl } from 'vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export interface IEditorOverrideServices { [index: string]: any; @@ -167,8 +166,6 @@ export module StaticServices { export const storageService = define(IStorageService, () => new InMemoryStorageService()); - export const storageSyncService = define(IStorageKeysSyncRegistryService, () => new StorageKeysSyncRegistryService()); - export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), logService.get(o))); } diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 54ca7023..544d7b07 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -16,7 +16,7 @@ import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/c import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IThemingRegistry, ITokenStyle } from 'vs/platform/theme/common/themeService'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ColorScheme } from 'vs/platform/theme/common/theme'; -import { CodiconStyles } from 'vs/base/browser/ui/codicons/codiconStyles'; +import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry'; const VS_THEME_NAME = 'vs'; const VS_DARK_THEME_NAME = 'vs-dark'; @@ -208,15 +208,18 @@ export class StandaloneThemeServiceImpl extends Disposable implements IStandalon this._knownThemes.set(VS_THEME_NAME, newBuiltInTheme(VS_THEME_NAME)); this._knownThemes.set(VS_DARK_THEME_NAME, newBuiltInTheme(VS_DARK_THEME_NAME)); this._knownThemes.set(HC_BLACK_THEME_NAME, newBuiltInTheme(HC_BLACK_THEME_NAME)); - this._codiconCSS = CodiconStyles.getCSS(); + + const iconRegistry = getIconRegistry(); + + this._codiconCSS = iconRegistry.getCSS(); this._themeCSS = ''; this._allCSS = `${this._codiconCSS}\n${this._themeCSS}`; this._globalStyleElement = null; this._styleElements = []; this.setTheme(VS_THEME_NAME); - CodiconStyles.onDidChange(() => { - this._codiconCSS = CodiconStyles.getCSS(); + iconRegistry.onDidChange(() => { + this._codiconCSS = iconRegistry.getCSS(); this._updateCSS(); }); } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index a90e11df..cb202a3f 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2208,6 +2208,42 @@ suite('Editor Controller - Regression tests', () => { }); }); + test('issue #110376: multiple selections with wordwrap behave differently', () => { + // a single model line => 4 view lines + withTestCodeEditor([ + [ + 'just a sentence. just a ', + 'sentence. just a sentence.', + ].join('') + ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 25 }, (editor, viewModel) => { + viewModel.setSelections('test', [ + new Selection(1, 1, 1, 16), + new Selection(1, 18, 1, 33), + new Selection(1, 35, 1, 50), + ]); + + moveLeft(editor, viewModel); + assertCursor(viewModel, [ + new Selection(1, 1, 1, 1), + new Selection(1, 18, 1, 18), + new Selection(1, 35, 1, 35), + ]); + + viewModel.setSelections('test', [ + new Selection(1, 1, 1, 16), + new Selection(1, 18, 1, 33), + new Selection(1, 35, 1, 50), + ]); + + moveRight(editor, viewModel); + assertCursor(viewModel, [ + new Selection(1, 16, 1, 16), + new Selection(1, 33, 1, 33), + new Selection(1, 50, 1, 50), + ]); + }); + }); + test('issue #98320: Multi-Cursor, Wrap lines and cursorSelectRight ==> cursors out of sync', () => { // a single model line => 4 view lines withTestCodeEditor([ @@ -4038,6 +4074,33 @@ suite('Editor Controller - Indentation Rules', () => { mode.dispose(); }); + test('issue #57197: indent rules regex should be stateless', () => { + usingCursor({ + text: [ + 'Project:', + ], + languageIdentifier: (new IndentRulesMode({ + decreaseIndentPattern: /^\s*}$/gm, + increaseIndentPattern: /^(?![^\S\n]*(?!--|––|——)(?:[-❍❑■âŦœâ–Ąâ˜â–Ēâ–Ģ–—≡→â€ē✘xX✔✓☑+]|\[[ xX+-]?\])\s[^\n]*)[^\S\n]*(.+:)[^\S\n]*(?:(?=@[^\s*~(]+(?::\/\/[^\s*~(:]+)?(?:\([^)]*\))?)|$)/gm, + })).getLanguageIdentifier(), + modelOpts: { insertSpaces: false }, + editorOpts: { autoIndent: 'full' } + }, (editor, model, viewModel) => { + moveTo(editor, viewModel, 1, 9, false); + assertCursor(viewModel, new Selection(1, 9, 1, 9)); + + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); + + moveTo(editor, viewModel, 1, 9, false); + assertCursor(viewModel, new Selection(1, 9, 1, 9)); + viewModel.type('\n', 'keyboard'); + model.forceTokenization(model.getLineCount()); + assertCursor(viewModel, new Selection(2, 2, 2, 2)); + }); + }); + test('', () => { class JSONMode extends MockMode { private static readonly _id = new LanguageIdentifier('indentRulesMode', 4); @@ -4633,7 +4696,7 @@ suite('autoClosingPairs', () => { 'v|ar |c = \'|asd\';|', 'v|ar d = "|asd";|', 'v|ar e = /*3*/ 3;|', - 'v|ar f = /** 3 */3;|', + 'v|ar f = /** 3| */3;|', 'v|ar g = (3+5|);|', 'v|ar h = { |a: \'v|alue\' |};|', ]; @@ -4814,13 +4877,13 @@ suite('autoClosingPairs', () => { let autoClosePositions = [ 'var a |=| [|]|;|', - 'var b |=| |`asd`|;|', - 'var c |=| |\'asd\'|;|', - 'var d |=| |"asd"|;|', + 'var b |=| `asd`|;|', + 'var c |=| \'asd\'|;|', + 'var d |=| "asd"|;|', 'var e |=| /*3*/| 3;|', 'var f |=| /**| 3 */3;|', 'var g |=| (3+5)|;|', - 'var h |=| {| a:| |\'value\'| |}|;|', + 'var h |=| {| a:| \'value\'| |}|;|', ]; for (let i = 0, len = autoClosePositions.length; i < len; i++) { const lineNumber = i + 1; @@ -4863,6 +4926,51 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('issue #72177: multi-character autoclose with conflicting patterns', () => { + const languageId = new LanguageIdentifier('autoClosingModeMultiChar', 5); + class AutoClosingModeMultiChar extends MockMode { + constructor() { + super(languageId); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + autoClosingPairs: [ + { open: '(', close: ')' }, + { open: '(*', close: '*)' }, + { open: '<@', close: '@>' }, + { open: '<@@', close: '@@>' }, + ], + })); + } + } + + const mode = new AutoClosingModeMultiChar(); + + usingCursor({ + text: [ + '', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (editor, model, viewModel) => { + viewModel.type('(', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '()'); + viewModel.type('*', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '(**)', `doesn't add entire close when already closed substring is there`); + + model.setValue('('); + viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]); + viewModel.type('*', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '(**)', `does add entire close if not already there`); + + model.setValue(''); + viewModel.type('<@', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '<@@>'); + viewModel.type('@', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '<@@@@>', `autocloses when before multi-character closing brace`); + viewModel.type('(', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '<@@()@@>', `autocloses when before multi-character closing brace`); + }); + mode.dispose(); + }); + test('issue #55314: Do not auto-close when ending with open', () => { const languageId = new LanguageIdentifier('myElectricMode', 5); class ElectricMode extends MockMode { @@ -4916,7 +5024,7 @@ suite('autoClosingPairs', () => { ], languageIdentifier: mode.getLanguageIdentifier() }, (editor, model, viewModel) => { - assertType(editor, model, viewModel, 1, 12, '"', '""', `does not over type and will auto close`); + assertType(editor, model, viewModel, 1, 12, '"', '"', `does not over type and will not auto close`); }); mode.dispose(); }); @@ -5277,7 +5385,7 @@ suite('autoClosingPairs', () => { assert.equal(model.getValue(), 'console.log(\'it\\\');'); viewModel.type('\'', 'keyboard'); - assert.equal(model.getValue(), 'console.log(\'it\\\'\'\');'); + assert.equal(model.getValue(), 'console.log(\'it\\\'\');'); }); mode.dispose(); }); diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index 4a3f4e19..eb095840 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -9,6 +9,7 @@ import { ISimpleModel, PagedScreenReaderStrategy, TextAreaState } from 'vs/edito import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { EndOfLinePreference } from 'vs/editor/common/model'; +import * as dom from 'vs/base/browser/dom'; // To run this test, open imeTester.html @@ -50,12 +51,13 @@ class TestView { } public paint(output: HTMLElement) { - let r = ''; + dom.clearNode(output); for (let i = 1; i <= this._model.getLineCount(); i++) { - let content = this._model.getModelLineContent(i); - r += content + '
'; + const textNode = document.createTextNode(this._model.getModelLineContent(i)); + output.appendChild(textNode); + const br = document.createElement('br'); + output.appendChild(br); } - output.innerHTML = r; } } @@ -69,7 +71,12 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string let title = document.createElement('div'); title.className = 'title'; - title.innerHTML = description + '. Type ' + inputStr + ''; + const inputStrStrong = document.createElement('strong'); + inputStrStrong.innerText = inputStr; + + title.innerText = description + '. Type '; + title.appendChild(inputStrStrong); + container.appendChild(title); let startBtn = document.createElement('button'); @@ -140,7 +147,7 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string check.innerText = '[BAD]'; check.className = 'check bad'; } - check.innerHTML += expected; + check.appendChild(document.createTextNode(expected)); }; handler.onType((e) => { diff --git a/src/vs/editor/test/browser/editorTestServices.ts b/src/vs/editor/test/browser/editorTestServices.ts index e9fba5fb..55ceb373 100644 --- a/src/vs/editor/test/browser/editorTestServices.ts +++ b/src/vs/editor/test/browser/editorTestServices.ts @@ -22,6 +22,7 @@ export class TestCodeEditorService extends AbstractCodeEditorService { public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { } public removeDecorationType(key: string): void { } public resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions { return {}; } + public resolveDecorationCSSRules(decorationTypeKey: string): CSSRuleList | null { return null; } } export class TestCommandService implements ICommandService { diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index bc522416..9c8c6c6e 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -149,25 +149,33 @@ suite('Decoration Render Options', () => { assert(readStyleSheet(styleSheet).indexOf(`{background:url('data:image/svg+xml;base64,PHN2ZyB4b+') center center no-repeat;}`) > 0); s.removeDecorationType('example'); + function assertBackground(url1: string, url2: string) { + const actual = readStyleSheet(styleSheet); + assert( + actual.indexOf(`{background:url('${url1}') center center no-repeat;}`) > 0 + || actual.indexOf(`{background:url('${url2}') center center no-repeat;}`) > 0 + ); + } + if (platform.isWindows) { // windows file path (used as string) s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\miles\\more.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///c:/files/miles/more.png') center center no-repeat;}`) > 0); + assertBackground('file:///c:/files/miles/more.png', 'vscode-file://vscode-app/c:/files/miles/more.png'); s.removeDecorationType('example'); // single quote must always be escaped/encoded s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\foo\\b\'ar.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///c:/files/foo/b%27ar.png') center center no-repeat;}`) > 0); + assertBackground('file:///c:/files/foo/b%27ar.png', 'vscode-file://vscode-app/c:/files/foo/b%27ar.png'); s.removeDecorationType('example'); } else { // unix file path (used as string) s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/bar.png') center center no-repeat;}`) > 0); + assertBackground('file:///Users/foo/bar.png', 'vscode-file://vscode-app/Users/foo/bar.png'); s.removeDecorationType('example'); // single quote must always be escaped/encoded s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/b%27ar.png') center center no-repeat;}`) > 0); + assertBackground('file:///Users/foo/b%27ar.png', 'vscode-file://vscode-app/Users/foo/b%27ar.png'); s.removeDecorationType('example'); } diff --git a/src/vs/editor/test/common/controller/cursorAtomicMoveOperations.test.ts b/src/vs/editor/test/common/controller/cursorAtomicMoveOperations.test.ts new file mode 100644 index 00000000..e9c60b6e --- /dev/null +++ b/src/vs/editor/test/common/controller/cursorAtomicMoveOperations.test.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations'; + +suite('Cursor move command test', () => { + + test('Test whitespaceVisibleColumn', () => { + const testCases = [ + { + lineContent: ' ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1], + expectedVisibleColumn: [0, 1, 2, 3, 4, 5, 6, 7, 8, -1], + }, + { + lineContent: ' ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, -1], + expectedVisibleColumn: [0, 1, 2, -1], + }, + { + lineContent: '\t', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, -1], + expectedVisibleColumn: [0, 4, -1], + }, + { + lineContent: '\t ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 1, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 4, -1], + expectedVisibleColumn: [0, 4, 5, -1], + }, + { + lineContent: ' \t\t ', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, 2, 3, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, 4, 8, -1], + expectedVisibleColumn: [0, 1, 4, 8, 9, -1], + }, + { + lineContent: ' \tA', + tabSize: 4, + expectedPrevTabStopPosition: [-1, 0, 0, -1, -1], + expectedPrevTabStopVisibleColumn: [-1, 0, 0, -1, -1], + expectedVisibleColumn: [0, 1, 4, -1, -1], + }, + { + lineContent: 'A', + tabSize: 4, + expectedPrevTabStopPosition: [-1, -1, -1], + expectedPrevTabStopVisibleColumn: [-1, -1, -1], + expectedVisibleColumn: [0, -1, -1], + }, + { + lineContent: '', + tabSize: 4, + expectedPrevTabStopPosition: [-1, -1], + expectedPrevTabStopVisibleColumn: [-1, -1], + expectedVisibleColumn: [0, -1], + }, + ]; + + for (const testCase of testCases) { + const maxPosition = testCase.expectedVisibleColumn.length; + for (let position = 0; position < maxPosition; position++) { + const actual = AtomicTabMoveOperations.whitespaceVisibleColumn(testCase.lineContent, position, testCase.tabSize); + const expected = [ + testCase.expectedPrevTabStopPosition[position], + testCase.expectedPrevTabStopVisibleColumn[position], + testCase.expectedVisibleColumn[position] + ]; + assert.deepStrictEqual(actual, expected); + } + } + }); + + test('Test atomicPosition', () => { + const testCases = [ + { + lineContent: ' ', + tabSize: 4, + expectedLeft: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1], + expectedRight: [4, 4, 4, 4, 8, 8, 8, 8, -1, -1], + expectedNearest: [0, 0, 0, 4, 4, 4, 4, 8, 8, -1], + }, + { + lineContent: ' \t', + tabSize: 4, + expectedLeft: [-1, 0, 0, -1], + expectedRight: [2, 2, -1, -1], + expectedNearest: [0, 0, 2, -1], + }, + { + lineContent: '\t ', + tabSize: 4, + expectedLeft: [-1, 0, -1, -1], + expectedRight: [1, -1, -1, -1], + expectedNearest: [0, 1, -1, -1], + }, + { + lineContent: ' \t ', + tabSize: 4, + expectedLeft: [-1, 0, 0, -1, -1], + expectedRight: [2, 2, -1, -1, -1], + expectedNearest: [0, 0, 2, -1, -1], + }, + { + lineContent: ' A', + tabSize: 4, + expectedLeft: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1, -1], + expectedRight: [4, 4, 4, 4, 8, 8, 8, 8, -1, -1, -1], + expectedNearest: [0, 0, 0, 4, 4, 4, 4, 8, 8, -1, -1], + }, + { + lineContent: ' foo', + tabSize: 4, + expectedLeft: [-1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1], + expectedRight: [4, 4, 4, 4, -1, -1, -1, -1, -1, -1, -1], + expectedNearest: [0, 0, 0, 4, 4, -1, -1, -1, -1, -1, -1], + }, + ]; + + for (const testCase of testCases) { + for (const { direction, expected } of [ + { + direction: Direction.Left, + expected: testCase.expectedLeft, + }, + { + direction: Direction.Right, + expected: testCase.expectedRight, + }, + { + direction: Direction.Nearest, + expected: testCase.expectedNearest, + }, + ]) { + + const actual = expected.map((_, i) => AtomicTabMoveOperations.atomicPosition(testCase.lineContent, i, testCase.tabSize, direction)); + assert.deepStrictEqual(actual, expected); + } + } + }); +}); diff --git a/src/vs/editor/test/common/diff/diffComputer.test.ts b/src/vs/editor/test/common/diff/diffComputer.test.ts index a4d574c2..9361e05d 100644 --- a/src/vs/editor/test/common/diff/diffComputer.test.ts +++ b/src/vs/editor/test/common/diff/diffComputer.test.ts @@ -852,4 +852,31 @@ suite('Editor Diff - DiffComputer', () => { ]; assertDiff(original, modified, expected, false, false, false); }); + + test('gives preference to matching longer lines', () => { + let original = [ + 'A', + 'A', + 'BB', + 'C', + ]; + let modified = [ + 'A', + 'BB', + 'A', + 'D', + 'E', + 'A', + 'C', + ]; + let expected = [ + createLineChange( + 2, 2, 1, 0 + ), + createLineChange( + 3, 0, 3, 6 + ) + ]; + assertDiff(original, modified, expected, false, false, false); + }); }); diff --git a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts index 49841dab..4f87bf63 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from 'vs/base/common/charCode'; +import { splitLines } from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ValidAnnotatedEditOperation } from 'vs/editor/common/model'; @@ -34,7 +35,7 @@ export function getRandomString(minLength: number, maxLength: number): string { export function generateRandomEdits(chunks: string[], editCnt: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { - let newLines = chunk.split(/\r\n|\r|\n/); + let newLines = splitLines(chunk); if (lines.length === 0) { lines.push(...newLines); } else { @@ -64,7 +65,7 @@ export function generateRandomEdits(chunks: string[], editCnt: number): ValidAnn export function generateSequentialInserts(chunks: string[], editCnt: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { - let newLines = chunk.split(/\r\n|\r|\n/); + let newLines = splitLines(chunk); if (lines.length === 0) { lines.push(...newLines); } else { @@ -96,7 +97,7 @@ export function generateSequentialInserts(chunks: string[], editCnt: number): Va export function generateRandomReplaces(chunks: string[], editCnt: number, searchStringLen: number, replaceStringLen: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { - let newLines = chunk.split(/\r\n|\r|\n/); + let newLines = splitLines(chunk); if (lines.length === 0) { lines.push(...newLines); } else { diff --git a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts index ddec1fac..019b23f4 100644 --- a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts @@ -14,6 +14,7 @@ import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeText import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; +import { splitLines } from 'vs/base/common/strings'; const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n'; @@ -75,7 +76,7 @@ function trimLineFeed(text: string): string { //#region Assertion function testLinesContent(str: string, pieceTable: PieceTreeBase) { - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assert.equal(pieceTable.getLinesRawContent(), str); for (let i = 0; i < lines.length; i++) { @@ -997,7 +998,7 @@ suite('CRLF', () => { pieceTable.delete(2, 3); str = str.substring(0, 2) + str.substring(2 + 3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1012,7 +1013,7 @@ suite('CRLF', () => { pieceTable.delete(4, 1); str = str.substring(0, 4) + str.substring(4 + 1); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1033,7 +1034,7 @@ suite('CRLF', () => { pieceTable.insert(3, '\r\r\r\n'); str = str.substring(0, 3) + '\r\r\r\n' + str.substring(3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1205,7 +1206,7 @@ suite('centralized lineStarts with CRLF', () => { pieceTable.delete(2, 3); str = str.substring(0, 2) + str.substring(2 + 3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1218,7 +1219,7 @@ suite('centralized lineStarts with CRLF', () => { pieceTable.delete(4, 1); str = str.substring(0, 4) + str.substring(4 + 1); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); @@ -1238,7 +1239,7 @@ suite('centralized lineStarts with CRLF', () => { pieceTable.insert(3, '\r\r\r\n'); str = str.substring(0, 3) + '\r\r\r\n' + str.substring(3); - let lines = str.split(/\r\n|\r|\n/); + let lines = splitLines(str); assert.equal(pieceTable.getLineCount(), lines.length); assertTreeInvariants(pieceTable); }); diff --git a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts index 2b2a748c..54f73ed0 100644 --- a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts +++ b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts @@ -162,7 +162,7 @@ suite('EditorSimpleWorker', () => { 'f f' // 2 ]); - return worker.textualSuggest(model.uri.toString(), { lineNumber: 2, column: 2 }, '[a-z]+', 'img').then((result) => { + return worker.textualSuggest([model.uri.toString()], 'f', '[a-z]+', 'img').then((result) => { if (!result) { assert.ok(false); return; diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index c4c1af55..910165a0 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -68,6 +68,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { horizontalSliderSize: EditorOptions.scrollbar.defaultValue.horizontalSliderSize, verticalScrollbarSize: input.verticalScrollbarWidth, verticalSliderSize: EditorOptions.scrollbar.defaultValue.verticalSliderSize, + scrollByPage: EditorOptions.scrollbar.defaultValue.scrollByPage, }; options._write(EditorOption.scrollbar, scrollbarOptions); const lineNumbersOptions: InternalEditorRenderLineNumbersOptions = { @@ -78,7 +79,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { options._write(EditorOption.wordWrap, 'off'); options._write(EditorOption.wordWrapColumn, 80); - options._write(EditorOption.wordWrapMinified, true); + options._write(EditorOption.wordWrapOverride1, 'inherit'); + options._write(EditorOption.wordWrapOverride2, 'inherit'); options._write(EditorOption.accessibilitySupport, 'auto'); const actual = EditorLayoutInfoComputer.computeLayout(options, { diff --git a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts index 509a0fe0..670f4240 100644 --- a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts @@ -5,8 +5,9 @@ import * as assert from 'assert'; import { WrappingIndent, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; -import { ILineBreaksComputerFactory, LineBreakData } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { LineBreakData } from 'vs/editor/common/viewModel/viewModel'; function parseAnnotatedText(annotatedText: string): { text: string; indices: number[]; } { let text = ''; diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 2c5cca1c..3156be0e 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -14,8 +14,8 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; -import { LineBreakData, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { ISimpleModel, SplitLine, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { LineBreakData, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; diff --git a/src/vs/loader.js b/src/vs/loader.js index 5a97ccfc..76db9773 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -198,6 +198,9 @@ var AMDLoader; if (!obj || typeof obj !== 'object' || obj instanceof RegExp) { return obj; } + if (!Array.isArray(obj) && Object.getPrototypeOf(obj) !== Object.prototype) { + return obj; + } var result = Array.isArray(obj) ? [] : {}; Utilities.forEachProperty(obj, function (key, value) { if (value && typeof value === 'object') { @@ -618,37 +621,8 @@ var AMDLoader; }; return OnlyOnceScriptLoader; }()); - var trustedTypesPolyfill = new /** @class */(function () { - function class_1() { - } - class_1.prototype.installIfNeeded = function () { - if (typeof globalThis.trustedTypes !== 'undefined') { - return; // already defined - } - var _defaultRules = { - createHTML: function () { throw new Error('Policy\'s TrustedTypePolicyOptions did not specify a \'createHTML\' member'); }, - createScript: function () { throw new Error('Policy\'s TrustedTypePolicyOptions did not specify a \'createScript\' member'); }, - createScriptURL: function () { throw new Error('Policy\'s TrustedTypePolicyOptions did not specify a \'createScriptURL\' member'); }, - }; - globalThis.trustedTypes = { - createPolicy: function (name, rules) { - var _a, _b, _c; - return { - name: name, - createHTML: (_a = rules.createHTML) !== null && _a !== void 0 ? _a : _defaultRules.createHTML, - createScript: (_b = rules.createScript) !== null && _b !== void 0 ? _b : _defaultRules.createScript, - createScriptURL: (_c = rules.createScriptURL) !== null && _c !== void 0 ? _c : _defaultRules.createScriptURL, - }; - } - }; - }; - return class_1; - }()); - //#endregion var BrowserScriptLoader = /** @class */ (function () { function BrowserScriptLoader() { - // polyfill trustedTypes-support if missing - trustedTypesPolyfill.installIfNeeded(); } /** * Attach load / error listeners to a script element and remove them when either one has fired. @@ -673,7 +647,7 @@ var AMDLoader; BrowserScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { if (/^node\|/.test(scriptSrc)) { var opts = moduleManager.getConfig().getOptionsLiteral(); - var nodeRequire = (opts.nodeRequire || AMDLoader.global.nodeRequire); + var nodeRequire = ensureRecordedNodeRequire(moduleManager.getRecorder(), (opts.nodeRequire || AMDLoader.global.nodeRequire)); var pieces = scriptSrc.split('|'); var moduleExports_1 = null; try { @@ -691,12 +665,9 @@ var AMDLoader; script.setAttribute('async', 'async'); script.setAttribute('type', 'text/javascript'); this.attachListeners(script, callback, errorback); - var createTrustedScriptURL = moduleManager.getConfig().getOptionsLiteral().createTrustedScriptURL; - if (createTrustedScriptURL) { - if (!this.scriptSourceURLPolicy) { - this.scriptSourceURLPolicy = trustedTypes.createPolicy('amdLoader', { createScriptURL: createTrustedScriptURL }); - } - scriptSrc = this.scriptSourceURLPolicy.createScriptURL(scriptSrc); + var trustedTypesPolicy = moduleManager.getConfig().getOptionsLiteral().trustedTypesPolicy; + if (trustedTypesPolicy) { + scriptSrc = trustedTypesPolicy.createScriptURL(scriptSrc); } script.setAttribute('src', scriptSrc); // Propagate CSP nonce to dynamically created script tag. @@ -711,16 +682,11 @@ var AMDLoader; }()); var WorkerScriptLoader = /** @class */ (function () { function WorkerScriptLoader() { - // polyfill trustedTypes-support if missing - trustedTypesPolyfill.installIfNeeded(); } WorkerScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { - var createTrustedScriptURL = moduleManager.getConfig().getOptionsLiteral().createTrustedScriptURL; - if (createTrustedScriptURL) { - if (!this.scriptSourceURLPolicy) { - this.scriptSourceURLPolicy = trustedTypes.createPolicy('amdLoader', { createScriptURL: createTrustedScriptURL }); - } - scriptSrc = this.scriptSourceURLPolicy.createScriptURL(scriptSrc); + var trustedTypesPolicy = moduleManager.getConfig().getOptionsLiteral().trustedTypesPolicy; + if (trustedTypesPolicy) { + scriptSrc = trustedTypesPolicy.createScriptURL(scriptSrc); } try { importScripts(scriptSrc); @@ -815,7 +781,7 @@ var AMDLoader; NodeScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { var _this = this; var opts = moduleManager.getConfig().getOptionsLiteral(); - var nodeRequire = (opts.nodeRequire || AMDLoader.global.nodeRequire); + var nodeRequire = ensureRecordedNodeRequire(moduleManager.getRecorder(), (opts.nodeRequire || AMDLoader.global.nodeRequire)); var nodeInstrumenter = (opts.nodeInstrumenter || function (c) { return c; }); this._init(nodeRequire); this._initNodeRequire(nodeRequire, moduleManager); @@ -1022,6 +988,24 @@ var AMDLoader; NodeScriptLoader._SUFFIX = '\n});'; return NodeScriptLoader; }()); + function ensureRecordedNodeRequire(recorder, _nodeRequire) { + if (_nodeRequire.__$__isRecorded) { + // it is already recorded + return _nodeRequire; + } + var nodeRequire = function nodeRequire(what) { + recorder.record(33 /* NodeBeginNativeRequire */, what); + try { + return _nodeRequire(what); + } + finally { + recorder.record(34 /* NodeEndNativeRequire */, what); + } + }; + nodeRequire.__$__isRecorded = true; + return nodeRequire; + } + AMDLoader.ensureRecordedNodeRequire = ensureRecordedNodeRequire; function createScriptLoader(env) { return new OnlyOnceScriptLoader(env); } @@ -1853,18 +1837,10 @@ var AMDLoader; }; function init() { if (typeof AMDLoader.global.require !== 'undefined' || typeof require !== 'undefined') { - var _nodeRequire_1 = (AMDLoader.global.require || require); - if (typeof _nodeRequire_1 === 'function' && typeof _nodeRequire_1.resolve === 'function') { + var _nodeRequire = (AMDLoader.global.require || require); + if (typeof _nodeRequire === 'function' && typeof _nodeRequire.resolve === 'function') { // re-expose node's require function - var nodeRequire = function (what) { - moduleManager.getRecorder().record(33 /* NodeBeginNativeRequire */, what); - try { - return _nodeRequire_1(what); - } - finally { - moduleManager.getRecorder().record(34 /* NodeEndNativeRequire */, what); - } - }; + var nodeRequire = AMDLoader.ensureRecordedNodeRequire(moduleManager.getRecorder(), _nodeRequire); AMDLoader.global.nodeRequire = nodeRequire; RequireFunc.nodeRequire = nodeRequire; RequireFunc.__$__nodeRequire = nodeRequire; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b9a579f9..2cb6913f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1115,6 +1115,10 @@ declare namespace monaco.editor { * Defaults to true. */ wordBasedSuggestions?: boolean; + /** + * Controls whether word based completions should be included from opened documents of the same language or any language. + */ + wordBasedSuggestionsOnlySameLanguage?: boolean; /** * Controls whether the semanticHighlighting is shown for the languages that support it. * true: semanticHighlighting is enabled for all themes @@ -1685,6 +1689,10 @@ declare namespace monaco.editor { * @return EOL char sequence (e.g.: '\n' or '\r\n'). */ getEOL(): string; + /** + * Get the end of line sequence predominantly used in the text buffer. + */ + getEndOfLineSequence(): EndOfLineSequence; /** * Get the minimum legal column for line at `lineNumber` */ @@ -1882,11 +1890,15 @@ declare namespace monaco.editor { */ detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void; /** - * Push a stack element onto the undo stack. This acts as an undo/redo point. - * The idea is to use `pushEditOperations` to edit the model and then to - * `pushStackElement` to create an undo/redo stop point. + * Close the current undo-redo element. + * This offers a way to create an undo/redo stop point. */ pushStackElement(): void; + /** + * Open the current undo-redo element. + * This offers a way to remove the current undo/redo stop point. + */ + popStackElement(): void; /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. @@ -2683,9 +2695,13 @@ declare namespace monaco.editor { */ readOnly?: boolean; /** - * Rename matching regions on type. + * Enable linked editing. * Defaults to false. */ + linkedEditing?: boolean; + /** + * deprecated, use linkedEditing instead + */ renameOnType?: boolean; /** * Should the editor render validation decorations. @@ -2799,6 +2815,14 @@ declare namespace monaco.editor { * Defaults to "off". */ wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded'; + /** + * Override the `wordWrap` setting. + */ + wordWrapOverride1?: 'off' | 'on' | 'inherit'; + /** + * Override the `wordWrapOverride1` setting. + */ + wordWrapOverride2?: 'off' | 'on' | 'inherit'; /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. @@ -2808,11 +2832,6 @@ declare namespace monaco.editor { * Defaults to 80. */ wordWrapColumn?: number; - /** - * Force word wrapping when the text appears to be of a minified/generated file. - * Defaults to true. - */ - wordWrapMinified?: boolean; /** * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. * Defaults to 'same' in vscode and to 'none' in monaco-editor. @@ -2909,6 +2928,10 @@ declare namespace monaco.editor { * Suggest options. */ suggest?: ISuggestOptions; + /** + * Smart select opptions; + */ + smartSelect?: ISmartSelectOptions; /** * */ @@ -2955,6 +2978,11 @@ declare namespace monaco.editor { * Defaults to advanced. */ autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; + /** + * Emulate selection behaviour of tab characters when using spaces for indentation. + * This means selection will stick to tab stops. + */ + stickyTabStops?: boolean; /** * Enable format on type. * Defaults to false. @@ -3030,6 +3058,14 @@ declare namespace monaco.editor { * Defaults to true. */ codeLens?: boolean; + /** + * Code lens font family. Defaults to editor font family. + */ + codeLensFontFamily?: string; + /** + * Code lens font size. Default to 90% of the editor font size + */ + codeLensFontSize?: number; /** * Control the behavior and rendering of the code action lightbulb. */ @@ -3177,15 +3213,19 @@ declare namespace monaco.editor { */ originalEditable?: boolean; /** - * Original editor should be have code lens enabled? + * Should the diff editor enable code lens? * Defaults to false. */ - originalCodeLens?: boolean; + diffCodeLens?: boolean; /** - * Modified editor should be have code lens enabled? - * Defaults to false. + * Is the diff editor inside another editor + * Defaults to false */ - modifiedCodeLens?: boolean; + isInEmbeddedEditor?: boolean; + /** + * Control the wrapping of the diff editor. + */ + diffWordWrap?: 'off' | 'on' | 'inherit'; } /** @@ -3656,6 +3696,11 @@ declare namespace monaco.editor { * Defaults to `horizontalScrollbarSize`. */ horizontalSliderSize?: number; + /** + * Scroll gutter clicks move by page vs jump to position. + * Defaults to false. + */ + scrollByPage?: boolean; } export interface InternalEditorScrollbarOptions { @@ -3671,6 +3716,7 @@ declare namespace monaco.editor { readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; readonly verticalSliderSize: number; + readonly scrollByPage: boolean; } /** @@ -3705,6 +3751,10 @@ declare namespace monaco.editor { * Enable or disable the suggest status bar. */ showStatusBar?: boolean; + /** + * Show details inline with the label. Defaults to true. + */ + showInlineDetails?: boolean; /** * Show method-suggestions. */ @@ -3817,6 +3867,12 @@ declare namespace monaco.editor { export type InternalSuggestOptions = Readonly>; + export interface ISmartSelectOptions { + selectLeadingAndTrailingWhitespace?: boolean; + } + + export type SmartSelectOptions = Readonly>; + /** * Describes how to indent wrapped lines. */ @@ -3859,113 +3915,119 @@ declare namespace monaco.editor { automaticLayout = 9, autoSurround = 10, codeLens = 11, - colorDecorators = 12, - columnSelection = 13, - comments = 14, - contextmenu = 15, - copyWithSyntaxHighlighting = 16, - cursorBlinking = 17, - cursorSmoothCaretAnimation = 18, - cursorStyle = 19, - cursorSurroundingLines = 20, - cursorSurroundingLinesStyle = 21, - cursorWidth = 22, - disableLayerHinting = 23, - disableMonospaceOptimizations = 24, - dragAndDrop = 25, - emptySelectionClipboard = 26, - extraEditorClassName = 27, - fastScrollSensitivity = 28, - find = 29, - fixedOverflowWidgets = 30, - folding = 31, - foldingStrategy = 32, - foldingHighlight = 33, - unfoldOnClickAfterEndOfLine = 34, - fontFamily = 35, - fontInfo = 36, - fontLigatures = 37, - fontSize = 38, - fontWeight = 39, - formatOnPaste = 40, - formatOnType = 41, - glyphMargin = 42, - gotoLocation = 43, - hideCursorInOverviewRuler = 44, - highlightActiveIndentGuide = 45, - hover = 46, - inDiffEditor = 47, - letterSpacing = 48, - lightbulb = 49, - lineDecorationsWidth = 50, - lineHeight = 51, - lineNumbers = 52, - lineNumbersMinChars = 53, - links = 54, - matchBrackets = 55, - minimap = 56, - mouseStyle = 57, - mouseWheelScrollSensitivity = 58, - mouseWheelZoom = 59, - multiCursorMergeOverlapping = 60, - multiCursorModifier = 61, - multiCursorPaste = 62, - occurrencesHighlight = 63, - overviewRulerBorder = 64, - overviewRulerLanes = 65, - padding = 66, - parameterHints = 67, - peekWidgetDefaultFocus = 68, - definitionLinkOpensInPeek = 69, - quickSuggestions = 70, - quickSuggestionsDelay = 71, - readOnly = 72, - renameOnType = 73, - renderControlCharacters = 74, - renderIndentGuides = 75, - renderFinalNewline = 76, - renderLineHighlight = 77, - renderLineHighlightOnlyWhenFocus = 78, - renderValidationDecorations = 79, - renderWhitespace = 80, - revealHorizontalRightPadding = 81, - roundedSelection = 82, - rulers = 83, - scrollbar = 84, - scrollBeyondLastColumn = 85, - scrollBeyondLastLine = 86, - scrollPredominantAxis = 87, - selectionClipboard = 88, - selectionHighlight = 89, - selectOnLineNumbers = 90, - showFoldingControls = 91, - showUnused = 92, - snippetSuggestions = 93, - smoothScrolling = 94, - stopRenderingLineAfter = 95, - suggest = 96, - suggestFontSize = 97, - suggestLineHeight = 98, - suggestOnTriggerCharacters = 99, - suggestSelection = 100, - tabCompletion = 101, - tabIndex = 102, - unusualLineTerminators = 103, - useTabStops = 104, - wordSeparators = 105, - wordWrap = 106, - wordWrapBreakAfterCharacters = 107, - wordWrapBreakBeforeCharacters = 108, - wordWrapColumn = 109, - wordWrapMinified = 110, - wrappingIndent = 111, - wrappingStrategy = 112, - showDeprecated = 113, - editorClassName = 114, - pixelRatio = 115, - tabFocusMode = 116, - layoutInfo = 117, - wrappingInfo = 118 + codeLensFontFamily = 12, + codeLensFontSize = 13, + colorDecorators = 14, + columnSelection = 15, + comments = 16, + contextmenu = 17, + copyWithSyntaxHighlighting = 18, + cursorBlinking = 19, + cursorSmoothCaretAnimation = 20, + cursorStyle = 21, + cursorSurroundingLines = 22, + cursorSurroundingLinesStyle = 23, + cursorWidth = 24, + disableLayerHinting = 25, + disableMonospaceOptimizations = 26, + dragAndDrop = 27, + emptySelectionClipboard = 28, + extraEditorClassName = 29, + fastScrollSensitivity = 30, + find = 31, + fixedOverflowWidgets = 32, + folding = 33, + foldingStrategy = 34, + foldingHighlight = 35, + unfoldOnClickAfterEndOfLine = 36, + fontFamily = 37, + fontInfo = 38, + fontLigatures = 39, + fontSize = 40, + fontWeight = 41, + formatOnPaste = 42, + formatOnType = 43, + glyphMargin = 44, + gotoLocation = 45, + hideCursorInOverviewRuler = 46, + highlightActiveIndentGuide = 47, + hover = 48, + inDiffEditor = 49, + letterSpacing = 50, + lightbulb = 51, + lineDecorationsWidth = 52, + lineHeight = 53, + lineNumbers = 54, + lineNumbersMinChars = 55, + linkedEditing = 56, + links = 57, + matchBrackets = 58, + minimap = 59, + mouseStyle = 60, + mouseWheelScrollSensitivity = 61, + mouseWheelZoom = 62, + multiCursorMergeOverlapping = 63, + multiCursorModifier = 64, + multiCursorPaste = 65, + occurrencesHighlight = 66, + overviewRulerBorder = 67, + overviewRulerLanes = 68, + padding = 69, + parameterHints = 70, + peekWidgetDefaultFocus = 71, + definitionLinkOpensInPeek = 72, + quickSuggestions = 73, + quickSuggestionsDelay = 74, + readOnly = 75, + renameOnType = 76, + renderControlCharacters = 77, + renderIndentGuides = 78, + renderFinalNewline = 79, + renderLineHighlight = 80, + renderLineHighlightOnlyWhenFocus = 81, + renderValidationDecorations = 82, + renderWhitespace = 83, + revealHorizontalRightPadding = 84, + roundedSelection = 85, + rulers = 86, + scrollbar = 87, + scrollBeyondLastColumn = 88, + scrollBeyondLastLine = 89, + scrollPredominantAxis = 90, + selectionClipboard = 91, + selectionHighlight = 92, + selectOnLineNumbers = 93, + showFoldingControls = 94, + showUnused = 95, + snippetSuggestions = 96, + smartSelect = 97, + smoothScrolling = 98, + stickyTabStops = 99, + stopRenderingLineAfter = 100, + suggest = 101, + suggestFontSize = 102, + suggestLineHeight = 103, + suggestOnTriggerCharacters = 104, + suggestSelection = 105, + tabCompletion = 106, + tabIndex = 107, + unusualLineTerminators = 108, + useTabStops = 109, + wordSeparators = 110, + wordWrap = 111, + wordWrapBreakAfterCharacters = 112, + wordWrapBreakBeforeCharacters = 113, + wordWrapColumn = 114, + wordWrapOverride1 = 115, + wordWrapOverride2 = 116, + wrappingIndent = 117, + wrappingStrategy = 118, + showDeprecated = 119, + editorClassName = 120, + pixelRatio = 121, + tabFocusMode = 122, + layoutInfo = 123, + wrappingInfo = 124 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -3979,7 +4041,10 @@ declare namespace monaco.editor { autoIndent: IEditorOption; automaticLayout: IEditorOption; autoSurround: IEditorOption; + stickyTabStops: IEditorOption; codeLens: IEditorOption; + codeLensFontFamily: IEditorOption; + codeLensFontSize: IEditorOption; colorDecorators: IEditorOption; columnSelection: IEditorOption; comments: IEditorOption; @@ -4022,6 +4087,7 @@ declare namespace monaco.editor { lineHeight: IEditorOption; lineNumbers: IEditorOption; lineNumbersMinChars: IEditorOption; + linkedEditing: IEditorOption; links: IEditorOption; matchBrackets: IEditorOption; minimap: IEditorOption; @@ -4063,6 +4129,7 @@ declare namespace monaco.editor { showUnused: IEditorOption; showDeprecated: IEditorOption; snippetSuggestions: IEditorOption; + smartSelect: IEditorOption; smoothScrolling: IEditorOption; stopRenderingLineAfter: IEditorOption; suggest: IEditorOption; @@ -4079,7 +4146,8 @@ declare namespace monaco.editor { wordWrapBreakAfterCharacters: IEditorOption; wordWrapBreakBeforeCharacters: IEditorOption; wordWrapColumn: IEditorOption; - wordWrapMinified: IEditorOption; + wordWrapOverride1: IEditorOption; + wordWrapOverride2: IEditorOption; wrappingIndent: IEditorOption; wrappingStrategy: IEditorOption; editorClassName: IEditorOption; @@ -4676,9 +4744,13 @@ declare namespace monaco.editor { */ executeCommand(source: string | null | undefined, command: ICommand): void; /** - * Push an "undo stop" in the undo-redo stack. + * Create an "undo stop" in the undo-redo stack. */ pushUndoStop(): boolean; + /** + * Remove the "undo stop" in the undo-redo stack. + */ + popUndoStop(): boolean; /** * Execute edits on the editor. * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. @@ -4997,6 +5069,10 @@ declare namespace monaco.languages { * Tokenize a line given the state at the beginning of the line. */ tokenizeEncoded(line: string, state: IState): IEncodedLineTokens; + /** + * Tokenize a line given the state at the beginning of the line. + */ + tokenize?(line: string, state: IState): ILineTokens; } /** @@ -5040,9 +5116,9 @@ declare namespace monaco.languages { export function registerDocumentHighlightProvider(languageId: string, provider: DocumentHighlightProvider): IDisposable; /** - * Register an on type rename provider. + * Register an linked editing range provider. */ - export function registerOnTypeRenameProvider(languageId: string, provider: OnTypeRenameProvider): IDisposable; + export function registerLinkedEditingRangeProvider(languageId: string, provider: LinkedEditingRangeProvider): IDisposable; /** * Register a definition provider (used by e.g. go to definition). @@ -5786,18 +5862,30 @@ declare namespace monaco.languages { } /** - * The rename provider interface defines the contract between extensions and - * the live-rename feature. + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. */ - export interface OnTypeRenameProvider { - wordPattern?: RegExp; + export interface LinkedEditingRangeProvider { /** - * Provide a list of ranges that can be live-renamed together. + * Provide a list of ranges that can be edited together. */ - provideOnTypeRenameRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ - ranges: IRange[]; - wordPattern?: RegExp; - }>; + provideLinkedEditingRanges(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid contents. + */ + export interface LinkedEditingRanges { + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap + */ + ranges: IRange[]; + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + wordPattern?: RegExp; } /** @@ -6195,12 +6283,6 @@ declare namespace monaco.languages { needsConfirmation: boolean; label: string; description?: string; - iconPath?: { - id: string; - } | Uri | { - light: Uri; - dark: Uri; - }; } export interface WorkspaceFileEditOptions { @@ -6208,6 +6290,10 @@ declare namespace monaco.languages { ignoreIfNotExists?: boolean; ignoreIfExists?: boolean; recursive?: boolean; + copy?: boolean; + folder?: boolean; + skipTrashBin?: boolean; + maxSize?: number; } export interface WorkspaceFileEdit { diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 9da3b3d1..1344ea90 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -16,10 +16,12 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { isWindows, isLinux } from 'vs/base/common/platform'; export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable { const groups = menu.getActions(options); - const useAlternativeActions = ModifierKeyEmitter.getInstance().keyStatus.altKey; + const modifierKeyEmitter = ModifierKeyEmitter.getInstance(); + const useAlternativeActions = modifierKeyEmitter.keyStatus.altKey || ((isWindows || isLinux) && modifierKeyEmitter.keyStatus.shiftKey); fillInActions(groups, target, useAlternativeActions, isPrimaryGroup); return asDisposable(groups); } @@ -102,7 +104,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { let mouseOver = false; - let alternativeKeyDown = this._altKey.keyStatus.altKey; + let alternativeKeyDown = this._altKey.keyStatus.altKey || ((isWindows || isLinux) && this._altKey.keyStatus.shiftKey); const updateAltState = () => { const wantsAltCommand = mouseOver && alternativeKeyDown; @@ -116,7 +118,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { if (this._action.alt) { this._register(this._altKey.event(value => { - alternativeKeyDown = value.altKey; + alternativeKeyDown = value.altKey || ((isWindows || isLinux) && value.shiftKey); updateAltState(); })); } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index aa382fdd..c06a3511 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -95,7 +95,7 @@ export class MenuId { static readonly MenubarSwitchGroupMenu = new MenuId('MenubarSwitchGroupMenu'); static readonly MenubarTerminalMenu = new MenuId('MenubarTerminalMenu'); static readonly MenubarViewMenu = new MenuId('MenubarViewMenu'); - static readonly MenubarWebNavigationMenu = new MenuId('MenubarWebNavigationMenu'); + static readonly MenubarHomeMenu = new MenuId('MenubarHomeMenu'); static readonly OpenEditorsContext = new MenuId('OpenEditorsContext'); static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext'); static readonly SCMChangeContext = new MenuId('SCMChangeContext'); diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index a7e9a9ee..42f5fb33 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -57,9 +57,6 @@ export class BackupMainService implements IBackupMainService { // read empty workspaces backups first if (backups.emptyWorkspaceInfos) { this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaceInfos); - } else if (Array.isArray(backups.emptyWorkspaces)) { - // read legacy entries - this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaces.map(emptyWindow => ({ backupFolder: emptyWindow }))); } // read workspace backups @@ -67,8 +64,6 @@ export class BackupMainService implements IBackupMainService { try { if (Array.isArray(backups.rootURIWorkspaces)) { rootWorkspaces = backups.rootURIWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) }, remoteAuthority: workspace.remoteAuthority })); - } else if (Array.isArray(backups.rootWorkspaces)) { - rootWorkspaces = backups.rootWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } })); } } catch (e) { // ignore URI parsing exceptions @@ -81,18 +76,6 @@ export class BackupMainService implements IBackupMainService { try { if (Array.isArray(backups.folderURIWorkspaces)) { workspaceFolders = backups.folderURIWorkspaces.map(folder => URI.parse(folder)); - } else if (Array.isArray(backups.folderWorkspaces)) { - // migrate legacy folder paths - workspaceFolders = []; - for (const folderPath of backups.folderWorkspaces) { - const oldFolderHash = this.getLegacyFolderHash(folderPath); - const folderUri = URI.file(folderPath); - const newFolderHash = this.getFolderHash(folderUri); - if (newFolderHash !== oldFolderHash) { - await this.moveBackupFolder(this.getBackupPath(newFolderHash), this.getBackupPath(oldFolderHash)); - } - workspaceFolders.push(folderUri); - } } } catch (e) { // ignore URI parsing exceptions @@ -174,23 +157,6 @@ export class BackupMainService implements IBackupMainService { } } - private async moveBackupFolder(backupPath: string, moveFromPath: string): Promise { - - // Target exists: make sure to convert existing backups to empty window backups - if (await exists(backupPath)) { - await this.convertToEmptyWindowBackup(backupPath); - } - - // When we have data to migrate from, move it over to the target location - if (await exists(moveFromPath)) { - try { - await rename(moveFromPath, backupPath); - } catch (ex) { - this.logService.error(`Backup: Could not move backup folder to new location: ${ex.toString()}`); - } - } - } - unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void { const id = workspace.id; const index = this.workspaces.findIndex(workspace => workspace.workspace.id === id); @@ -475,8 +441,7 @@ export class BackupMainService implements IBackupMainService { return { rootURIWorkspaces: this.workspaces.map(workspace => ({ id: workspace.workspace.id, configURIPath: workspace.workspace.configPath.toString(), remoteAuthority: workspace.remoteAuthority })), folderURIWorkspaces: this.folders.map(folder => folder.toString()), - emptyWorkspaceInfos: this.emptyWindows, - emptyWorkspaces: this.emptyWindows.map(emptyWindow => emptyWindow.backupFolder) + emptyWorkspaceInfos: this.emptyWindows }; } @@ -496,8 +461,4 @@ export class BackupMainService implements IBackupMainService { return crypto.createHash('md5').update(key).digest('hex'); } - - protected getLegacyFolderHash(folderPath: string): string { - return crypto.createHash('md5').update(platform.isLinux ? folderPath : folderPath.toLowerCase()).digest('hex'); - } } diff --git a/src/vs/platform/backup/node/backup.ts b/src/vs/platform/backup/node/backup.ts index d40d01b1..b054be3e 100644 --- a/src/vs/platform/backup/node/backup.ts +++ b/src/vs/platform/backup/node/backup.ts @@ -9,11 +9,6 @@ export interface IBackupWorkspacesFormat { rootURIWorkspaces: ISerializedWorkspace[]; folderURIWorkspaces: string[]; emptyWorkspaceInfos: IEmptyWindowBackupInfo[]; - - // deprecated - folderWorkspaces?: string[]; // use folderURIWorkspaces instead - emptyWorkspaces?: string[]; - rootWorkspaces?: { id: string, configPath: string }[]; // use rootURIWorkspaces instead } export interface IEmptyWindowBackupInfo { diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 4db502cc..42e2636d 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -53,10 +53,6 @@ suite('BackupMainService', () => { getFolderHash(folderUri: URI): string { return super.getFolderHash(folderUri); } - - toLegacyBackupPath(folderPath: string): string { - return path.join(this.backupHome, super.getLegacyFolderHash(folderPath)); - } } function toWorkspace(path: string): IWorkspaceIdentifier { @@ -266,68 +262,6 @@ suite('BackupMainService', () => { assert.equal(1, fs.readdirSync(path.join(backupHome, emptyBackups[0].backupFolder!)).length); }); - suite('migrate path to URI', () => { - - test('migration folder path to URI makes sure to preserve existing backups', async () => { - let path1 = path.join(parentDir, 'folder1'); - let path2 = path.join(parentDir, 'FOLDER2'); - let uri1 = URI.file(path1); - let uri2 = URI.file(path2); - - if (!fs.existsSync(path1)) { - fs.mkdirSync(path1); - } - if (!fs.existsSync(path2)) { - fs.mkdirSync(path2); - } - const backupFolder1 = service.toLegacyBackupPath(path1); - if (!fs.existsSync(backupFolder1)) { - fs.mkdirSync(backupFolder1); - fs.mkdirSync(path.join(backupFolder1, Schemas.file)); - await pfs.writeFile(path.join(backupFolder1, Schemas.file, 'unsaved1.txt'), 'Legacy'); - } - const backupFolder2 = service.toLegacyBackupPath(path2); - if (!fs.existsSync(backupFolder2)) { - fs.mkdirSync(backupFolder2); - fs.mkdirSync(path.join(backupFolder2, Schemas.file)); - await pfs.writeFile(path.join(backupFolder2, Schemas.file, 'unsaved2.txt'), 'Legacy'); - } - - const workspacesJson = { rootWorkspaces: [], folderWorkspaces: [path1, path2], emptyWorkspaces: [] }; - await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)); - await service.initialize(); - const content = await pfs.readFile(backupWorkspacesPath, 'utf-8'); - const json = (JSON.parse(content)); - assert.deepEqual(json.folderURIWorkspaces, [uri1.toString(), uri2.toString()]); - const newBackupFolder1 = service.toBackupPath(uri1); - assert.ok(fs.existsSync(path.join(newBackupFolder1, Schemas.file, 'unsaved1.txt'))); - const newBackupFolder2 = service.toBackupPath(uri2); - assert.ok(fs.existsSync(path.join(newBackupFolder2, Schemas.file, 'unsaved2.txt'))); - }); - - test('migrate storage file', async () => { - let folderPath = path.join(parentDir, 'f1'); - ensureFolderExists(URI.file(folderPath)); - const backupFolderPath = service.toLegacyBackupPath(folderPath); - await createBackupFolder(backupFolderPath); - - let workspacePath = path.join(parentDir, 'f2.code-workspace'); - const workspace = toWorkspace(workspacePath); - await ensureWorkspaceExists(workspace); - - const workspacesJson = { rootWorkspaces: [{ id: workspace.id, configPath: workspacePath }], folderWorkspaces: [folderPath], emptyWorkspaces: [] }; - await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)); - await service.initialize(); - const content = await pfs.readFile(backupWorkspacesPath, 'utf-8'); - const json = (JSON.parse(content)); - assert.deepEqual(json.folderURIWorkspaces, [URI.file(folderPath).toString()]); - assert.deepEqual(json.rootURIWorkspaces, [{ id: workspace.id, configURIPath: URI.file(workspacePath).toString() }]); - - assertEqualUris(service.getWorkspaceBackups().map(window => window.workspace.configPath), [workspace.configPath]); - }); - }); - - suite('loadSync', () => { test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => { assertEqualUris(service.getFolderBackupPaths(), []); @@ -650,12 +584,12 @@ suite('BackupMainService', () => { const buffer = await pfs.readFile(backupWorkspacesPath, 'utf-8'); const json = (JSON.parse(buffer)); - assert.deepEqual(json.emptyWorkspaces, ['bar']); + assert.deepEqual(json.emptyWorkspaceInfos, [{ backupFolder: 'bar' }]); service.unregisterEmptyWindowBackupSync('bar'); const content = await pfs.readFile(backupWorkspacesPath, 'utf-8'); const json2 = (JSON.parse(content)); - assert.deepEqual(json2.emptyWorkspaces, []); + assert.deepEqual(json2.emptyWorkspaceInfos, []); }); test('should fail gracefully when removing a path that doesn\'t exist', async () => { diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 7f14056f..d5b333f7 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -114,7 +114,7 @@ export interface IConfigurationService { inspect(key: string, overrides?: IConfigurationOverrides): IConfigurationValue; - reloadConfiguration(folder?: IWorkspaceFolder): Promise; + reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise; keys(): { default: string[]; diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 3a7eaf96..07680c0f 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -478,6 +478,9 @@ const configurationRegistry = new ConfigurationRegistry(); Registry.add(Extensions.Configuration, configurationRegistry); export function validateProperty(property: string): string | null { + if (!property.trim()) { + return nls.localize('config.property.empty', "Cannot register an empty property"); + } if (OVERRIDE_PROPERTY_PATTERN.test(property)) { return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property); } diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index 1ff040dc..c743e36b 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -7,9 +7,10 @@ import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { URI } from 'vs/base/common/uri'; -import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { join } from 'vs/base/common/path'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; suite('ConfigurationModel', () => { diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index b4d8e42c..21320c0d 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; +import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { TernarySearchTree } from 'vs/base/common/map'; import { distinct } from 'vs/base/common/objects'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -90,10 +92,9 @@ class NullContext extends Context { } class ConfigAwareContextValuesContainer extends Context { - private static readonly _keyPrefix = 'config.'; - private readonly _values = new Map(); + private readonly _values = TernarySearchTree.forConfigKeys(); private readonly _listener: IDisposable; constructor( @@ -106,18 +107,26 @@ class ConfigAwareContextValuesContainer extends Context { this._listener = this._configurationService.onDidChangeConfiguration(event => { if (event.source === ConfigurationTarget.DEFAULT) { // new setting, reset everything - const allKeys = Array.from(this._values.keys()); + const allKeys = Array.from(Iterable.map(this._values, ([k]) => k)); this._values.clear(); emitter.fire(new ArrayContextKeyChangeEvent(allKeys)); } else { const changedKeys: string[] = []; for (const configKey of event.affectedKeys) { const contextKey = `config.${configKey}`; + + const cachedItems = this._values.findSuperstr(contextKey); + if (cachedItems !== undefined) { + changedKeys.push(...Iterable.map(cachedItems, ([key]) => key)); + this._values.deleteSuperstr(contextKey); + } + if (this._values.has(contextKey)) { - this._values.delete(contextKey); changedKeys.push(contextKey); + this._values.delete(contextKey); } } + emitter.fire(new ArrayContextKeyChangeEvent(changedKeys)); } }); @@ -149,6 +158,8 @@ class ConfigAwareContextValuesContainer extends Context { default: if (Array.isArray(configValue)) { value = JSON.stringify(configValue); + } else { + value = configValue; } } @@ -405,6 +416,9 @@ class ScopedContextKeyService extends AbstractContextKeyService { if (domNode) { this._domNode = domNode; + if (this._domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) { + console.error('Element already has context attribute'); + } this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId)); } } diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index dad27b17..ea7c8387 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -6,8 +6,9 @@ import { Event } from 'vs/base/common/event'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { isMacintosh, isLinux, isWindows, isWeb } from 'vs/base/common/platform'; +import { userAgent, isMacintosh, isLinux, isWindows, isWeb } from 'vs/base/common/platform'; +let _userAgent = userAgent || ''; const STATIC_VALUES = new Map(); STATIC_VALUES.set('false', false); STATIC_VALUES.set('true', true); @@ -16,6 +17,11 @@ STATIC_VALUES.set('isLinux', isLinux); STATIC_VALUES.set('isWindows', isWindows); STATIC_VALUES.set('isWeb', isWeb); STATIC_VALUES.set('isMacNative', isMacintosh && !isWeb); +STATIC_VALUES.set('isEdge', _userAgent.indexOf('Edg/') >= 0); +STATIC_VALUES.set('isFirefox', _userAgent.indexOf('Firefox') >= 0); +STATIC_VALUES.set('isChrome', _userAgent.indexOf('Chrome') >= 0); +STATIC_VALUES.set('isSafari', _userAgent.indexOf('Safari') >= 0); +STATIC_VALUES.set('isIPad', _userAgent.indexOf('iPad') >= 0); const hasOwnProperty = Object.prototype.hasOwnProperty; @@ -32,6 +38,10 @@ export const enum ContextKeyExprType { Or = 9, In = 10, NotIn = 11, + Greater = 12, + GreaterEquals = 13, + Smaller = 14, + SmallerEquals = 15, } export interface IContextKeyExprMapper { @@ -39,6 +49,10 @@ export interface IContextKeyExprMapper { mapNot(key: string): ContextKeyExpression; mapEquals(key: string, value: any): ContextKeyExpression; mapNotEquals(key: string, value: any): ContextKeyExpression; + mapGreater(key: string, value: any): ContextKeyExpression; + mapGreaterEquals(key: string, value: any): ContextKeyExpression; + mapSmaller(key: string, value: any): ContextKeyExpression; + mapSmallerEquals(key: string, value: any): ContextKeyExpression; mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr; mapIn(key: string, valueKey: string): ContextKeyInExpr; } @@ -57,7 +71,9 @@ export interface IContextKeyExpression { export type ContextKeyExpression = ( ContextKeyFalseExpr | ContextKeyTrueExpr | ContextKeyDefinedExpr | ContextKeyNotExpr | ContextKeyEqualsExpr | ContextKeyNotEqualsExpr | ContextKeyRegexExpr - | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr | ContextKeyNotInExpr + | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr + | ContextKeyNotInExpr | ContextKeyGreaterExpr | ContextKeyGreaterEqualsExpr + | ContextKeySmallerExpr | ContextKeySmallerEqualsExpr ); export abstract class ContextKeyExpr { @@ -102,6 +118,14 @@ export abstract class ContextKeyExpr { return ContextKeyOrExpr.create(expr); } + public static greater(key: string, value: any): ContextKeyExpression { + return ContextKeyGreaterExpr.create(key, value); + } + + public static less(key: string, value: any): ContextKeyExpression { + return ContextKeySmallerExpr.create(key, value); + } + public static deserialize(serialized: string | null | undefined, strict: boolean = false): ContextKeyExpression | undefined { if (!serialized) { return undefined; @@ -143,6 +167,26 @@ export abstract class ContextKeyExpr { return ContextKeyInExpr.create(pieces[0].trim(), pieces[1].trim()); } + if (/^[^<=>]+>=[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('>='); + return ContextKeyGreaterEqualsExpr.create(pieces[0].trim(), pieces[1].trim()); + } + + if (/^[^<=>]+>[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('>'); + return ContextKeyGreaterExpr.create(pieces[0].trim(), pieces[1].trim()); + } + + if (/^[^<=>]+<=[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('<='); + return ContextKeySmallerEqualsExpr.create(pieces[0].trim(), pieces[1].trim()); + } + + if (/^[^<=>]+<[^<=>]+$/.test(serializedOne)) { + const pieces = serializedOne.split('<'); + return ContextKeySmallerExpr.create(pieces[0].trim(), pieces[1].trim()); + } + if (/^\!\s*/.test(serializedOne)) { return ContextKeyNotExpr.create(serializedOne.substr(1).trim()); } @@ -302,13 +346,7 @@ export class ContextKeyDefinedExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - return 0; + return cmp1(this.key, other.key); } public equals(other: ContextKeyExpression): boolean { @@ -362,19 +400,7 @@ export class ContextKeyEqualsExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - if (this.value < other.value) { - return -1; - } - if (this.value > other.value) { - return 1; - } - return 0; + return cmp2(this.key, this.value, other.key, other.value); } public equals(other: ContextKeyExpression): boolean { @@ -391,7 +417,7 @@ export class ContextKeyEqualsExpr implements IContextKeyExpression { } public serialize(): string { - return this.key + ' == \'' + this.value + '\''; + return `${this.key} == '${this.value}'`; } public keys(): string[] { @@ -422,19 +448,7 @@ export class ContextKeyInExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - if (this.valueKey < other.valueKey) { - return -1; - } - if (this.valueKey > other.valueKey) { - return 1; - } - return 0; + return cmp2(this.key, this.valueKey, other.key, other.valueKey); } public equals(other: ContextKeyExpression): boolean { @@ -460,7 +474,7 @@ export class ContextKeyInExpr implements IContextKeyExpression { } public serialize(): string { - return this.key + ' in \'' + this.valueKey + '\''; + return `${this.key} in '${this.valueKey}'`; } public keys(): string[] { @@ -549,19 +563,7 @@ export class ContextKeyNotEqualsExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - if (this.value < other.value) { - return -1; - } - if (this.value > other.value) { - return 1; - } - return 0; + return cmp2(this.key, this.value, other.key, other.value); } public equals(other: ContextKeyExpression): boolean { @@ -578,7 +580,7 @@ export class ContextKeyNotEqualsExpr implements IContextKeyExpression { } public serialize(): string { - return this.key + ' != \'' + this.value + '\''; + return `${this.key} != '${this.value}'`; } public keys(): string[] { @@ -613,13 +615,7 @@ export class ContextKeyNotExpr implements IContextKeyExpression { if (other.type !== this.type) { return this.type - other.type; } - if (this.key < other.key) { - return -1; - } - if (this.key > other.key) { - return 1; - } - return 0; + return cmp1(this.key, other.key); } public equals(other: ContextKeyExpression): boolean { @@ -634,7 +630,7 @@ export class ContextKeyNotExpr implements IContextKeyExpression { } public serialize(): string { - return '!' + this.key; + return `!${this.key}`; } public keys(): string[] { @@ -650,6 +646,200 @@ export class ContextKeyNotExpr implements IContextKeyExpression { } } +export class ContextKeyGreaterExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeyGreaterExpr(key, value); + } + + public readonly type = ContextKeyExprType.Greater; + + private constructor( + private readonly key: string, + private readonly value: any + ) { } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) > parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} > ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapGreater(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeySmallerEqualsExpr.create(this.key, this.value); + } +} + +export class ContextKeyGreaterEqualsExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeyGreaterEqualsExpr(key, value); + } + + public readonly type = ContextKeyExprType.GreaterEquals; + + private constructor( + private readonly key: string, + private readonly value: any + ) { } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) >= parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} >= ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapGreaterEquals(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeySmallerExpr.create(this.key, this.value); + } +} + +export class ContextKeySmallerExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeySmallerExpr(key, value); + } + + public readonly type = ContextKeyExprType.Smaller; + + private constructor( + private readonly key: string, + private readonly value: any + ) { + } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) < parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} < ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapSmaller(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeyGreaterEqualsExpr.create(this.key, this.value); + } +} + +export class ContextKeySmallerEqualsExpr implements IContextKeyExpression { + + public static create(key: string, value: any): ContextKeyExpression { + return new ContextKeySmallerEqualsExpr(key, value); + } + + public readonly type = ContextKeyExprType.SmallerEquals; + + private constructor( + private readonly key: string, + private readonly value: any + ) { + } + + public cmp(other: ContextKeyExpression): number { + if (other.type !== this.type) { + return this.type - other.type; + } + return cmp2(this.key, this.value, other.key, other.value); + } + + public equals(other: ContextKeyExpression): boolean { + if (other.type === this.type) { + return (this.key === other.key && this.value === other.value); + } + return false; + } + + public evaluate(context: IContext): boolean { + return (parseFloat(context.getValue(this.key)) <= parseFloat(this.value)); + } + + public serialize(): string { + return `${this.key} <= ${this.value}`; + } + + public keys(): string[] { + return [this.key]; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression { + return mapFnc.mapSmallerEquals(this.key, this.value); + } + + public negate(): ContextKeyExpression { + return ContextKeyGreaterExpr.create(this.key, this.value); + } +} + export class ContextKeyRegexExpr implements IContextKeyExpression { public static create(key: string, regexp: RegExp | null): ContextKeyRegexExpr { @@ -1143,3 +1333,29 @@ export interface IContextKeyService { } export const SET_CONTEXT_COMMAND_ID = 'setContext'; + +function cmp1(key1: string, key2: string): number { + if (key1 < key2) { + return -1; + } + if (key1 > key2) { + return 1; + } + return 0; +} + +function cmp2(key1: string, value1: any, key2: string, value2: any): number { + if (key1 < key2) { + return -1; + } + if (key1 > key2) { + return 1; + } + if (value1 < value2) { + return -1; + } + if (value1 > value2) { + return 1; + } + return 0; +} diff --git a/src/vs/platform/contextkey/test/common/contextkey.test.ts b/src/vs/platform/contextkey/test/common/contextkey.test.ts index 2912df4b..91a548be 100644 --- a/src/vs/platform/contextkey/test/common/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/common/contextkey.test.ts @@ -186,4 +186,84 @@ suite('ContextKeyExpr', () => { ); assert.equal(actual!.equals(expected!), true); }); + + test('Greater, GreaterEquals, Smaller, SmallerEquals evaluate', () => { + function checkEvaluate(expr: string, ctx: any, expected: any): void { + const _expr = ContextKeyExpr.deserialize(expr)!; + assert.equal(_expr.evaluate(createContext(ctx)), expected); + } + + checkEvaluate('a>1', {}, false); + checkEvaluate('a>1', { a: 0 }, false); + checkEvaluate('a>1', { a: 1 }, false); + checkEvaluate('a>1', { a: 2 }, true); + checkEvaluate('a>1', { a: '0' }, false); + checkEvaluate('a>1', { a: '1' }, false); + checkEvaluate('a>1', { a: '2' }, true); + checkEvaluate('a>1', { a: 'a' }, false); + + checkEvaluate('a>10', { a: 2 }, false); + checkEvaluate('a>10', { a: 11 }, true); + checkEvaluate('a>10', { a: '11' }, true); + checkEvaluate('a>10', { a: '2' }, false); + checkEvaluate('a>10', { a: '11' }, true); + + checkEvaluate('a>1.1', { a: 1 }, false); + checkEvaluate('a>1.1', { a: 2 }, true); + checkEvaluate('a>1.1', { a: 11 }, true); + checkEvaluate('a>1.1', { a: '1.1' }, false); + checkEvaluate('a>1.1', { a: '2' }, true); + checkEvaluate('a>1.1', { a: '11' }, true); + + checkEvaluate('a>b', { a: 'b' }, false); + checkEvaluate('a>b', { a: 'c' }, false); + checkEvaluate('a>b', { a: 1000 }, false); + + checkEvaluate('a >= 2', { a: '1' }, false); + checkEvaluate('a >= 2', { a: '2' }, true); + checkEvaluate('a >= 2', { a: '3' }, true); + + checkEvaluate('a < 2', { a: '1' }, true); + checkEvaluate('a < 2', { a: '2' }, false); + checkEvaluate('a < 2', { a: '3' }, false); + + checkEvaluate('a <= 2', { a: '1' }, true); + checkEvaluate('a <= 2', { a: '2' }, true); + checkEvaluate('a <= 2', { a: '3' }, false); + }); + + test('Greater, GreaterEquals, Smaller, SmallerEquals negate', () => { + function checkNegate(expr: string, expected: string): void { + const a = ContextKeyExpr.deserialize(expr)!; + const b = a.negate(); + assert.equal(b.serialize(), expected); + } + + checkNegate('a>1', 'a <= 1'); + checkNegate('a>1.1', 'a <= 1.1'); + checkNegate('a>b', 'a <= b'); + + checkNegate('a>=1', 'a < 1'); + checkNegate('a>=1.1', 'a < 1.1'); + checkNegate('a>=b', 'a < b'); + + checkNegate('a<1', 'a >= 1'); + checkNegate('a<1.1', 'a >= 1.1'); + checkNegate('a= b'); + + checkNegate('a<=1', 'a > 1'); + checkNegate('a<=1.1', 'a > 1.1'); + checkNegate('a<=b', 'a > b'); + }); + + test('issue #111899: context keys can use `<` or `>` ', () => { + const actual = ContextKeyExpr.deserialize('editorTextFocus && vim.active && vim.use')!; + assert.ok(actual.equals( + ContextKeyExpr.and( + ContextKeyExpr.has('editorTextFocus'), + ContextKeyExpr.has('vim.active'), + ContextKeyExpr.has('vim.use'), + )! + )); + }); }); diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index d307c292..1788290c 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -55,6 +55,7 @@ export class ContextMenuHandler { getAnchor: () => delegate.getAnchor(), canRelayout: false, anchorAlignment: delegate.anchorAlignment, + anchorAxisAlignment: delegate.anchorAxisAlignment, render: (container) => { let className = delegate.getMenuClassName ? delegate.getMenuClassName() : ''; @@ -79,7 +80,7 @@ export class ContextMenuHandler { const menuDisposables = new DisposableStore(); const actionRunner = delegate.actionRunner || new ActionRunner(); - actionRunner.onDidBeforeRun(this.onActionRun, this, menuDisposables); + actionRunner.onBeforeRun(this.onActionRun, this, menuDisposables); actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables); menu = new Menu(container, actions, { actionViewItemProvider: delegate.getActionViewItem, diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts index dcba62aa..b33d652c 100644 --- a/src/vs/platform/contextview/browser/contextView.ts +++ b/src/vs/platform/contextview/browser/contextView.ts @@ -6,7 +6,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; -import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, AnchorAxisAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; export const IContextViewService = createDecorator('contextViewService'); @@ -31,6 +31,7 @@ export interface IContextViewDelegate { onHide?(data?: any): void; focus?(): void; anchorAlignment?: AnchorAlignment; + anchorAxisAlignment?: AnchorAxisAlignment; } export const IContextMenuService = createDecorator('contextMenuService'); diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index b3b4815f..2fe576f1 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -18,6 +18,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Iterable } from 'vs/base/common/iterator'; import { Schemas } from 'vs/base/common/network'; +import { ByteSize } from 'vs/platform/files/common/files'; export const ID = 'diagnosticsService'; export const IDiagnosticsService = createDecorator(ID); @@ -163,12 +164,10 @@ function asSortedItems(items: Map): WorkspaceStatItem[] { } export function getMachineInfo(): IMachineInfo { - const MB = 1024 * 1024; - const GB = 1024 * MB; const machineInfo: IMachineInfo = { os: `${osLib.type()} ${osLib.arch()} ${osLib.release()}`, - memory: `${(osLib.totalmem() / GB).toFixed(2)}GB (${(osLib.freemem() / GB).toFixed(2)}GB free)`, + memory: `${(osLib.totalmem() / ByteSize.GB).toFixed(2)}GB (${(osLib.freemem() / ByteSize.GB).toFixed(2)}GB free)`, vmHint: `${Math.round((virtualMachineHint.value() * 100))}%`, }; @@ -238,9 +237,6 @@ export class DiagnosticsService implements IDiagnosticsService { } private formatEnvironment(info: IMainProcessInfo): string { - const MB = 1024 * 1024; - const GB = 1024 * MB; - const output: string[] = []; output.push(`Version: ${product.nameShort} ${product.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`); output.push(`OS Version: ${osLib.type()} ${osLib.arch()} ${osLib.release()}`); @@ -248,7 +244,7 @@ export class DiagnosticsService implements IDiagnosticsService { if (cpus && cpus.length > 0) { output.push(`CPUs: ${cpus[0].model} (${cpus.length} x ${cpus[0].speed})`); } - output.push(`Memory (System): ${(osLib.totalmem() / GB).toFixed(2)}GB (${(osLib.freemem() / GB).toFixed(2)}GB free)`); + output.push(`Memory (System): ${(osLib.totalmem() / ByteSize.GB).toFixed(2)}GB (${(osLib.freemem() / ByteSize.GB).toFixed(2)}GB free)`); if (!isWindows) { output.push(`Load (avg): ${osLib.loadavg().map(l => Math.round(l)).join(', ')}`); // only provided on Linux/macOS } @@ -493,8 +489,6 @@ export class DiagnosticsService implements IDiagnosticsService { private formatProcessItem(mainPid: number, mapPidToWindowTitle: Map, output: string[], item: ProcessItem, indent: number): void { const isRoot = (indent === 0); - const MB = 1024 * 1024; - // Format name with indent let name: string; if (isRoot) { @@ -508,7 +502,7 @@ export class DiagnosticsService implements IDiagnosticsService { } const memory = process.platform === 'win32' ? item.mem : (osLib.totalmem() * (item.mem / 100)); - output.push(`${item.load.toFixed(0).padStart(5, ' ')}\t${(memory / MB).toFixed(0).padStart(6, ' ')}\t${item.pid.toFixed(0).padStart(6, ' ')}\t${name}`); + output.push(`${item.load.toFixed(0).padStart(5, ' ')}\t${(memory / ByteSize.MB).toFixed(0).padStart(6, ' ')}\t${item.pid.toFixed(0).padStart(6, ' ')}\t${name}`); // Recurse into children if any if (Array.isArray(item.children)) { diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index 127c8fa9..8f7bb194 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -22,6 +22,29 @@ export interface ICheckbox { checked?: boolean; } +export interface IConfirmDialogArgs { + confirmation: IConfirmation; +} + +export interface IShowDialogArgs { + severity: Severity; + message: string; + buttons: string[]; + options?: IDialogOptions; +} + +export interface IInputDialogArgs extends IShowDialogArgs { + inputs: IInput[], +} + +export interface IDialog { + confirmArgs?: IConfirmDialogArgs; + showArgs?: IShowDialogArgs; + inputArgs?: IInputDialogArgs; +} + +export type IDialogResult = IConfirmationResult | IInputResult | IShowResult; + export interface IConfirmation { title?: string; type?: DialogType; @@ -166,6 +189,40 @@ export interface IInput { value?: string; } +/** + * A handler to bring up modal dialogs. + */ +export interface IDialogHandler { + /** + * Ask the user for confirmation with a modal dialog. + */ + confirm(confirmation: IConfirmation): Promise; + + /** + * Present a modal dialog to the user. + * + * @returns A promise with the selected choice index. If the user refused to choose, + * then a promise with index of `cancelId` option is returned. If there is no such + * option then promise with index `0` is returned. + */ + show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise; + + /** + * Present a modal dialog to the user asking for input. + * + * @returns A promise with the selected choice index. If the user refused to choose, + * then a promise with index of `cancelId` option is returned. If there is no such + * option then promise with index `0` is returned. In addition, the values for the + * inputs are returned as well. + */ + input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise; + + /** + * Present the about dialog to the user. + */ + about(): Promise; +} + /** * A service to bring up modal dialogs. * @@ -218,20 +275,23 @@ export interface IFileDialogService { /** * The default path for a new file based on previously used files. * @param schemeFilter The scheme of the file path. If no filter given, the scheme of the current window is used. + * Falls back to user home in the absence of enough information to find a better URI. */ - defaultFilePath(schemeFilter?: string): URI | undefined; + defaultFilePath(schemeFilter?: string): Promise; /** * The default path for a new folder based on previously used folders. * @param schemeFilter The scheme of the folder path. If no filter given, the scheme of the current window is used. + * Falls back to user home in the absence of enough information to find a better URI. */ - defaultFolderPath(schemeFilter?: string): URI | undefined; + defaultFolderPath(schemeFilter?: string): Promise; /** * The default path for a new workspace based on previously used workspaces. * @param schemeFilter The scheme of the workspace path. If no filter given, the scheme of the current window is used. + * Falls back to user home in the absence of enough information to find a better URI. */ - defaultWorkspacePath(schemeFilter?: string, filename?: string): URI | undefined; + defaultWorkspacePath(schemeFilter?: string, filename?: string): Promise; /** * Shows a file-folder selection dialog and opens the selected entry. diff --git a/src/vs/editor/contrib/rename/media/onTypeRename.css b/src/vs/platform/display/common/displayMainService.ts similarity index 68% rename from src/vs/editor/contrib/rename/media/onTypeRename.css rename to src/vs/platform/display/common/displayMainService.ts index 16bb0178..44d79f8d 100644 --- a/src/vs/editor/contrib/rename/media/onTypeRename.css +++ b/src/vs/platform/display/common/displayMainService.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .on-type-rename-decoration { - border-left: 1px solid transparent; - /* So border can be transparent */ - background-clip: padding-box; +import { Event } from 'vs/base/common/event'; + +export interface IDisplayMainService { + readonly _serviceBrand: undefined; + readonly onDidDisplayChanged: Event; } diff --git a/src/vs/platform/display/electron-main/displayMainService.ts b/src/vs/platform/display/electron-main/displayMainService.ts new file mode 100644 index 00000000..e360fc48 --- /dev/null +++ b/src/vs/platform/display/electron-main/displayMainService.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisplayMainService as ICommonDisplayMainService } from 'vs/platform/display/common/displayMainService'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { app, Display, screen } from 'electron'; +import { RunOnceScheduler } from 'vs/base/common/async'; + +export const IDisplayMainService = createDecorator('displayMainService'); + +export interface IDisplayMainService extends ICommonDisplayMainService { } + +export class DisplayMainService extends Disposable implements ICommonDisplayMainService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidDisplayChanged = this._register(new Emitter()); + readonly onDidDisplayChanged = this._onDidDisplayChanged.event; + + constructor() { + super(); + + const displayChangedScheduler = this._register(new RunOnceScheduler(() => { + this._onDidDisplayChanged.fire(); + }, 100)); + + app.whenReady().then(() => { + + const displayChangedListener = (event: Event, display: Display, changedMetrics?: string[]) => { + displayChangedScheduler.schedule(); + }; + + screen.on('display-metrics-changed', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-metrics-changed', displayChangedListener))); + + screen.on('display-added', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-added', displayChangedListener))); + + screen.on('display-removed', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-removed', displayChangedListener))); + }); + } +} diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 409bb7e1..e719e129 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -72,12 +72,13 @@ export interface NativeParsedArgs { 'driver'?: string; 'driver-verbose'?: boolean; 'remote'?: string; - 'disable-user-env-probe'?: boolean; 'force'?: boolean; 'do-not-sync'?: boolean; 'force-user-env'?: boolean; + 'force-disable-user-env'?: boolean; 'sync'?: 'on' | 'off'; '__sandbox'?: boolean; + 'logsPath'?: string; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 21b4d719..f4b34454 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -119,7 +119,7 @@ export interface INativeEnvironmentService extends IEnvironmentService { sharedIPCHandle: string; // --- Extensions - extensionsPath?: string; + extensionsPath: string; extensionsDownloadPath: string; builtinExtensionsPath: string; diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 8240ed28..35b09d89 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -19,6 +19,9 @@ export const IEnvironmentMainService = createDecorator( */ export interface IEnvironmentMainService extends INativeEnvironmentService { + // --- NLS cache path + cachedLanguagesPath: string; + // --- backup paths backupHome: string; backupWorkspacesPath: string; @@ -35,7 +38,10 @@ export interface IEnvironmentMainService extends INativeEnvironmentService { disableUpdates: boolean; } -export class EnvironmentMainService extends NativeEnvironmentService { +export class EnvironmentMainService extends NativeEnvironmentService implements IEnvironmentMainService { + + @memoize + get cachedLanguagesPath(): string { return join(this.userDataPath, 'clp'); } @memoize get backupHome(): string { return join(this.userDataPath, 'Backups'); } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 149e6ffb..7263bcbf 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -43,8 +43,6 @@ export const OPTIONS: OptionDescriptions> = { 'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") }, 'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") }, 'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") }, - 'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") }, - 'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, 'wait': { type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") }, 'waitMarkerFilePath': { type: 'string' }, 'locale': { type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") }, @@ -57,7 +55,7 @@ export const OPTIONS: OptionDescriptions> = { 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") }, - 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id[@version] | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts. The identifier of an extension is always `${publisher}.${name}`. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.") }, + 'install-extension': { type: 'string[]', cat: 'e', args: 'extension-id[@version] | path-to-vsix', description: localize('installExtension', "Installs or updates the extension. The identifier of an extension is always `${publisher}.${name}`. Use `--force` argument to update to latest version. To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.") }, 'uninstall-extension': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") }, 'enable-proposed-api': { type: 'string[]', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, @@ -79,6 +77,9 @@ export const OPTIONS: OptionDescriptions> = { 'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, 'remote': { type: 'string' }, + 'folder-uri': { type: 'string[]', cat: 'o', args: 'uri' }, + 'file-uri': { type: 'string[]', cat: 'o', args: 'uri' }, + 'locate-extension': { type: 'string[]' }, 'extensionDevelopmentPath': { type: 'string[]' }, 'extensionTestsPath': { type: 'string' }, @@ -96,7 +97,6 @@ export const OPTIONS: OptionDescriptions> = { 'disable-crash-reporter': { type: 'boolean' }, 'crash-reporter-directory': { type: 'string' }, 'crash-reporter-id': { type: 'string' }, - 'disable-user-env-probe': { type: 'boolean' }, 'skip-add-to-recently-opened': { type: 'boolean' }, 'unity-launch': { type: 'boolean' }, 'open-url': { type: 'boolean' }, @@ -110,8 +110,10 @@ export const OPTIONS: OptionDescriptions> = { 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, 'force-user-env': { type: 'boolean' }, + 'force-disable-user-env': { type: 'boolean' }, 'open-devtools': { type: 'boolean' }, '__sandbox': { type: 'boolean' }, + 'logsPath': { type: 'string' }, // chromium flags 'no-proxy-server': { type: 'boolean' }, diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index a3c16f98..3ace20cf 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -52,7 +52,7 @@ export function parseMainProcessArgv(processArgv: string[]): NativeParsedArgs { } // If called from CLI, don't report warnings as they are already reported. - let reportWarnings = !process.env['VSCODE_CLI']; + const reportWarnings = !isLaunchedFromCli(process.env); return parseAndValidate(args, reportWarnings); } @@ -60,7 +60,7 @@ export function parseMainProcessArgv(processArgv: string[]): NativeParsedArgs { * Use this to parse raw code CLI process.argv such as: `Electron cli.js . --verbose --wait` */ export function parseCLIProcessArgv(processArgv: string[]): NativeParsedArgs { - let [, , ...args] = processArgv; // remove the first non-option argument: it's always the app location + const [, , ...args] = processArgv; // remove the first non-option argument: it's always the app location return parseAndValidate(args, true); } @@ -78,3 +78,7 @@ export function addArg(argv: string[], ...args: string[]): string[] { return argv; } + +export function isLaunchedFromCli(env: NodeJS.ProcessEnv): boolean { + return env['VSCODE_CLI'] === '1'; +} diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 80f68fb1..cef26362 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import * as paths from 'vs/base/node/paths'; -import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as resources from 'vs/base/common/resources'; import { memoize } from 'vs/base/common/decorators'; @@ -204,12 +204,11 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; } constructor(protected _args: NativeParsedArgs) { - if (!process.env['VSCODE_LOGS']) { + if (!_args.logsPath) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); - process.env['VSCODE_LOGS'] = path.join(this.userDataPath, 'logs', key); + _args.logsPath = path.join(this.userDataPath, 'logs', key); } - - this.logsPath = process.env['VSCODE_LOGS']!; + this.logsPath = _args.logsPath; } } @@ -246,5 +245,5 @@ export function parsePathArg(arg: string | undefined, process: NodeJS.Process): } export function parseUserDataDir(args: NativeParsedArgs, process: NodeJS.Process): string { - return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath(process.platform)); + return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath()); } diff --git a/src/vs/code/test/node/argv.test.ts b/src/vs/platform/environment/test/node/argv.test.ts similarity index 99% rename from src/vs/code/test/node/argv.test.ts rename to src/vs/platform/environment/test/node/argv.test.ts index 79ce7d22..61a4b4f2 100644 --- a/src/vs/code/test/node/argv.test.ts +++ b/src/vs/platform/environment/test/node/argv.test.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { formatOptions, Option } from 'vs/platform/environment/node/argv'; import { addArg } from 'vs/platform/environment/node/argvHelper'; diff --git a/src/vs/platform/environment/test/node/nativeModules.test.ts b/src/vs/platform/environment/test/node/nativeModules.test.ts new file mode 100644 index 00000000..b64282f6 --- /dev/null +++ b/src/vs/platform/environment/test/node/nativeModules.test.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; + +function testErrorMessage(module: string): string { + return `Unable to load "${module}" dependency. It was probably not compiled for the right operating system architecture or had missing build tools.`; +} + +suite('Native Modules (all platforms)', () => { + + test('native-is-elevated', async () => { + const isElevated = await import('native-is-elevated'); + assert.ok(typeof isElevated === 'function', testErrorMessage('native-is-elevated ')); + }); + + test('native-keymap', async () => { + const keyMap = await import('native-keymap'); + assert.ok(typeof keyMap.getCurrentKeyboardLayout === 'function', testErrorMessage('native-keymap')); + }); + + test('native-watchdog', async () => { + const watchDog = await import('native-watchdog'); + assert.ok(typeof watchDog.start === 'function', testErrorMessage('native-watchdog')); + }); + + test('node-pty', async () => { + const nodePty = await import('node-pty'); + assert.ok(typeof nodePty.spawn === 'function', testErrorMessage('node-pty')); + }); + + test('spdlog', async () => { + const spdlog = await import('spdlog'); + assert.ok(typeof spdlog.createRotatingLogger === 'function', testErrorMessage('spdlog')); + }); + + test('v8-inspect-profiler', async () => { + const profiler = await import('v8-inspect-profiler'); + assert.ok(typeof profiler.startProfiling === 'function', testErrorMessage('v8-inspect-profiler')); + }); + + test('vscode-nsfw', async () => { + const nsfWatcher = await import('vscode-nsfw'); + assert.ok(typeof nsfWatcher === 'function', testErrorMessage('vscode-nsfw')); + }); + + test('vscode-sqlite3', async () => { + const sqlite3 = await import('vscode-sqlite3'); + assert.ok(typeof sqlite3.Database === 'function', testErrorMessage('vscode-sqlite3')); + }); +}); + +(!isMacintosh ? suite.skip : suite)('Native Modules (macOS)', () => { + + test('chokidar (fsevents)', async () => { + const chokidar = await import('chokidar'); + const watcher = chokidar.watch(__dirname); + assert.ok(watcher.options.useFsEvents, testErrorMessage('chokidar (fsevents)')); + + return watcher.close(); + }); +}); + +(!isWindows ? suite.skip : suite)('Native Modules (Windows)', () => { + + test('windows-mutex', async () => { + const mutex = await import('windows-mutex'); + assert.ok(mutex && typeof mutex.isActive === 'function', testErrorMessage('windows-mutex')); + assert.ok(typeof mutex.isActive === 'function', testErrorMessage('windows-mutex')); + }); + + test('windows-foreground-love', async () => { + const foregroundLove = await import('windows-foreground-love'); + assert.ok(typeof foregroundLove.allowSetForegroundWindow === 'function', testErrorMessage('windows-foreground-love')); + }); + + test('windows-process-tree', async () => { + const processTree = await import('windows-process-tree'); + assert.ok(typeof processTree.getProcessTree === 'function', testErrorMessage('windows-process-tree')); + }); + + test('vscode-windows-registry', async () => { + const windowsRegistry = await import('vscode-windows-registry'); + assert.ok(typeof windowsRegistry.GetStringRegKey === 'function', testErrorMessage('vscode-windows-registry')); + }); + + test('vscode-windows-ca-certs', async () => { + // @ts-ignore Windows only + const windowsCerts = await import('vscode-windows-ca-certs'); + const store = windowsCerts(); + assert.ok(windowsCerts, testErrorMessage('vscode-windows-ca-certs')); + let certCount = 0; + try { + while (store.next()) { + certCount++; + } + } finally { + store.done(); + } + assert(certCount > 0); + }); +}); diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index afb3cf6e..7b0a60ee 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -7,7 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionIdentifier, IGlobalExtensionEnablementService, DISABLED_EXTENSIONS_STORAGE_PATH } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { isUndefinedOrNull } from 'vs/base/common/types'; export class GlobalExtensionEnablementService extends Disposable implements IGlobalExtensionEnablementService { @@ -96,7 +96,7 @@ export class StorageManager extends Disposable { constructor(private storageService: IStorageService) { super(); - this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e))); } get(key: string, scope: StorageScope): IExtensionIdentifier[] { @@ -127,14 +127,14 @@ export class StorageManager extends Disposable { } } - private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { - if (workspaceStorageChangeEvent.scope === StorageScope.GLOBAL) { - if (!isUndefinedOrNull(this.storage[workspaceStorageChangeEvent.key])) { - const newValue = this._get(workspaceStorageChangeEvent.key, workspaceStorageChangeEvent.scope); - if (newValue !== this.storage[workspaceStorageChangeEvent.key]) { - const oldValues = this.get(workspaceStorageChangeEvent.key, workspaceStorageChangeEvent.scope); - delete this.storage[workspaceStorageChangeEvent.key]; - const newValues = this.get(workspaceStorageChangeEvent.key, workspaceStorageChangeEvent.scope); + private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { + if (storageChangeEvent.scope === StorageScope.GLOBAL) { + if (!isUndefinedOrNull(this.storage[storageChangeEvent.key])) { + const newValue = this._get(storageChangeEvent.key, storageChangeEvent.scope); + if (newValue !== this.storage[storageChangeEvent.key]) { + const oldValues = this.get(storageChangeEvent.key, storageChangeEvent.scope); + delete this.storage[storageChangeEvent.key]; + const newValues = this.get(storageChangeEvent.key, storageChangeEvent.scope); const added = oldValues.filter(oldValue => !newValues.some(newValue => areSameExtensions(oldValue, newValue))); const removed = newValues.filter(newValue => !oldValues.some(oldValue => areSameExtensions(oldValue, newValue))); if (added.length || removed.length) { @@ -151,7 +151,8 @@ export class StorageManager extends Disposable { private _set(key: string, value: string | undefined, scope: StorageScope): void { if (value) { - this.storageService.store(key, value, scope); + // Enablement state is synced separately through extensions + this.storageService.store(key, value, scope, StorageTarget.MACHINE); } else { this.storageService.remove(key, scope); } diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index b9e1f47e..3f0dd835 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -19,7 +19,7 @@ import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { joinPath } from 'vs/base/common/resources'; @@ -803,7 +803,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService: { get: (key: string, scope: StorageScope) => string | undefined, - store: (key: string, value: string, scope: StorageScope) => void + store: (key: string, value: string, scope: StorageScope, target: StorageTarget) => void } | undefined): Promise<{ [key: string]: string; }> { const headers: IHeaders = { 'X-Market-Client-Id': `VSCode ${version}`, diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 0c20952b..0e46a92d 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -201,7 +201,8 @@ export class ExtensionManagementError extends Error { } } -export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean }; +export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, donotIncludePackAndDependencies?: boolean }; +export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean }; export const IExtensionManagementService = createDecorator('extensionManagementService'); export interface IExtensionManagementService { @@ -218,7 +219,7 @@ export interface IExtensionManagementService { install(vsix: URI, options?: InstallOptions): Promise; canInstall(extension: IGalleryExtension): Promise; installFromGallery(extension: IGalleryExtension, options?: InstallOptions): Promise; - uninstall(extension: ILocalExtension, force?: boolean): Promise; + uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise; reinstallFromGallery(extension: ILocalExtension): Promise; getInstalled(type?: ExtensionType): Promise; getExtensionsReport(): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index da354045..75422f96 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -4,12 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension, IExtensionTipsService, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { Event } from 'vs/base/common/event'; +import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension, IExtensionTipsService, InstallOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { Emitter, Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { cloneAndChange } from 'vs/base/common/objects'; import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { Disposable } from 'vs/base/common/lifecycle'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -77,18 +78,31 @@ export class ExtensionManagementChannel implements IServerChannel { } } -export class ExtensionManagementChannelClient implements IExtensionManagementService { +export class ExtensionManagementChannelClient extends Disposable implements IExtensionManagementService { declare readonly _serviceBrand: undefined; + private readonly _onInstallExtension = this._register(new Emitter()); + readonly onInstallExtension = this._onInstallExtension.event; + + private readonly _onDidInstallExtension = this._register(new Emitter()); + readonly onDidInstallExtension = this._onDidInstallExtension.event; + + private readonly _onUninstallExtension = this._register(new Emitter()); + readonly onUninstallExtension = this._onUninstallExtension.event; + + private readonly _onDidUninstallExtension = this._register(new Emitter()); + readonly onDidUninstallExtension = this._onDidUninstallExtension.event; + constructor( private readonly channel: IChannel, - ) { } - - get onInstallExtension(): Event { return this.channel.listen('onInstallExtension'); } - get onDidInstallExtension(): Event { return Event.map(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: i.local ? transformIncomingExtension(i.local, null) : i.local })); } - get onUninstallExtension(): Event { return this.channel.listen('onUninstallExtension'); } - get onDidUninstallExtension(): Event { return this.channel.listen('onDidUninstallExtension'); } + ) { + super(); + this._register(this.channel.listen('onInstallExtension')(e => this._onInstallExtension.fire(e))); + this._register(this.channel.listen('onDidInstallExtension')(e => this._onDidInstallExtension.fire({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local }))); + this._register(this.channel.listen('onUninstallExtension')(e => this._onUninstallExtension.fire(e))); + this._register(this.channel.listen('onDidUninstallExtension')(e => this._onDidUninstallExtension.fire(e))); + } zip(extension: ILocalExtension): Promise { return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(result))); @@ -114,8 +128,8 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null)); } - uninstall(extension: ILocalExtension, force = false): Promise { - return Promise.resolve(this.channel.call('uninstall', [extension!, force])); + uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { + return Promise.resolve(this.channel.call('uninstall', [extension!, options])); } reinstallFromGallery(extension: ILocalExtension): Promise { diff --git a/src/vs/platform/extensionManagement/common/extensionUrlTrust.ts b/src/vs/platform/extensionManagement/common/extensionUrlTrust.ts new file mode 100644 index 00000000..b2639564 --- /dev/null +++ b/src/vs/platform/extensionManagement/common/extensionUrlTrust.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IExtensionUrlTrustService = createDecorator('extensionUrlTrustService'); + +export interface IExtensionUrlTrustService { + readonly _serviceBrand: undefined; + isExtensionUrlTrusted(extensionId: string, url: string): Promise; +} diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index 4072c4ed..7d17d9ef 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -20,7 +20,7 @@ import { disposableTimeout, timeout } from 'vs/base/common/async'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { localize } from 'vs/nls'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; type ExeExtensionRecommendationsClassification = { extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -241,7 +241,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } private updateLastPromptedMediumExeTime(value: number): void { - this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.GLOBAL); + this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.GLOBAL, StorageTarget.MACHINE); } private getPromptedExecutableTips(): IStringDictionary { @@ -251,7 +251,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { private addToRecommendedExecutables(exeName: string, tips: IExecutableBasedExtensionTip[]) { const promptedExecutableTips = this.getPromptedExecutableTips(); promptedExecutableTips[exeName] = tips.map(({ extensionId }) => extensionId.toLowerCase()); - this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.GLOBAL); + this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.GLOBAL, StorageTarget.USER); } private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { diff --git a/src/vs/platform/extensionManagement/node/extensionDownloader.ts b/src/vs/platform/extensionManagement/node/extensionDownloader.ts index f11e0174..822dd47b 100644 --- a/src/vs/platform/extensionManagement/node/extensionDownloader.ts +++ b/src/vs/platform/extensionManagement/node/extensionDownloader.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; +import { rename } from 'vs/base/node/pfs'; import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -13,6 +14,7 @@ import { ExtensionIdentifierWithVersion, groupByExtension } from 'vs/platform/ex import { ILogService } from 'vs/platform/log/common/log'; import { generateUuid } from 'vs/base/common/uuid'; import * as semver from 'vs/base/common/semver/semver'; +import { isWindows } from 'vs/base/common/platform'; const ExtensionIdVersionRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)$/; @@ -36,8 +38,21 @@ export class ExtensionsDownloader extends Disposable { async downloadExtension(extension: IGalleryExtension, operation: InstallOperation): Promise { await this.cleanUpPromise; - const location = joinPath(this.extensionsDownloadDir, this.getName(extension)); - await this.download(extension, location, operation); + const vsixName = this.getName(extension); + const location = joinPath(this.extensionsDownloadDir, vsixName); + + // Download only if vsix does not exist + if (!await this.fileService.exists(location)) { + // Download to temporary location first only if vsix does not exist + const tempLocation = joinPath(this.extensionsDownloadDir, `.${vsixName}`); + if (!await this.fileService.exists(tempLocation)) { + await this.extensionGalleryService.download(extension, tempLocation, operation); + } + + // Rename temp location to original + await this.rename(tempLocation, location, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); + } + return location; } @@ -45,9 +60,15 @@ export class ExtensionsDownloader extends Disposable { // noop as caching is enabled always } - private async download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise { - if (!await this.fileService.exists(location)) { - await this.extensionGalleryService.download(extension, location, operation); + private async rename(from: URI, to: URI, retryUntil: number): Promise { + try { + await rename(from.fsPath, to.fsPath); + } catch (error) { + if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { + this.logService.info(`Failed renaming ${from} to ${to} with 'EPERM' error. Trying again...`); + return this.rename(from, to, retryUntil); + } + throw error; } } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 11135f9e..af1092c1 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -20,7 +20,8 @@ import { INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, ExtensionManagementError, - InstallOptions + InstallOptions, + UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, getGalleryExtensionId, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -297,11 +298,13 @@ export class ExtensionManagementService extends Disposable implements IExtension try { await this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)); } catch (error) { /* Ignore */ } - try { - await this.installDependenciesAndPackExtensions(local, existingExtension, options); - } catch (error) { - try { await this.uninstall(local); } catch (error) { /* Ignore */ } - throw error; + if (!options.donotIncludePackAndDependencies) { + try { + await this.installDependenciesAndPackExtensions(local, existingExtension, options); + } catch (error) { + try { await this.uninstall(local); } catch (error) { /* Ignore */ } + throw error; + } } if (existingExtension && semver.neq(existingExtension.manifest.version, extension.version)) { @@ -471,7 +474,7 @@ export class ExtensionManagementService extends Disposable implements IExtension await Promise.all(extensionsToUninstall.map(local => this.uninstall(local))); } - async uninstall(extension: ILocalExtension): Promise { + async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise { this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); const installed = await this.getInstalled(ExtensionType.User); const extensionToUninstall = installed.find(e => areSameExtensions(e.identifier, extension.identifier)); @@ -480,7 +483,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } try { - await this.checkForDependenciesAndUninstall(extensionToUninstall, installed); + await this.checkForDependenciesAndUninstall(extensionToUninstall, installed, options); } catch (error) { throw this.joinErrors(error); } @@ -533,15 +536,11 @@ export class ExtensionManagementService extends Disposable implements IExtension }, new Error('')); } - private async checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): Promise { + private async checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], options: UninstallOptions): Promise { try { await this.preUninstallExtension(extension); - const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed); - if (packedExtensions.length) { - await this.uninstallExtensions(extension, packedExtensions, installed); - } else { - await this.uninstallExtensions(extension, [], installed); - } + const packedExtensions = options.donotIncludePack ? [] : this.getAllPackExtensionsToUninstall(extension, installed); + await this.uninstallExtensions(extension, packedExtensions, installed, options); } catch (error) { await this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL)); throw error; @@ -549,10 +548,12 @@ export class ExtensionManagementService extends Disposable implements IExtension await this.postUninstallExtension(extension); } - private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): Promise { + private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[], options: UninstallOptions): Promise { const extensionsToUninstall = [extension, ...otherExtensionsToUninstall]; - for (const e of extensionsToUninstall) { - this.checkForDependents(e, extensionsToUninstall, installed, extension); + if (!options.donotCheckDependents) { + for (const e of extensionsToUninstall) { + this.checkForDependents(e, extensionsToUninstall, installed, extension); + } } await Promise.all([this.uninstallExtension(extension), ...otherExtensionsToUninstall.map(d => this.doUninstall(d))]); } diff --git a/src/vs/platform/extensionManagement/node/extensionUrlTrustService.ts b/src/vs/platform/extensionManagement/node/extensionUrlTrustService.ts new file mode 100644 index 00000000..8b638a63 --- /dev/null +++ b/src/vs/platform/extensionManagement/node/extensionUrlTrustService.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as crypto from 'crypto'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; + +export class ExtensionUrlTrustService implements IExtensionUrlTrustService { + + declare readonly _serviceBrand: undefined; + + private trustedExtensionUrlPublicKeys = new Map(); + + constructor( + @IProductService private readonly productService: IProductService, + @ILogService private readonly logService: ILogService + ) { } + + async isExtensionUrlTrusted(extensionId: string, url: string): Promise { + if (!this.productService.trustedExtensionUrlPublicKeys) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'There are no configured trusted keys'); + return false; + } + + const match = /^(.*)#([^#]+)$/.exec(url); + + if (!match) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Uri has no fragment', url); + return false; + } + + const [, originalUrl, fragment] = match; + + let keys = this.trustedExtensionUrlPublicKeys.get(extensionId); + + if (!keys) { + keys = this.productService.trustedExtensionUrlPublicKeys[extensionId]; + + if (!keys || keys.length === 0) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Extension doesn\'t have any trusted keys', extensionId); + return false; + } + + this.trustedExtensionUrlPublicKeys.set(extensionId, [...keys]); + } + + const fragmentBuffer = Buffer.from(decodeURIComponent(fragment), 'base64'); + + if (fragmentBuffer.length <= 6) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Uri fragment is not a signature', url); + return false; + } + + const timestampBuffer = fragmentBuffer.slice(0, 6); + const timestamp = fragmentBuffer.readUIntBE(0, 6); + const diff = Date.now() - timestamp; + + if (diff < 0 || diff > 3_600_000) { // 1 hour + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri has expired', url); + return false; + } + + const signatureBuffer = fragmentBuffer.slice(6); + const verify = crypto.createVerify('SHA256'); + verify.write(timestampBuffer); + verify.write(Buffer.from(originalUrl)); + verify.end(); + + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + + if (key === null) { // failed to be parsed before + continue; + } else if (typeof key === 'string') { // needs to be parsed + try { + key = crypto.createPublicKey({ key: Buffer.from(key, 'base64'), format: 'der', type: 'spki' }); + keys[i] = key; + } catch (err) { + this.logService.warn('ExtensionUrlTrustService#isExtensionUrlTrusted', `Failed to parse trusted extension uri public key #${i + 1} for ${extensionId}:`, err); + keys[i] = null; + continue; + } + } + + if (verify.verify(key, signatureBuffer)) { + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri is valid', url); + return true; + } + } + + this.logService.trace('ExtensionUrlTrustService#isExtensionUrlTrusted', 'Signed uri could not be verified', url); + return false; + } +} diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts index aee65f8e..adce023b 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -49,7 +49,7 @@ export class ExtensionsScanner extends Disposable { ) { super(); this.systemExtensionsPath = environmentService.builtinExtensionsPath; - this.extensionsPath = environmentService.extensionsPath!; + this.extensionsPath = environmentService.extensionsPath; this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); this.uninstalledFileLimiter = new Queue(); } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index b36a5aac..23001b2a 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -180,6 +180,7 @@ export class FileService extends Disposable implements IFileService { private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise; private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise { const provider = await this.withProvider(resource); + const isPathCaseSensitive = this.isPathCaseSensitive(provider); const resolveTo = options?.resolveTo; const resolveSingleChildDescendants = options?.resolveSingleChildDescendants; @@ -193,7 +194,7 @@ export class FileService extends Disposable implements IFileService { // lazy trie to check for recursive resolving if (!trie) { - trie = TernarySearchTree.forUris(); + trie = TernarySearchTree.forUris(() => !isPathCaseSensitive); trie.set(resource, true); if (isNonEmptyArray(resolveTo)) { resolveTo.forEach(uri => trie!.set(uri, true)); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 2d2cdcfe..4ae15434 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -521,19 +521,19 @@ export class FileChangesEvent { switch (change.type) { case FileChangeType.ADDED: if (!this.added) { - this.added = TernarySearchTree.forUris(this.ignorePathCasing); + this.added = TernarySearchTree.forUris(() => this.ignorePathCasing); } this.added.set(change.resource, change); break; case FileChangeType.UPDATED: if (!this.updated) { - this.updated = TernarySearchTree.forUris(this.ignorePathCasing); + this.updated = TernarySearchTree.forUris(() => this.ignorePathCasing); } this.updated.set(change.resource, change); break; case FileChangeType.DELETED: if (!this.deleted) { - this.deleted = TernarySearchTree.forUris(this.ignorePathCasing); + this.deleted = TernarySearchTree.forUris(() => this.ignorePathCasing); } this.deleted.set(change.resource, change); break; @@ -971,33 +971,33 @@ export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; /** * Helper to format a raw byte size into a human readable label. */ -export class BinarySize { +export class ByteSize { static readonly KB = 1024; - static readonly MB = BinarySize.KB * BinarySize.KB; - static readonly GB = BinarySize.MB * BinarySize.KB; - static readonly TB = BinarySize.GB * BinarySize.KB; + static readonly MB = ByteSize.KB * ByteSize.KB; + static readonly GB = ByteSize.MB * ByteSize.KB; + static readonly TB = ByteSize.GB * ByteSize.KB; static formatSize(size: number): string { if (!isNumber(size)) { size = 0; } - if (size < BinarySize.KB) { + if (size < ByteSize.KB) { return localize('sizeB', "{0}B", size.toFixed(0)); } - if (size < BinarySize.MB) { - return localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2)); + if (size < ByteSize.MB) { + return localize('sizeKB', "{0}KB", (size / ByteSize.KB).toFixed(2)); } - if (size < BinarySize.GB) { - return localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2)); + if (size < ByteSize.GB) { + return localize('sizeMB', "{0}MB", (size / ByteSize.MB).toFixed(2)); } - if (size < BinarySize.TB) { - return localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2)); + if (size < ByteSize.TB) { + return localize('sizeGB', "{0}GB", (size / ByteSize.GB).toFixed(2)); } - return localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2)); + return localize('sizeTB', "{0}TB", (size / ByteSize.TB).toFixed(2)); } } diff --git a/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts b/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts index de85fd77..9ad083fc 100644 --- a/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts +++ b/src/vs/platform/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts @@ -5,23 +5,27 @@ import * as assert from 'assert'; import * as platform from 'vs/base/common/platform'; -import { NsfwWatcherService } from 'vs/platform/files/node/watcher/nsfw/nsfwWatcherService'; import { IWatcherRequest } from 'vs/platform/files/node/watcher/nsfw/watcher'; -class TestNsfwWatcherService extends NsfwWatcherService { +suite('NSFW Watcher Service', async () => { - normalizeRoots(roots: string[]): string[] { + // Load `nsfwWatcherService` within the suite to prevent all tests + // from failing to start if `vscode-nsfw` was not properly installed + const { NsfwWatcherService } = await import('vs/platform/files/node/watcher/nsfw/nsfwWatcherService'); - // Work with strings as paths to simplify testing - const requests: IWatcherRequest[] = roots.map(r => { - return { path: r, excludes: [] }; - }); + class TestNsfwWatcherService extends NsfwWatcherService { - return this._normalizeRoots(requests).map(r => r.path); + normalizeRoots(roots: string[]): string[] { + + // Work with strings as paths to simplify testing + const requests: IWatcherRequest[] = roots.map(r => { + return { path: r, excludes: [] }; + }); + + return this._normalizeRoots(requests).map(r => r.path); + } } -} -suite('NSFW Watcher Service', () => { suite('_normalizeRoots', () => { test('should not impacts roots that don\'t overlap', () => { const service = new TestNsfwWatcherService(); diff --git a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts index 1f88cadc..895e5dfa 100644 --- a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts @@ -142,7 +142,7 @@ export class ChokidarWatcherService extends Disposable implements IWatcherServic } if (this.verboseLogging) { - this.log(`Start watching with chockidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`); + this.log(`Start watching with chokidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`); } let chokidarWatcher: chokidar.FSWatcher | null = chokidar.watch(realBasePath, watcherOpts); diff --git a/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts b/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts index 4fe0f39c..4c780ec6 100644 --- a/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts +++ b/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts @@ -5,32 +5,36 @@ import * as assert from 'assert'; import * as platform from 'vs/base/common/platform'; -import { normalizeRoots } from 'vs/platform/files/node/watcher/unix/chokidarWatcherService'; import { IWatcherRequest } from 'vs/platform/files/node/watcher/unix/watcher'; -function newRequest(basePath: string, ignored: string[] = []): IWatcherRequest { - return { path: basePath, excludes: ignored }; -} +suite('Chokidar normalizeRoots', async () => { -function assertNormalizedRootPath(inputPaths: string[], expectedPaths: string[]) { - const requests = inputPaths.map(path => newRequest(path)); - const actual = normalizeRoots(requests); - assert.deepEqual(Object.keys(actual).sort(), expectedPaths); -} + // Load `chokidarWatcherService` within the suite to prevent all tests + // from failing to start if `chokidar` was not properly installed + const { normalizeRoots } = await import('vs/platform/files/node/watcher/unix/chokidarWatcherService'); -function assertNormalizedRequests(inputRequests: IWatcherRequest[], expectedRequests: { [path: string]: IWatcherRequest[] }) { - const actual = normalizeRoots(inputRequests); - const actualPath = Object.keys(actual).sort(); - const expectedPaths = Object.keys(expectedRequests).sort(); - assert.deepEqual(actualPath, expectedPaths); - for (let path of actualPath) { - let a = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); - let e = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); - assert.deepEqual(a, e); + function newRequest(basePath: string, ignored: string[] = []): IWatcherRequest { + return { path: basePath, excludes: ignored }; + } + + function assertNormalizedRootPath(inputPaths: string[], expectedPaths: string[]) { + const requests = inputPaths.map(path => newRequest(path)); + const actual = normalizeRoots(requests); + assert.deepEqual(Object.keys(actual).sort(), expectedPaths); + } + + function assertNormalizedRequests(inputRequests: IWatcherRequest[], expectedRequests: { [path: string]: IWatcherRequest[] }) { + const actual = normalizeRoots(inputRequests); + const actualPath = Object.keys(actual).sort(); + const expectedPaths = Object.keys(expectedRequests).sort(); + assert.deepEqual(actualPath, expectedPaths); + for (let path of actualPath) { + let a = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); + let e = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); + assert.deepEqual(a, e); + } } -} -suite('Chokidar normalizeRoots', () => { test('should not impacts roots that don\'t overlap', () => { if (platform.isWindows) { assertNormalizedRootPath(['C:\\a'], ['C:\\a']); diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 9cbaae73..e981a34d 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -55,6 +55,7 @@ export interface IssueReporterData extends WindowData { enabledExtensions: IssueReporterExtensionData[]; issueType?: IssueType; extensionId?: string; + experiments?: string; readonly issueTitle?: string; readonly issueBody?: string; } diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index ca72fa62..da98e2f9 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -55,7 +55,7 @@ export class IssueMainService implements ICommonIssueService { .then(result => { const [info, remoteData] = result; this.diagnosticsService.getSystemInfo(info, remoteData).then(msg => { - event.sender.send('vscode:issueSystemInfoResponse', msg); + this.safeSend(event, 'vscode:issueSystemInfoResponse', msg); }); }); }); @@ -86,7 +86,7 @@ export class IssueMainService implements ICommonIssueService { this.logService.error(`Listing processes failed: ${e}`); } - event.sender.send('vscode:listProcessesResponse', processes); + this.safeSend(event, 'vscode:listProcessesResponse', processes); }); ipcMain.on('vscode:issueReporterClipboard', (event: IpcMainEvent) => { @@ -102,14 +102,14 @@ export class IssueMainService implements ICommonIssueService { if (this._issueWindow) { this.dialogMainService.showMessageBox(messageOptions, this._issueWindow) .then(result => { - event.sender.send('vscode:issueReporterClipboardResponse', result.response === 0); + this.safeSend(event, 'vscode:issueReporterClipboardResponse', result.response === 0); }); } }); ipcMain.on('vscode:issuePerformanceInfoRequest', (event: IpcMainEvent) => { this.getPerformanceInfo().then(msg => { - event.sender.send('vscode:issuePerformanceInfoResponse', msg); + this.safeSend(event, 'vscode:issuePerformanceInfoResponse', msg); }); }); @@ -174,11 +174,17 @@ export class IssueMainService implements ICommonIssueService { ipcMain.on('vscode:windowsInfoRequest', (event: IpcMainEvent) => { this.launchMainService.getMainProcessInfo().then(info => { - event.sender.send('vscode:windowsInfoResponse', info.windows); + this.safeSend(event, 'vscode:windowsInfoResponse', info.windows); }); }); } + private safeSend(event: IpcMainEvent, channel: string, ...args: unknown[]): void { + if (!event.sender.isDestroyed()) { + event.sender.send(channel, ...args); + } + } + openReporter(data: IssueReporterData): Promise { return new Promise(_ => { if (!this._issueWindow) { @@ -203,18 +209,8 @@ export class IssueMainService implements ICommonIssueService { spellcheck: false, nativeWindowOpen: true, zoomFactor: zoomLevelToZoomFactor(data.zoomLevel), - ...this.environmentService.sandbox ? - - // Sandbox - { - sandbox: true, - contextIsolation: true - } : - - // No Sandbox - { - nodeIntegration: true - } + sandbox: true, + contextIsolation: true } }); @@ -269,18 +265,8 @@ export class IssueMainService implements ICommonIssueService { spellcheck: false, nativeWindowOpen: true, zoomFactor: zoomLevelToZoomFactor(data.zoomLevel), - ...this.environmentService.sandbox ? - - // Sandbox - { - sandbox: true, - contextIsolation: true - } : - - // No Sandbox - { - nodeIntegration: true - } + sandbox: true, + contextIsolation: true } }); @@ -288,7 +274,6 @@ export class IssueMainService implements ICommonIssueService { const windowConfiguration = { appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, windowId: this._processExplorerWindow.id, userEnv: this.userEnv, machineId: this.machineId, @@ -416,7 +401,6 @@ export class IssueMainService implements ICommonIssueService { const windowConfiguration = { appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, windowId: this._issueWindow.id, machineId: this.machineId, userEnv: this.userEnv, @@ -452,7 +436,7 @@ function toWindowUrl(modulePathToHtml: string, windowConfiguration: T): strin } return FileAccess - .asBrowserUri(modulePathToHtml, require) + ._asCodeFileUri(modulePathToHtml, require) .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) .toString(true); } diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 43a109d6..3582ce6d 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -383,14 +383,9 @@ function printWhenExplanation(when: ContextKeyExpression | undefined): string { } function printSourceExplanation(kb: ResolvedKeybindingItem): string { - if (kb.isDefault) { - if (kb.extensionId) { - return `built-in extension ${kb.extensionId}`; - } - return `built-in`; - } - if (kb.extensionId) { - return `user extension ${kb.extensionId}`; - } - return `user`; + return ( + kb.extensionId + ? (kb.isBuiltinExtension ? `built-in extension ${kb.extensionId}` : `user extension ${kb.extensionId}`) + : (kb.isDefault ? `built-in` : `user`) + ); } diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index b4210f9e..2bf215e0 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -17,6 +17,7 @@ export interface IKeybindingItem { weight1: number; weight2: number; extensionId: string | null; + isBuiltinExtension: boolean; } export interface IKeybindings { @@ -53,6 +54,7 @@ export interface IKeybindingRule2 { weight: number; when: ContextKeyExpression | undefined; extensionId?: string; + isBuiltinExtension?: boolean; } export const enum KeybindingWeight { @@ -164,7 +166,8 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { when: rule.when, weight1: rule.weight, weight2: 0, - extensionId: rule.extensionId || null + extensionId: rule.extensionId || null, + isBuiltinExtension: rule.isBuiltinExtension || false }; } } @@ -223,7 +226,8 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { when: when, weight1: weight1, weight2: weight2, - extensionId: null + extensionId: null, + isBuiltinExtension: false }); this._cachedMergedKeybindings = null; } diff --git a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts index 0410c469..6d2df159 100644 --- a/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts +++ b/src/vs/platform/keybinding/common/resolvedKeybindingItem.ts @@ -18,8 +18,9 @@ export class ResolvedKeybindingItem { public readonly when: ContextKeyExpression | undefined; public readonly isDefault: boolean; public readonly extensionId: string | null; + public readonly isBuiltinExtension: boolean; - constructor(resolvedKeybinding: ResolvedKeybinding | undefined, command: string | null, commandArgs: any, when: ContextKeyExpression | undefined, isDefault: boolean, extensionId: string | null) { + constructor(resolvedKeybinding: ResolvedKeybinding | undefined, command: string | null, commandArgs: any, when: ContextKeyExpression | undefined, isDefault: boolean, extensionId: string | null, isBuiltinExtension: boolean) { this.resolvedKeybinding = resolvedKeybinding; this.keypressParts = resolvedKeybinding ? removeElementsAfterNulls(resolvedKeybinding.getDispatchParts()) : []; this.bubble = (command ? command.charCodeAt(0) === CharCode.Caret : false); @@ -28,6 +29,7 @@ export class ResolvedKeybindingItem { this.when = when; this.isDefault = isDefault; this.extensionId = extensionId; + this.isBuiltinExtension = isBuiltinExtension; } } diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index b9af02e7..cbbb5434 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -192,7 +192,8 @@ suite('AbstractKeybindingService', () => { null, when, true, - null + null, + false ); } diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index 0723eb63..2820e0ea 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -28,7 +28,8 @@ suite('KeybindingResolver', () => { commandArgs, when, isDefault, - null + null, + false ); } diff --git a/src/vs/workbench/services/keybinding/common/dispatchConfig.ts b/src/vs/platform/keyboardLayout/common/dispatchConfig.ts similarity index 100% rename from src/vs/workbench/services/keybinding/common/dispatchConfig.ts rename to src/vs/platform/keyboardLayout/common/dispatchConfig.ts diff --git a/src/vs/platform/keyboardLayout/common/keyboardLayout.ts b/src/vs/platform/keyboardLayout/common/keyboardLayout.ts new file mode 100644 index 00000000..db0975fe --- /dev/null +++ b/src/vs/platform/keyboardLayout/common/keyboardLayout.ts @@ -0,0 +1,260 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; +import { ScanCode, ScanCodeUtils } from 'vs/base/common/scanCode'; +import { IKeyboardMapper } from 'vs/platform/keyboardLayout/common/keyboardMapper'; +import { DispatchConfig } from 'vs/platform/keyboardLayout/common/dispatchConfig'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + +export const IKeyboardLayoutService = createDecorator('keyboardLayoutService'); + +export interface IWindowsKeyMapping { + vkey: string; + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface IWindowsKeyboardMapping { + [code: string]: IWindowsKeyMapping; +} +export interface ILinuxKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface ILinuxKeyboardMapping { + [code: string]: ILinuxKeyMapping; +} +export interface IMacKeyMapping { + value: string; + valueIsDeadKey: boolean; + withShift: string; + withShiftIsDeadKey: boolean; + withAltGr: string; + withAltGrIsDeadKey: boolean; + withShiftAltGr: string; + withShiftAltGrIsDeadKey: boolean; +} +export interface IMacKeyboardMapping { + [code: string]: IMacKeyMapping; +} + +export type IMacLinuxKeyMapping = IMacKeyMapping | ILinuxKeyMapping; +export type IMacLinuxKeyboardMapping = IMacKeyboardMapping | ILinuxKeyboardMapping; +export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping; + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IWindowsKeyboardLayoutInfo { + name: string; + id: string; + text: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface ILinuxKeyboardLayoutInfo { + model: string; + layout: string; + variant: string; + options: string; + rules: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "localizedName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IMacKeyboardLayoutInfo { + id: string; + lang: string; + localizedName?: string; +} + +export type IKeyboardLayoutInfo = (IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo) & { isUserKeyboardLayout?: boolean; isUSStandard?: true }; + +export interface IKeyboardLayoutService { + readonly _serviceBrand: undefined; + + readonly onDidChangeKeyboardLayout: Event; + + getRawKeyboardMapping(): IKeyboardMapping | null; + getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null; + getAllKeyboardLayouts(): IKeyboardLayoutInfo[]; + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper; + validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void; +} + +export function areKeyboardLayoutsEqual(a: IKeyboardLayoutInfo | null, b: IKeyboardLayoutInfo | null): boolean { + if (!a || !b) { + return false; + } + + if ((a).name && (b).name && (a).name === (b).name) { + return true; + } + + if ((a).id && (b).id && (a).id === (b).id) { + return true; + } + + if ((a).model && + (b).model && + (a).model === (b).model && + (a).layout === (b).layout + ) { + return true; + } + + return false; +} + +export function parseKeyboardLayoutDescription(layout: IKeyboardLayoutInfo | null): { label: string, description: string } { + if (!layout) { + return { label: '', description: '' }; + } + + if ((layout).name) { + // windows + let windowsLayout = layout; + return { + label: windowsLayout.text, + description: '' + }; + } + + if ((layout).id) { + let macLayout = layout; + if (macLayout.localizedName) { + return { + label: macLayout.localizedName, + description: '' + }; + } + + if (/^com\.apple\.keylayout\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^com\.apple\.keylayout\./, '').replace(/-/, ' '), + description: '' + }; + } + if (/^.*inputmethod\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^.*inputmethod\./, '').replace(/[-\.]/, ' '), + description: `Input Method (${macLayout.lang})` + }; + } + + return { + label: macLayout.lang, + description: '' + }; + } + + let linuxLayout = layout; + + return { + label: linuxLayout.layout, + description: '' + }; +} + +export function getKeyboardLayoutId(layout: IKeyboardLayoutInfo): string { + if ((layout).name) { + return (layout).name; + } + + if ((layout).id) { + return (layout).id; + } + + return (layout).layout; +} + +function windowsKeyMappingEquals(a: IWindowsKeyMapping, b: IWindowsKeyMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.vkey === b.vkey + && a.value === b.value + && a.withShift === b.withShift + && a.withAltGr === b.withAltGr + && a.withShiftAltGr === b.withShiftAltGr + ); +} + +export function windowsKeyboardMappingEquals(a: IWindowsKeyboardMapping | null, b: IWindowsKeyboardMapping | null): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const strScanCode = ScanCodeUtils.toString(scanCode); + const aEntry = a[strScanCode]; + const bEntry = b[strScanCode]; + if (!windowsKeyMappingEquals(aEntry, bEntry)) { + return false; + } + } + return true; +} + +function macLinuxKeyMappingEquals(a: IMacLinuxKeyMapping, b: IMacLinuxKeyMapping): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.value === b.value + && a.withShift === b.withShift + && a.withAltGr === b.withAltGr + && a.withShiftAltGr === b.withShiftAltGr + ); +} + +export function macLinuxKeyboardMappingEquals(a: IMacLinuxKeyboardMapping | null, b: IMacLinuxKeyboardMapping | null): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) { + const strScanCode = ScanCodeUtils.toString(scanCode); + const aEntry = a[strScanCode]; + const bEntry = b[strScanCode]; + if (!macLinuxKeyMappingEquals(aEntry, bEntry)) { + return false; + } + } + return true; +} diff --git a/src/vs/platform/keyboardLayout/common/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/common/keyboardLayoutMainService.ts new file mode 100644 index 00000000..4b6d3d46 --- /dev/null +++ b/src/vs/platform/keyboardLayout/common/keyboardLayoutMainService.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IKeyboardLayoutInfo, IKeyboardMapping } from 'vs/platform/keyboardLayout/common/keyboardLayout'; +import { Event } from 'vs/base/common/event'; + +export interface IKeyboardLayoutData { + keyboardLayoutInfo: IKeyboardLayoutInfo; + keyboardMapping: IKeyboardMapping; +} + +export interface IKeyboardLayoutMainService { + readonly _serviceBrand: undefined; + readonly onDidChangeKeyboardLayout: Event; + getKeyboardLayoutData(): Promise; +} diff --git a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts b/src/vs/platform/keyboardLayout/common/keyboardMapper.ts similarity index 100% rename from src/vs/workbench/services/keybinding/common/keyboardMapper.ts rename to src/vs/platform/keyboardLayout/common/keyboardMapper.ts diff --git a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts new file mode 100644 index 00000000..f28a15dc --- /dev/null +++ b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IKeyboardLayoutData, IKeyboardLayoutMainService as ICommonKeyboardLayoutMainService } from 'vs/platform/keyboardLayout/common/keyboardLayoutMainService'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as nativeKeymap from 'native-keymap'; + +export const IKeyboardLayoutMainService = createDecorator('keyboardLayoutMainService'); + +export interface IKeyboardLayoutMainService extends ICommonKeyboardLayoutMainService { } + +export class KeyboardLayoutMainService extends Disposable implements ICommonKeyboardLayoutMainService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidChangeKeyboardLayout = this._register(new Emitter()); + readonly onDidChangeKeyboardLayout = this._onDidChangeKeyboardLayout.event; + + private _initPromise: Promise | null; + private _keyboardLayoutData: IKeyboardLayoutData | null; + + constructor() { + super(); + this._initPromise = null; + this._keyboardLayoutData = null; + } + + private _initialize(): Promise { + if (!this._initPromise) { + this._initPromise = this._doInitialize(); + } + return this._initPromise; + } + + private async _doInitialize(): Promise { + const nativeKeymapMod = await import('native-keymap'); + + this._keyboardLayoutData = readKeyboardLayoutData(nativeKeymapMod); + nativeKeymapMod.onDidChangeKeyboardLayout(() => { + this._keyboardLayoutData = readKeyboardLayoutData(nativeKeymapMod); + this._onDidChangeKeyboardLayout.fire(this._keyboardLayoutData); + }); + } + + public async getKeyboardLayoutData(): Promise { + await this._initialize(); + return this._keyboardLayoutData!; + } +} + +function readKeyboardLayoutData(nativeKeymapMod: typeof nativeKeymap): IKeyboardLayoutData { + const keyboardMapping = nativeKeymapMod.getKeyMap(); + const keyboardLayoutInfo = nativeKeymapMod.getCurrentKeyboardLayout(); + return { keyboardMapping, keyboardLayoutInfo }; +} diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index e646cc85..9b3d6566 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -19,6 +19,8 @@ import { BrowserWindow, ipcMain, Event as IpcEvent, app } from 'electron'; import { coalesce } from 'vs/base/common/arrays'; import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/node/launch'; +import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const ID = 'launchMainService'; export const ILaunchMainService = createDecorator(ID); @@ -33,14 +35,14 @@ export interface IRemoteDiagnosticOptions { includeWorkspaceMetadata?: boolean; } -function parseOpenUrl(args: NativeParsedArgs): URI[] { +function parseOpenUrl(args: NativeParsedArgs): { uri: URI, url: string }[] { if (args['open-url'] && args._urls && args._urls.length > 0) { // --open-url must contain -- followed by the url(s) // process.argv is used over args._ as args._ are resolved to file paths at this point return coalesce(args._urls .map(url => { try { - return URI.parse(url); + return { uri: URI.parse(url), url }; } catch (err) { return null; } @@ -99,8 +101,8 @@ export class LaunchMainService implements ILaunchMainService { // Make sure a window is open, ready to receive the url event whenWindowReady.then(() => { - for (const url of urlsToOpen) { - this.urlService.open(url); + for (const { uri, url } of urlsToOpen) { + this.urlService.open(uri, { originalUrl: url }); } }); } @@ -112,7 +114,7 @@ export class LaunchMainService implements ILaunchMainService { } private startOpenWindow(args: NativeParsedArgs, userEnv: IProcessEnvironment): Promise { - const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; + const context = isLaunchedFromCli(userEnv) ? OpenContext.CLI : OpenContext.DESKTOP; let usedWindows: ICodeWindow[] = []; const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined; @@ -138,7 +140,7 @@ export class LaunchMainService implements ILaunchMainService { // Otherwise check for settings else { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const openWithoutArgumentsInNewWindowConfig = windowConfig?.openWithoutArgumentsInNewWindow || 'default' /* default */; switch (openWithoutArgumentsInNewWindowConfig) { case 'on': @@ -247,7 +249,7 @@ export class LaunchMainService implements ILaunchMainService { folders: options.includeWorkspaceMetadata ? this.getFolderURIs(window) : undefined }; - window.sendWhenReady('vscode:getDiagnosticInfo', { replyChannel, args }); + window.sendWhenReady('vscode:getDiagnosticInfo', CancellationToken.None, { replyChannel, args }); ipcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => { // No data is returned if getting the connection fails. diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 48ad05b7..62db3f75 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcMain as ipc, app, BrowserWindow } from 'electron'; +import { ipcMain, app, BrowserWindow } from 'electron'; import { ILogService } from 'vs/platform/log/common/log'; import { IStateService } from 'vs/platform/state/node/state'; import { Event, Emitter } from 'vs/base/common/event'; @@ -371,7 +371,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // Only reload when the window has not vetoed this const veto = await this.unload(window, UnloadReason.RELOAD); if (!veto) { - window.reload(undefined, cli); + window.reload(cli); } } @@ -437,11 +437,11 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const okChannel = `vscode:ok${oneTimeEventToken}`; const cancelChannel = `vscode:cancel${oneTimeEventToken}`; - ipc.once(okChannel, () => { + ipcMain.once(okChannel, () => { resolve(false); // no veto }); - ipc.once(cancelChannel, () => { + ipcMain.once(cancelChannel, () => { resolve(true); // veto }); @@ -468,7 +468,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const oneTimeEventToken = this.oneTimeListenerTokenGenerator++; const replyChannel = `vscode:reply${oneTimeEventToken}`; - ipc.once(replyChannel, () => resolve()); + ipcMain.once(replyChannel, () => resolve()); window.send('vscode:onWillUnload', { replyChannel, reason }); }); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index b1a86552..b553a058 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -118,14 +118,15 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi return result; } -export const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier'; -export const openModeSettingKey = 'workbench.list.openMode'; -export const horizontalScrollingKey = 'workbench.list.horizontalScrolling'; -export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation'; -export const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation'; +const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier'; +const openModeSettingKey = 'workbench.list.openMode'; +const horizontalScrollingKey = 'workbench.list.horizontalScrolling'; +const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation'; +const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation'; const treeIndentKey = 'workbench.tree.indent'; const treeRenderIndentGuidesKey = 'workbench.tree.renderIndentGuides'; const listSmoothScrolling = 'workbench.list.smoothScrolling'; +const treeExpandMode = 'workbench.tree.expandMode'; function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean { return configurationService.getValue(multiSelectModifierSettingKey) === 'alt'; @@ -444,11 +445,11 @@ abstract class ResourceNavigator extends Disposable { private readonly openOnFocus: boolean; private openOnSingleClick: boolean; - private readonly _onDidOpen = new Emitter>(); - readonly onDidOpen: Event> = this._onDidOpen.event; + private readonly _onDidOpen = this._register(new Emitter>()); + readonly onDidOpen: Event> = this._onDidOpen.event; constructor( - private readonly widget: ListWidget, + protected readonly widget: ListWidget, options?: IResourceNavigatorOptions ) { super(); @@ -456,8 +457,8 @@ abstract class ResourceNavigator extends Disposable { this.openOnFocus = options?.openOnFocus ?? false; this._register(Event.filter(this.widget.onDidChangeSelection, e => e.browserEvent instanceof KeyboardEvent)(e => this.onSelectionFromKeyboard(e))); - this._register(this.widget.onPointer((e: { browserEvent: MouseEvent }) => this.onPointer(e.browserEvent))); - this._register(this.widget.onMouseDblClick((e: { browserEvent: MouseEvent }) => this.onMouseDblClick(e.browserEvent))); + this._register(this.widget.onPointer((e: { browserEvent: MouseEvent, element: T | undefined }) => this.onPointer(e.element, e.browserEvent))); + this._register(this.widget.onMouseDblClick((e: { browserEvent: MouseEvent, element: T | undefined }) => this.onMouseDblClick(e.element, e.browserEvent))); if (this.openOnFocus) { this._register(Event.filter(this.widget.onDidChangeFocus, e => e.browserEvent instanceof KeyboardEvent)(e => this.onFocusFromKeyboard(e))); @@ -478,10 +479,10 @@ abstract class ResourceNavigator extends Disposable { this.widget.setSelection(focus, event.browserEvent); const preserveFocus = typeof (event.browserEvent as SelectionKeyboardEvent).preserveFocus === 'boolean' ? (event.browserEvent as SelectionKeyboardEvent).preserveFocus! : true; - const pinned = false; + const pinned = !preserveFocus; const sideBySide = false; - this._open(preserveFocus, pinned, sideBySide, event.browserEvent); + this._open(this.getSelectedElement(), preserveFocus, pinned, sideBySide, event.browserEvent); } private onSelectionFromKeyboard(event: ITreeEvent): void { @@ -490,13 +491,13 @@ abstract class ResourceNavigator extends Disposable { } const preserveFocus = typeof (event.browserEvent as SelectionKeyboardEvent).preserveFocus === 'boolean' ? (event.browserEvent as SelectionKeyboardEvent).preserveFocus! : true; - const pinned = false; + const pinned = !preserveFocus; const sideBySide = false; - this._open(preserveFocus, pinned, sideBySide, event.browserEvent); + this._open(this.getSelectedElement(), preserveFocus, pinned, sideBySide, event.browserEvent); } - private onPointer(browserEvent: MouseEvent): void { + private onPointer(element: T | undefined, browserEvent: MouseEvent): void { if (!this.openOnSingleClick) { return; } @@ -512,10 +513,10 @@ abstract class ResourceNavigator extends Disposable { const pinned = isMiddleClick; const sideBySide = browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey; - this._open(preserveFocus, pinned, sideBySide, browserEvent); + this._open(element, preserveFocus, pinned, sideBySide, browserEvent); } - private onMouseDblClick(browserEvent?: MouseEvent): void { + private onMouseDblClick(element: T | undefined, browserEvent?: MouseEvent): void { if (!browserEvent) { return; } @@ -524,10 +525,14 @@ abstract class ResourceNavigator extends Disposable { const pinned = true; const sideBySide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey); - this._open(preserveFocus, pinned, sideBySide, browserEvent); + this._open(element, preserveFocus, pinned, sideBySide, browserEvent); } - private _open(preserveFocus: boolean, pinned: boolean, sideBySide: boolean, browserEvent?: UIEvent): void { + private _open(element: T | undefined, preserveFocus: boolean, pinned: boolean, sideBySide: boolean, browserEvent?: UIEvent): void { + if (!element) { + return; + } + this._onDidOpen.fire({ editorOptions: { preserveFocus, @@ -535,27 +540,39 @@ abstract class ResourceNavigator extends Disposable { revealIfVisible: true }, sideBySide, - element: this.widget.getSelection()[0], + element, browserEvent }); } + + abstract getSelectedElement(): T | undefined; } -export class ListResourceNavigator extends ResourceNavigator { +export class ListResourceNavigator extends ResourceNavigator { + constructor( - list: List | PagedList, + protected readonly widget: List | PagedList, options?: IResourceNavigatorOptions ) { - super(list, options); + super(widget, options); + } + + getSelectedElement(): T | undefined { + return this.widget.getSelectedElements()[0]; } } class TreeResourceNavigator extends ResourceNavigator { + constructor( - tree: ObjectTree | CompressibleObjectTree | DataTree | AsyncDataTree | CompressibleAsyncDataTree, + protected readonly widget: ObjectTree | CompressibleObjectTree | DataTree | AsyncDataTree | CompressibleAsyncDataTree, options: IResourceNavigatorOptions ) { - super(tree, options); + super(widget, options); + } + + getSelectedElement(): T | undefined { + return this.widget.getSelection()[0] ?? undefined; } } @@ -590,7 +607,7 @@ export class WorkbenchObjectTree, TFilterData = void> private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -626,7 +643,7 @@ export class WorkbenchCompressibleObjectTree, TFilter private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -670,7 +687,7 @@ export class WorkbenchDataTree extends DataTree; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -715,7 +732,7 @@ export class WorkbenchAsyncDataTree extends Async private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -757,7 +774,7 @@ export class WorkbenchCompressibleAsyncDataTree e private internals: WorkbenchTreeInternals; get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; } get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; } - get onDidOpen(): Event> { return this.internals.onDidOpen; } + get onDidOpen(): Event> { return this.internals.onDidOpen; } constructor( user: string, @@ -831,7 +848,8 @@ function workbenchTreeDataPreamble(treeExpandMode) === 'doubleClick') } as TOptions }; } @@ -847,7 +865,7 @@ class WorkbenchTreeInternals { private styler: IDisposable | undefined; private navigator: TreeResourceNavigator; - get onDidOpen(): Event> { return this.navigator.onDidOpen; } + get onDidOpen(): Event> { return this.navigator.onDidOpen; } constructor( private tree: WorkbenchObjectTree | WorkbenchCompressibleObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree | WorkbenchCompressibleAsyncDataTree, @@ -933,6 +951,9 @@ class WorkbenchTreeInternals { if (e.affectsConfiguration(openModeSettingKey)) { newOptions = { ...newOptions, expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' }; } + if (e.affectsConfiguration(treeExpandMode) && options.expandOnlyOnTwistieClick === undefined) { + newOptions = { ...newOptions, expandOnlyOnTwistieClick: configurationService.getValue<'singleClick' | 'doubleClick'>(treeExpandMode) === 'doubleClick' }; + } if (Object.keys(newOptions).length > 0) { tree.updateOptions(newOptions); } @@ -1036,6 +1057,12 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'default': true, markdownDescription: localize('automatic keyboard navigation setting', "Controls whether keyboard navigation in lists and trees is automatically triggered simply by typing. If set to `false`, keyboard navigation is only triggered when executing the `list.toggleKeyboardNavigation` command, for which you can assign a keyboard shortcut.") + }, + [treeExpandMode]: { + type: 'string', + enum: ['singleClick', 'doubleClick'], + default: 'singleClick', + description: localize('expand mode', "Controls how tree folders are expanded when clicking the folder names."), } } }); diff --git a/src/vs/platform/log/common/fileLogService.ts b/src/vs/platform/log/common/fileLogService.ts index a6318446..7f2665b5 100644 --- a/src/vs/platform/log/common/fileLogService.ts +++ b/src/vs/platform/log/common/fileLogService.ts @@ -5,7 +5,7 @@ import { ILogService, LogLevel, AbstractLogService, ILoggerService, ILogger } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; -import { FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; +import { ByteSize, FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; import { Queue } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { dirname, joinPath, basename } from 'vs/base/common/resources'; @@ -13,7 +13,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; -const MAX_FILE_SIZE = 1024 * 1024 * 5; +const MAX_FILE_SIZE = 5 * ByteSize.MB; export class FileLogService extends AbstractLogService implements ILogService { diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index add6b766..67462e8f 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -419,23 +419,43 @@ export function getLogLevel(environmentService: IEnvironmentService): LogLevel { return LogLevel.Trace; } if (typeof environmentService.logLevel === 'string') { - const logLevel = environmentService.logLevel.toLowerCase(); - switch (logLevel) { - case 'trace': - return LogLevel.Trace; - case 'debug': - return LogLevel.Debug; - case 'info': - return LogLevel.Info; - case 'warn': - return LogLevel.Warning; - case 'error': - return LogLevel.Error; - case 'critical': - return LogLevel.Critical; - case 'off': - return LogLevel.Off; + const logLevel = parseLogLevel(environmentService.logLevel.toLowerCase()); + if (logLevel !== undefined) { + return logLevel; } } return DEFAULT_LOG_LEVEL; } + +export function parseLogLevel(logLevel: string): LogLevel | undefined { + switch (logLevel) { + case 'trace': + return LogLevel.Trace; + case 'debug': + return LogLevel.Debug; + case 'info': + return LogLevel.Info; + case 'warn': + return LogLevel.Warning; + case 'error': + return LogLevel.Error; + case 'critical': + return LogLevel.Critical; + case 'off': + return LogLevel.Off; + } + return undefined; +} + +export function LogLevelToString(logLevel: LogLevel): string { + switch (logLevel) { + case LogLevel.Trace: return 'trace'; + case LogLevel.Debug: return 'debug'; + case LogLevel.Info: return 'info'; + case LogLevel.Warning: return 'warn'; + case LogLevel.Error: return 'error'; + case LogLevel.Critical: return 'critical'; + case LogLevel.Off: return 'off'; + } +} + diff --git a/src/vs/platform/log/node/spdlogService.ts b/src/vs/platform/log/node/spdlogService.ts index c32ab07a..b011f242 100644 --- a/src/vs/platform/log/node/spdlogService.ts +++ b/src/vs/platform/log/node/spdlogService.ts @@ -6,6 +6,7 @@ import * as path from 'vs/base/common/path'; import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log'; import * as spdlog from 'spdlog'; +import { ByteSize } from 'vs/platform/files/common/files'; async function createSpdLogLogger(processName: string, logsFolder: string): Promise { // Do not crash if spdlog cannot be loaded @@ -13,7 +14,7 @@ async function createSpdLogLogger(processName: string, logsFolder: string): Prom const _spdlog = await import('spdlog'); _spdlog.setAsyncMode(8192, 500); const logfilePath = path.join(logsFolder, `${processName}.log`); - return _spdlog.createRotatingLoggerAsync(processName, logfilePath, 1024 * 1024 * 5, 6); + return _spdlog.createRotatingLoggerAsync(processName, logfilePath, 5 * ByteSize.MB, 6); } catch (e) { console.error(e); } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 4ecb3725..3039b01a 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -24,6 +24,7 @@ import { IStateService } from 'vs/platform/state/node/state'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService'; +import { CancellationToken } from 'vs/base/common/cancellation'; const telemetryFrom = 'menu'; @@ -82,7 +83,7 @@ export class Menubar { this.menubarMenus = Object.create(null); this.keybindings = Object.create(null); - if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + if (isMacintosh || getTitleBarStyle(this.configurationService) === 'native') { this.restoreCachedMenubarData(); } @@ -416,7 +417,7 @@ export class Menubar { private shouldDrawMenu(menuId: string): boolean { // We need to draw an empty menu to override the electron default - if (!isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (!isMacintosh && getTitleBarStyle(this.configurationService) === 'custom') { return false; } @@ -754,10 +755,10 @@ export class Menubar { if (invocation.type === 'commandId') { const runActionPayload: INativeRunActionInWindowRequest = { id: invocation.commandId, from: 'menu' }; - activeWindow.sendWhenReady('vscode:runAction', runActionPayload); + activeWindow.sendWhenReady('vscode:runAction', CancellationToken.None, runActionPayload); } else { const runKeybindingPayload: INativeRunKeybindingInWindowRequest = { userSettingsLabel: invocation.userSettingsLabel }; - activeWindow.sendWhenReady('vscode:runKeybinding', runKeybindingPayload); + activeWindow.sendWhenReady('vscode:runKeybinding', CancellationToken.None, runKeybindingPayload); } } else { this.logService.trace('menubar#runActionInRenderer: no active window found', invocation); diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index c71a369e..e24efa83 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -10,7 +10,7 @@ import { OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IOpenedWindow, IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions, IColorScheme } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; -import { isMacintosh, isWindows, isRootUser, isLinux } from 'vs/base/common/platform'; +import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { ICommonNativeHostService, IOSProperties, IOSStatistics } from 'vs/platform/native/common/native'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; @@ -364,7 +364,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain if (isWindows) { isAdmin = (await import('native-is-elevated'))(); } else { - isAdmin = isRootUser(); + isAdmin = process.getuid() === 0; } return isAdmin; @@ -550,7 +550,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async reload(windowId: number | undefined, options?: { disableExtensions?: boolean }): Promise { const window = this.windowById(windowId); if (window) { - return this.lifecycleMainService.reload(window, options?.disableExtensions ? { _: [], 'disable-extensions': true } : undefined); + return this.lifecycleMainService.reload(window, options?.disableExtensions !== undefined ? { _: [], 'disable-extensions': options?.disableExtensions } : undefined); } } @@ -615,11 +615,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain const window = this.windowById(windowId); if (window) { const contents = window.win.webContents; - if (isMacintosh && window.hasHiddenTitleBarStyle && !window.isFullScreen && !contents.isDevToolsOpened()) { - contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 - } else { - contents.toggleDevTools(); - } + contents.toggleDevTools(); } } diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index cc3cb107..74c26363 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -114,6 +114,8 @@ export interface INotificationActions { /** * Primary actions show up as buttons as part of the message and will close * the notification once clicked. + * + * Pass `ActionWithMenuAction` for an action that has additional menu actions. */ readonly primary?: ReadonlyArray; @@ -209,19 +211,13 @@ export interface INotificationHandle { close(): void; } -export interface IPromptChoice { +interface IBasePromptChoice { /** * Label to show for the choice to the user. */ readonly label: string; - /** - * Primary choices show up as buttons in the notification below the message. - * Secondary choices show up under the gear icon in the header of the notification. - */ - readonly isSecondary?: boolean; - /** * Whether to keep the notification open after the choice was selected * by the user. By default, will close the notification upon click. @@ -234,6 +230,28 @@ export interface IPromptChoice { run: () => void; } +export interface IPromptChoice extends IBasePromptChoice { + + /** + * Primary choices show up as buttons in the notification below the message. + * Secondary choices show up under the gear icon in the header of the notification. + */ + readonly isSecondary?: boolean; +} + +export interface IPromptChoiceWithMenu extends IPromptChoice { + + /** + * Additional choices those will be shown in the dropdown menu for this choice. + */ + readonly menu: IBasePromptChoice[]; + + /** + * Menu is not supported on secondary choices + */ + readonly isSecondary: false | undefined; +} + export interface IPromptOptions extends INotificationProperties { /** @@ -327,7 +345,7 @@ export interface INotificationService { * * @returns a handle on the notification to e.g. hide it or update message, buttons, etc. */ - prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle; + prompt(severity: Severity, message: string, choices: (IPromptChoice | IPromptChoiceWithMenu)[], options?: IPromptOptions): INotificationHandle; /** * Shows a status message in the status area with the provided text. diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 977cb79b..d82651f9 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -7,6 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; export const IOpenerService = createDecorator('openerService'); @@ -18,6 +19,11 @@ type OpenInternalOptions = { */ readonly openToSide?: boolean; + /** + * Extra editor options to apply in case an editor is used to open. + */ + readonly editorOptions?: IEditorOptions; + /** * Signals that the editor to open was triggered through a user * action, such as keyboard or mouse usage. diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 2bea8574..56fbc68a 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -20,7 +20,7 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! // Running out of sources if (Object.keys(product).length === 0) { Object.assign(product, { - version: '1.51.0-dev', + version: '1.52.0-dev', nameShort: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev', nameLong: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev', applicationName: 'code-oss', @@ -32,7 +32,6 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! extensionAllowedProposedApi: [ 'ms-vscode.vscode-js-profile-flame', 'ms-vscode.vscode-js-profile-table', - 'ms-vscode.references-view', 'ms-vscode.github-browser' ], }); diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 333e5b24..74a8c744 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -80,6 +80,7 @@ export interface IProductConfiguration { readonly remoteExtensionTips?: { [remoteName: string]: IRemoteExtensionTip; }; readonly extensionKeywords?: { [extension: string]: readonly string[]; }; readonly keymapExtensionTips?: readonly string[]; + readonly trustedExtensionUrlPublicKeys?: { [id: string]: string[]; }; readonly crashReporter?: { readonly companyName: string; diff --git a/src/vs/platform/quickinput/browser/commandsQuickAccess.ts b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts index 18b7dc26..a1d27c5a 100644 --- a/src/vs/platform/quickinput/browser/commandsQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts @@ -11,7 +11,7 @@ import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecyc import { or, matchesPrefix, matchesWords, matchesContiguousSubString } from 'vs/base/common/filters'; import { withNullAsUndefined } from 'vs/base/common/types'; import { LRUCache } from 'vs/base/common/map'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -21,7 +21,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export interface ICommandQuickPick extends IPickerQuickAccessItem { commandId: string; @@ -204,14 +203,9 @@ export class CommandsHistory extends Disposable { constructor( @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_CACHE, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_COUNTER, version: 1 }); - this.updateConfiguration(); this.load(); @@ -279,8 +273,8 @@ export class CommandsHistory extends Disposable { const serializedCache: ISerializedCommandHistory = { usesLRU: true, entries: [] }; CommandsHistory.cache.forEach((value, key) => serializedCache.entries.push({ key, value })); - storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL); - storageService.store(CommandsHistory.PREF_KEY_COUNTER, CommandsHistory.counter, StorageScope.GLOBAL); + storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(CommandsHistory.PREF_KEY_COUNTER, CommandsHistory.counter, StorageScope.GLOBAL, StorageTarget.USER); } static getConfiguredCommandHistoryLength(configurationService: IConfigurationService): number { diff --git a/src/vs/platform/remote/common/tunnel.ts b/src/vs/platform/remote/common/tunnel.ts index 52dfacbc..980c3203 100644 --- a/src/vs/platform/remote/common/tunnel.ts +++ b/src/vs/platform/remote/common/tunnel.ts @@ -26,8 +26,12 @@ export interface TunnelOptions { label?: string; } +export interface TunnelCreationOptions { + elevationRequired?: boolean; +} + export interface ITunnelProvider { - forwardPort(tunnelOptions: TunnelOptions): Promise | undefined; + forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined; } export interface ITunnelService { @@ -56,8 +60,14 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: }; } +export const LOCALHOST_ADDRESSES = ['localhost', '127.0.0.1', '0:0:0:0:0:0:0:1', '::1']; export function isLocalhost(host: string): boolean { - return host === 'localhost' || host === '127.0.0.1'; + return LOCALHOST_ADDRESSES.indexOf(host) >= 0; +} + +export const ALL_INTERFACES_ADDRESSES = ['0.0.0.0', '0:0:0:0:0:0:0:0', '::']; +export function isAllInterfaces(host: string): boolean { + return ALL_INTERFACES_ADDRESSES.indexOf(host) >= 0; } function getOtherLocalhost(host: string): string | undefined { @@ -198,6 +208,10 @@ export abstract class AbstractTunnelService implements ITunnelService { } protected abstract retainOrCreateTunnel(addressProvider: IAddressProvider, remoteHost: string, remotePort: number, localPort?: number): Promise | undefined; + + protected isPortPrivileged(port: number): boolean { + return port < 1024; + } } export class TunnelService extends AbstractTunnelService { @@ -209,7 +223,10 @@ export class TunnelService extends AbstractTunnelService { } if (this._tunnelProvider) { - const tunnel = this._tunnelProvider.forwardPort({ remoteAddress: { host: remoteHost, port: remotePort } }); + const preferredLocalPort = localPort === undefined ? remotePort : localPort; + const tunnelOptions = { remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort }; + const creationInfo = { elevationRequired: this.isPortPrivileged(preferredLocalPort) }; + const tunnel = this._tunnelProvider.forwardPort(tunnelOptions, creationInfo); if (tunnel) { this.addTunnelToMap(remoteHost, remotePort, tunnel); } diff --git a/src/vs/platform/remote/node/tunnelService.ts b/src/vs/platform/remote/node/tunnelService.ts index 0a27aabb..19ec1efd 100644 --- a/src/vs/platform/remote/node/tunnelService.ts +++ b/src/vs/platform/remote/node/tunnelService.ts @@ -146,7 +146,10 @@ export class TunnelService extends AbstractTunnelService { } if (this._tunnelProvider) { - const tunnel = this._tunnelProvider.forwardPort({ remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort }); + const preferredLocalPort = localPort === undefined ? remotePort : localPort; + const creationInfo = { elevationRequired: this.isPortPrivileged(preferredLocalPort) }; + const tunnelOptions = { remoteAddress: { host: remoteHost, port: remotePort }, localAddressPort: localPort }; + const tunnel = this._tunnelProvider.forwardPort(tunnelOptions, creationInfo); if (tunnel) { this.addTunnelToMap(remoteHost, remotePort, tunnel); } diff --git a/src/vs/platform/serviceMachineId/common/serviceMachineId.ts b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts index 97abad37..c760a13b 100644 --- a/src/vs/platform/serviceMachineId/common/serviceMachineId.ts +++ b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IFileService } from 'vs/platform/files/common/files'; -import { StorageScope } from 'vs/platform/storage/common/storage'; +import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isUUID, generateUuid } from 'vs/base/common/uuid'; import { VSBuffer } from 'vs/base/common/buffer'; export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: { get: (key: string, scope: StorageScope, fallbackValue?: string | undefined) => string | undefined, - store: (key: string, value: string, scope: StorageScope) => void + store: (key: string, value: string, scope: StorageScope, target: StorageTarget) => void } | undefined): Promise { let uuid: string | null = storageService ? storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null : null; if (uuid) { @@ -34,7 +34,7 @@ export async function getServiceMachineId(environmentService: IEnvironmentServic } } if (storageService) { - storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL); + storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL, StorageTarget.MACHINE); } return uuid; } diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index ab3fd347..c667dd88 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -5,7 +5,7 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { StorageScope, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; @@ -16,15 +16,7 @@ import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; -export class BrowserStorageService extends Disposable implements IStorageService { - - declare readonly _serviceBrand: undefined; - - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; - - private readonly _onWillSaveState = this._register(new Emitter()); - readonly onWillSaveState = this._onWillSaveState.event; +export class BrowserStorageService extends AbstractStorageService { private globalStorage: IStorage | undefined; private workspaceStorage: IStorage | undefined; @@ -40,6 +32,10 @@ export class BrowserStorageService extends Disposable implements IStorageService private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 5000 /* every 5s */)); private runWhenIdleDisposable: IDisposable | undefined = undefined; + get hasPendingUpdate(): boolean { + return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate); + } + constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService, @IFileService private readonly fileService: IFileService @@ -66,13 +62,13 @@ export class BrowserStorageService extends Disposable implements IStorageService this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService)); this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); - this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE }))); + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); // Global Storage this.globalStorageFile = joinPath(stateRoot, 'global.json'); this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, true /* watch for external changes */, this.fileService)); this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); - this._register(this.globalStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL }))); + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); // Init both await Promise.all([ @@ -122,11 +118,11 @@ export class BrowserStorageService extends Disposable implements IStorageService return this.getStorage(scope).getNumber(key, fallbackValue); } - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { + protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { this.getStorage(scope).set(key, value); } - remove(key: string, scope: StorageScope): void { + protected doRemove(key: string, scope: StorageScope): void { this.getStorage(scope).delete(key); } @@ -149,6 +145,13 @@ export class BrowserStorageService extends Disposable implements IStorageService throw new Error('Migrating storage is currently unsupported in Web'); } + protected async doFlush(): Promise { + await Promise.all([ + this.getStorage(StorageScope.GLOBAL).whenFlushed(), + this.getStorage(StorageScope.WORKSPACE).whenFlushed() + ]); + } + private doFlushWhenIdle(): void { // Dispose any previous idle runner @@ -175,14 +178,6 @@ export class BrowserStorageService extends Disposable implements IStorageService }); } - get hasPendingUpdate(): boolean { - return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate); - } - - flush(): void { - this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); - } - close(): void { // We explicitly do not close our DBs because writing data onBeforeUnload() // can result in unexpected results. Namely, it seems that - even though this @@ -195,10 +190,6 @@ export class BrowserStorageService extends Disposable implements IStorageService this.dispose(); } - isNew(scope: StorageScope): boolean { - return this.getBoolean(IS_NEW_KEY, scope) === true; - } - dispose(): void { dispose(this.runWhenIdleDisposable); this.runWhenIdleDisposable = undefined; diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 6611f1da..3d176f5b 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -4,18 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event, Emitter, PauseableEmitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; export const IS_NEW_KEY = '__$__isNewStorageMarker'; +const TARGET_KEY = '__$__targetStorageMarker'; export const IStorageService = createDecorator('storageService'); export enum WillSaveStateReason { - NONE = 0, - SHUTDOWN = 1 + + /** + * No specific reason to save state. + */ + NONE, + + /** + * A hint that the workbench is about to shutdown. + */ + SHUTDOWN } export interface IWillSaveStateEvent { @@ -29,7 +38,12 @@ export interface IStorageService { /** * Emitted whenever data is updated or deleted. */ - readonly onDidChangeStorage: Event; + readonly onDidChangeValue: Event; + + /** + * Emitted whenever target of a storage entry changes. + */ + readonly onDidChangeTarget: Event; /** * Emitted when the storage is about to persist. This is the right time @@ -48,44 +62,49 @@ export interface IStorageService { /** * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. + * the provided `defaultValue` if the element is `null` or `undefined`. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. */ get(key: string, scope: StorageScope, fallbackValue: string): string; get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; /** * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a boolean. + * the provided `defaultValue` if the element is `null` or `undefined`. + * The element will be converted to a `boolean`. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. */ getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; /** * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a number using parseInt with a base of 10. + * the provided `defaultValue` if the element is `null` or `undefined`. + * The element will be converted to a `number` using `parseInt` with a + * base of `10`. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. */ getNumber(key: string, scope: StorageScope, fallbackValue: number): number; getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; /** - * Store a value under the given key to storage. The value will be converted to a string. - * Storing either undefined or null will remove the entry under the key. + * Store a value under the given key to storage. The value will be + * converted to a `string`. Storing either `undefined` or `null` will + * remove the entry under the key. * - * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * @param scope allows to define the scope of the storage operation + * to either the current workspace only or all workspaces. + * + * @param target allows to define the target of the storage operation + * to either the current machine or user. */ - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void; + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void; /** * Delete an element stored under the provided key from storage. @@ -95,6 +114,22 @@ export interface IStorageService { */ remove(key: string, scope: StorageScope): void; + /** + * Returns all the keys used in the storage for the provided `scope` + * and `target`. + * + * Note: this will NOT return all keys stored in the storage layer. + * Some keys may not have an associated `StorageTarget` and thus + * will be excluded from the results. + * + * @param scope allows to define the scope for the keys + * to either the current workspace only or all workspaces. + * + * @param target allows to define the target for the keys + * to either the current machine or user. + */ + keys(scope: StorageScope, target: StorageTarget): string[]; + /** * Log the contents of the storage to the console. */ @@ -108,16 +143,18 @@ export interface IStorageService { /** * Whether the storage for the given scope was created during this session or * existed before. - * */ isNew(scope: StorageScope): boolean; /** * Allows to flush state, e.g. in cases where a shutdown is - * imminent. This will send out the onWillSaveState to ask + * imminent. This will send out the `onWillSaveState` to ask * everyone for latest state. + * + * @returns a `Promise` that can be awaited on when all updates + * to the underlying storage have been flushed. */ - flush(): void; + flush(): Promise; } export const enum StorageScope { @@ -133,21 +170,247 @@ export const enum StorageScope { WORKSPACE } -export interface IWorkspaceStorageChangeEvent { +export const enum StorageTarget { + + /** + * The stored data is user specific and applies across machines. + */ + USER, + + /** + * The stored data is machine specific. + */ + MACHINE +} + +export interface IStorageValueChangeEvent { + + /** + * The scope for the storage entry that changed + * or was removed. + */ + readonly scope: StorageScope; + + /** + * The `key` of the storage entry that was changed + * or was removed. + */ readonly key: string; + + /** + * The `target` can be `undefined` if a key is being + * removed. + */ + readonly target: StorageTarget | undefined; +} + +export interface IStorageTargetChangeEvent { + + /** + * The scope for the target that changed. Listeners + * should use `keys(scope, target)` to get an updated + * list of keys for the given `scope` and `target`. + */ readonly scope: StorageScope; } -export class InMemoryStorageService extends Disposable implements IStorageService { +interface IKeyTargets { + [key: string]: StorageTarget +} + +export abstract class AbstractStorageService extends Disposable implements IStorageService { declare readonly _serviceBrand: undefined; - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; + private readonly _onDidChangeValue = this._register(new PauseableEmitter()); + readonly onDidChangeValue = this._onDidChangeValue.event; - protected readonly _onWillSaveState = this._register(new Emitter()); + private readonly _onDidChangeTarget = this._register(new PauseableEmitter()); + readonly onDidChangeTarget = this._onDidChangeTarget.event; + + private readonly _onWillSaveState = this._register(new Emitter()); readonly onWillSaveState = this._onWillSaveState.event; + protected emitDidChangeValue(scope: StorageScope, key: string): void { + + // Specially handle `TARGET_KEY` + if (key === TARGET_KEY) { + + // Clear our cached version which is now out of date + if (scope === StorageScope.GLOBAL) { + this._globalKeyTargets = undefined; + } else if (scope === StorageScope.WORKSPACE) { + this._workspaceKeyTargets = undefined; + } + + // Emit as `didChangeTarget` event + this._onDidChangeTarget.fire({ scope }); + } + + // Emit any other key to outside + else { + this._onDidChangeValue.fire({ scope, key, target: this.getKeyTargets(scope)[key] }); + } + } + + protected emitWillSaveState(reason: WillSaveStateReason): void { + this._onWillSaveState.fire({ reason }); + } + + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void { + + // We remove the key for undefined/null values + if (isUndefinedOrNull(value)) { + this.remove(key, scope); + return; + } + + // Update our datastructures but send events only after + this.withPausedEmitters(() => { + + // Update key-target map + this.updateKeyTarget(key, scope, target); + + // Store actual value + this.doStore(key, value, scope); + }); + } + + remove(key: string, scope: StorageScope): void { + + // Update our datastructures but send events only after + this.withPausedEmitters(() => { + + // Update key-target map + this.updateKeyTarget(key, scope, undefined); + + // Remove actual key + this.doRemove(key, scope); + }); + } + + private withPausedEmitters(fn: Function): void { + + // Pause emitters + this._onDidChangeValue.pause(); + this._onDidChangeTarget.pause(); + + try { + fn(); + } finally { + + // Resume emitters + this._onDidChangeValue.resume(); + this._onDidChangeTarget.resume(); + } + } + + keys(scope: StorageScope, target: StorageTarget): string[] { + const keys: string[] = []; + + const keyTargets = this.getKeyTargets(scope); + for (const key of Object.keys(keyTargets)) { + const keyTarget = keyTargets[key]; + if (keyTarget === target) { + keys.push(key); + } + } + + return keys; + } + + private updateKeyTarget(key: string, scope: StorageScope, target: StorageTarget | undefined): void { + + // Add + const keyTargets = this.getKeyTargets(scope); + if (typeof target === 'number') { + if (keyTargets[key] !== target) { + keyTargets[key] = target; + this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + } + } + + // Remove + else { + if (typeof keyTargets[key] === 'number') { + delete keyTargets[key]; + this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + } + } + } + + private _workspaceKeyTargets: IKeyTargets | undefined = undefined; + private get workspaceKeyTargets(): IKeyTargets { + if (!this._workspaceKeyTargets) { + this._workspaceKeyTargets = this.loadKeyTargets(StorageScope.WORKSPACE); + } + + return this._workspaceKeyTargets; + } + + private _globalKeyTargets: IKeyTargets | undefined = undefined; + private get globalKeyTargets(): IKeyTargets { + if (!this._globalKeyTargets) { + this._globalKeyTargets = this.loadKeyTargets(StorageScope.GLOBAL); + } + + return this._globalKeyTargets; + } + + private getKeyTargets(scope: StorageScope): IKeyTargets { + return scope === StorageScope.GLOBAL ? this.globalKeyTargets : this.workspaceKeyTargets; + } + + private loadKeyTargets(scope: StorageScope): { [key: string]: StorageTarget } { + const keysRaw = this.get(TARGET_KEY, scope); + if (keysRaw) { + try { + return JSON.parse(keysRaw); + } catch (error) { + // Fail gracefully + } + } + + return Object.create(null); + } + + isNew(scope: StorageScope): boolean { + return this.getBoolean(IS_NEW_KEY, scope) === true; + } + + flush(): Promise { + + // Signal event to collect changes + this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); + + // Await flush + return this.doFlush(); + } + + // --- abstract + + abstract get(key: string, scope: StorageScope, fallbackValue: string): string; + abstract get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; + + abstract getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; + abstract getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; + + abstract getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + abstract getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; + + protected abstract doStore(key: string, value: string | boolean | number, scope: StorageScope): void; + + protected abstract doRemove(key: string, scope: StorageScope): void; + + protected abstract doFlush(): Promise; + + abstract migrate(toWorkspace: IWorkspaceInitializationPayload): Promise; + + abstract logStorage(): void; +} + +export class InMemoryStorageService extends AbstractStorageService { + private readonly globalCache = new Map(); private readonly workspaceCache = new Map(); @@ -188,12 +451,7 @@ export class InMemoryStorageService extends Disposable implements IStorageServic return parseInt(value, 10); } - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise { - - // We remove the key for undefined/null values - if (isUndefinedOrNull(value)) { - return this.remove(key, scope); - } + protected doStore(key: string, value: string | boolean | number, scope: StorageScope): void { // Otherwise, convert to String and store const valueStr = String(value); @@ -201,28 +459,24 @@ export class InMemoryStorageService extends Disposable implements IStorageServic // Return early if value already set const currentValue = this.getCache(scope).get(key); if (currentValue === valueStr) { - return Promise.resolve(); + return; } // Update in cache this.getCache(scope).set(key, valueStr); // Events - this._onDidChangeStorage.fire({ scope, key }); - - return Promise.resolve(); + this.emitDidChangeValue(scope, key); } - remove(key: string, scope: StorageScope): Promise { + protected doRemove(key: string, scope: StorageScope): void { const wasDeleted = this.getCache(scope).delete(key); if (!wasDeleted) { - return Promise.resolve(); // Return early if value already deleted + return; // Return early if value already deleted } // Events - this._onDidChangeStorage.fire({ scope, key }); - - return Promise.resolve(); + this.emitDidChangeValue(scope, key); } logStorage(): void { @@ -233,13 +487,7 @@ export class InMemoryStorageService extends Disposable implements IStorageServic // not supported } - flush(): void { - this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); - } - - isNew(): boolean { - return true; // always new when in-memory - } + async doFlush(): Promise { } async close(): Promise { } } diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 096b9e23..97cb9b16 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { StorageScope, WillSaveStateReason, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; import { Storage, IStorageDatabase, IStorage, StorageHint } from 'vs/base/parts/storage/common/storage'; import { mark } from 'vs/base/common/performance'; @@ -17,19 +16,11 @@ import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderW import { assertIsDefined } from 'vs/base/common/types'; import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; -export class NativeStorageService extends Disposable implements IStorageService { - - declare readonly _serviceBrand: undefined; +export class NativeStorageService extends AbstractStorageService { private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; private static readonly WORKSPACE_META_NAME = 'workspace.json'; - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; - - private readonly _onWillSaveState = this._register(new Emitter()); - readonly onWillSaveState = this._onWillSaveState.event; - private readonly globalStorage = new Storage(this.globalStorageDatabase); private workspaceStoragePath: string | undefined; @@ -54,11 +45,7 @@ export class NativeStorageService extends Disposable implements IStorageService private registerListeners(): void { // Global Storage change events - this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL))); - } - - private handleDidChangeStorage(key: string, scope: StorageScope): void { - this._onDidChangeStorage.fire({ key, scope }); + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); } initialize(payload?: IWorkspaceInitializationPayload): Promise { @@ -134,7 +121,7 @@ export class NativeStorageService extends Disposable implements IStorageService // Create new this.workspaceStoragePath = workspaceStoragePath; this.workspaceStorage = new Storage(new SQLiteStorageDatabase(workspaceStoragePath, { logging: workspaceLoggingOptions }), { hint }); - this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE)); + this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); return this.workspaceStorage; } @@ -201,11 +188,11 @@ export class NativeStorageService extends Disposable implements IStorageService return this.getStorage(scope).getNumber(key, fallbackValue); } - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { + protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { this.getStorage(scope).set(key, value); } - remove(key: string, scope: StorageScope): void { + protected doRemove(key: string, scope: StorageScope): void { this.getStorage(scope).delete(key); } @@ -213,6 +200,19 @@ export class NativeStorageService extends Disposable implements IStorageService return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); } + protected async doFlush(): Promise { + const promises: Promise[] = []; + if (this.globalStorage) { + promises.push(this.globalStorage.whenFlushed()); + } + + if (this.workspaceStorage) { + promises.push(this.workspaceStorage.whenFlushed()); + } + + await Promise.all(promises); + } + private doFlushWhenIdle(): void { // Dispose any previous idle runner @@ -229,10 +229,6 @@ export class NativeStorageService extends Disposable implements IStorageService }); } - flush(): void { - this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); - } - async close(): Promise { // Stop periodic scheduler and idle runner as we now collect state normally @@ -241,7 +237,7 @@ export class NativeStorageService extends Disposable implements IStorageService this.runWhenIdleDisposable = undefined; // Signal as event so that clients can still store data - this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); // Do it await Promise.all([ @@ -277,8 +273,4 @@ export class NativeStorageService extends Disposable implements IStorageService // Recreate and init workspace storage return this.createWorkspaceStorage(newWorkspaceStoragePath).init(); } - - isNew(scope: StorageScope): boolean { - return this.getBoolean(IS_NEW_KEY, scope) === true; - } } diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts new file mode 100644 index 00000000..886efb2f --- /dev/null +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { strictEqual, ok, equal } from 'assert'; +import { StorageScope, InMemoryStorageService, StorageTarget, IStorageValueChangeEvent, IStorageTargetChangeEvent } from 'vs/platform/storage/common/storage'; + +suite('StorageService', function () { + + test('Get Data, Integer, Boolean (global, in-memory)', () => { + storeData(StorageScope.GLOBAL); + }); + + test('Get Data, Integer, Boolean (workspace, in-memory)', () => { + storeData(StorageScope.WORKSPACE); + }); + + function storeData(scope: StorageScope): void { + const storage = new InMemoryStorageService(); + + let storageValueChangeEvents: IStorageValueChangeEvent[] = []; + storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + + strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar'); + strictEqual(storage.get('test.get', scope, ''), ''); + strictEqual(storage.getNumber('test.getNumber', scope, 5), 5); + strictEqual(storage.getNumber('test.getNumber', scope, 0), 0); + strictEqual(storage.getBoolean('test.getBoolean', scope, true), true); + strictEqual(storage.getBoolean('test.getBoolean', scope, false), false); + + storage.store('test.get', 'foobar', scope, StorageTarget.MACHINE); + strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar'); + let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.get'); + storageValueChangeEvents = []; + + storage.store('test.get', '', scope, StorageTarget.MACHINE); + strictEqual(storage.get('test.get', scope, (undefined)!), ''); + storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); + equal(storageValueChangeEvent!.scope, scope); + equal(storageValueChangeEvent!.key, 'test.get'); + + storage.store('test.getNumber', 5, scope, StorageTarget.MACHINE); + strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5); + + storage.store('test.getNumber', 0, scope, StorageTarget.MACHINE); + strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0); + + storage.store('test.getBoolean', true, scope, StorageTarget.MACHINE); + strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true); + + storage.store('test.getBoolean', false, scope, StorageTarget.MACHINE); + strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false); + + strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault'); + strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5); + strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true); + } + + test('Remove Data (global, in-memory)', () => { + removeData(StorageScope.GLOBAL); + }); + + test('Remove Data (workspace, in-memory)', () => { + removeData(StorageScope.WORKSPACE); + }); + + function removeData(scope: StorageScope): void { + const storage = new InMemoryStorageService(); + + let storageValueChangeEvents: IStorageValueChangeEvent[] = []; + storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + + storage.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); + strictEqual('foobar', storage.get('test.remove', scope, (undefined)!)); + + storage.remove('test.remove', scope); + ok(!storage.get('test.remove', scope, (undefined)!)); + let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.remove'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.remove'); + } + + test('Keys (in-memory)', () => { + const storage = new InMemoryStorageService(); + + let storageTargetEvent: IStorageTargetChangeEvent | undefined = undefined; + storage.onDidChangeTarget(e => storageTargetEvent = e); + + let storageValueChangeEvent: IStorageValueChangeEvent | undefined = undefined; + storage.onDidChangeValue(e => storageValueChangeEvent = e); + + // Empty + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + strictEqual(storage.keys(scope, target).length, 0); + } + } + + // Add values + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + storageTargetEvent = Object.create(null); + storageValueChangeEvent = Object.create(null); + + storage.store('test.target1', 'value1', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + equal(storageTargetEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.target1'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.target, target); + + storageTargetEvent = undefined; + storageValueChangeEvent = Object.create(null); + + storage.store('test.target1', 'otherValue1', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + equal(storageTargetEvent, undefined); + equal(storageValueChangeEvent?.key, 'test.target1'); + equal(storageValueChangeEvent?.scope, scope); + equal(storageValueChangeEvent?.target, target); + + storage.store('test.target2', 'value2', scope, target); + storage.store('test.target3', 'value3', scope, target); + + strictEqual(storage.keys(scope, target).length, 3); + } + } + + // Remove values + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + const keysLength = storage.keys(scope, target).length; + + storage.store('test.target4', 'value1', scope, target); + strictEqual(storage.keys(scope, target).length, keysLength + 1); + + storageTargetEvent = Object.create(null); + storageValueChangeEvent = Object.create(null); + + storage.remove('test.target4', scope); + strictEqual(storage.keys(scope, target).length, keysLength); + equal(storageTargetEvent?.scope, scope); + equal(storageValueChangeEvent?.key, 'test.target4'); + equal(storageValueChangeEvent?.scope, scope); + } + } + + // Remove all + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + const keys = storage.keys(scope, target); + + for (const key of keys) { + storage.remove(key, scope); + } + + strictEqual(storage.keys(scope, target).length, 0); + } + } + + // Adding undefined or null removes value + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + storage.store('test.target1', 'value1', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + + storageTargetEvent = Object.create(null); + + storage.store('test.target1', undefined, scope, target); + strictEqual(storage.keys(scope, target).length, 0); + equal(storageTargetEvent?.scope, scope); + + storage.store('test.target1', '', scope, target); + strictEqual(storage.keys(scope, target).length, 1); + + storage.store('test.target1', null, scope, target); + strictEqual(storage.keys(scope, target).length, 0); + } + } + + // Target change + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + ok(!storageTargetEvent); // no change in target + }); +}); diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index a87802a8..e0a410ef 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { strictEqual, ok, equal } from 'assert'; -import { StorageScope, InMemoryStorageService } from 'vs/platform/storage/common/storage'; +import { equal } from 'assert'; +import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { NativeStorageService } from 'vs/platform/storage/node/storageService'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'vs/base/common/path'; @@ -16,66 +16,7 @@ import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; -suite('StorageService', function () { - - test('Remove Data (global, in-memory)', () => { - removeData(StorageScope.GLOBAL); - }); - - test('Remove Data (workspace, in-memory)', () => { - removeData(StorageScope.WORKSPACE); - }); - - function removeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - - storage.store('test.remove', 'foobar', scope); - strictEqual('foobar', storage.get('test.remove', scope, (undefined)!)); - - storage.remove('test.remove', scope); - ok(!storage.get('test.remove', scope, (undefined)!)); - } - - test('Get Data, Integer, Boolean (global, in-memory)', () => { - storeData(StorageScope.GLOBAL); - }); - - test('Get Data, Integer, Boolean (workspace, in-memory)', () => { - storeData(StorageScope.WORKSPACE); - }); - - function storeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - - strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar'); - strictEqual(storage.get('test.get', scope, ''), ''); - strictEqual(storage.getNumber('test.getNumber', scope, 5), 5); - strictEqual(storage.getNumber('test.getNumber', scope, 0), 0); - strictEqual(storage.getBoolean('test.getBoolean', scope, true), true); - strictEqual(storage.getBoolean('test.getBoolean', scope, false), false); - - storage.store('test.get', 'foobar', scope); - strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar'); - - storage.store('test.get', '', scope); - strictEqual(storage.get('test.get', scope, (undefined)!), ''); - - storage.store('test.getNumber', 5, scope); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5); - - storage.store('test.getNumber', 0, scope); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0); - - storage.store('test.getBoolean', true, scope); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true); - - storage.store('test.getBoolean', false, scope); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false); - - strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault'); - strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5); - strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true); - } +suite('NativeStorageService', function () { function uniqueStorageDir(): string { const id = generateUuid(); @@ -111,9 +52,13 @@ suite('StorageService', function () { const storage = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(storageDir), storageDir)); await storage.initialize({ id: String(Date.now()) }); - storage.store('bar', 'foo', StorageScope.WORKSPACE); - storage.store('barNumber', 55, StorageScope.WORKSPACE); - storage.store('barBoolean', true, StorageScope.GLOBAL); + storage.store('bar', 'foo', StorageScope.WORKSPACE, StorageTarget.MACHINE); + storage.store('barNumber', 55, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storage.store('barBoolean', true, StorageScope.GLOBAL, StorageTarget.MACHINE); + + equal(storage.get('bar', StorageScope.WORKSPACE), 'foo'); + equal(storage.getNumber('barNumber', StorageScope.WORKSPACE), 55); + equal(storage.getBoolean('barBoolean', StorageScope.GLOBAL), true); await storage.migrate({ id: String(Date.now() + 100) }); diff --git a/src/vs/platform/telemetry/browser/errorTelemetry.ts b/src/vs/platform/telemetry/browser/errorTelemetry.ts index 6ecc99a2..6ef338fd 100644 --- a/src/vs/platform/telemetry/browser/errorTelemetry.ts +++ b/src/vs/platform/telemetry/browser/errorTelemetry.ts @@ -5,7 +5,7 @@ import { toDisposable } from 'vs/base/common/lifecycle'; import { globals } from 'vs/base/common/platform'; -import BaseErrorTelemetry, { ErrorEvent } from '../common/errorTelemetry'; +import BaseErrorTelemetry, { ErrorEvent } from 'vs/platform/telemetry/common/errorTelemetry'; export default class ErrorTelemetry extends BaseErrorTelemetry { protected installErrorListeners(): void { diff --git a/src/vs/platform/telemetry/node/appInsightsAppender.ts b/src/vs/platform/telemetry/node/appInsightsAppender.ts index b288c282..0001e177 100644 --- a/src/vs/platform/telemetry/node/appInsightsAppender.ts +++ b/src/vs/platform/telemetry/node/appInsightsAppender.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as appInsights from 'applicationinsights'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { mixin } from 'vs/base/common/objects'; import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; -function getClient(aiKey: string): appInsights.TelemetryClient { - +async function getClient(aiKey: string): Promise { + const appInsights = await import('applicationinsights'); let client: appInsights.TelemetryClient; if (appInsights.defaultClient) { client = new appInsights.TelemetryClient(aiKey); @@ -36,7 +37,8 @@ function getClient(aiKey: string): appInsights.TelemetryClient { export class AppInsightsAppender implements ITelemetryAppender { - private _aiClient?: appInsights.TelemetryClient; + private _aiClient: string | appInsights.TelemetryClient | undefined; + private _asyncAIClient: Promise | null; constructor( private _eventPrefix: string, @@ -47,11 +49,37 @@ export class AppInsightsAppender implements ITelemetryAppender { this._defaultData = Object.create(null); } - if (typeof aiKeyOrClientFactory === 'string') { - this._aiClient = getClient(aiKeyOrClientFactory); - } else if (typeof aiKeyOrClientFactory === 'function') { + if (typeof aiKeyOrClientFactory === 'function') { this._aiClient = aiKeyOrClientFactory(); + } else { + this._aiClient = aiKeyOrClientFactory; } + this._asyncAIClient = null; + } + + private _withAIClient(callback: (aiClient: appInsights.TelemetryClient) => void): void { + if (!this._aiClient) { + return; + } + + if (typeof this._aiClient !== 'string') { + callback(this._aiClient); + return; + } + + if (!this._asyncAIClient) { + this._asyncAIClient = getClient(this._aiClient); + } + + this._asyncAIClient.then( + (aiClient) => { + callback(aiClient); + }, + (err) => { + onUnexpectedError(err); + console.error(err); + } + ); } log(eventName: string, data?: any): void { @@ -61,22 +89,24 @@ export class AppInsightsAppender implements ITelemetryAppender { data = mixin(data, this._defaultData); data = validateTelemetryData(data); - this._aiClient.trackEvent({ + this._withAIClient((aiClient) => aiClient.trackEvent({ name: this._eventPrefix + '/' + eventName, properties: data.properties, measurements: data.measurements - }); + })); } flush(): Promise { if (this._aiClient) { return new Promise(resolve => { - this._aiClient!.flush({ - callback: () => { - // all data flushed - this._aiClient = undefined; - resolve(undefined); - } + this._withAIClient((aiClient) => { + aiClient.flush({ + callback: () => { + // all data flushed + this._aiClient = undefined; + resolve(undefined); + } + }); }); }); } diff --git a/src/vs/platform/telemetry/node/errorTelemetry.ts b/src/vs/platform/telemetry/node/errorTelemetry.ts index 60f1633e..562eb44b 100644 --- a/src/vs/platform/telemetry/node/errorTelemetry.ts +++ b/src/vs/platform/telemetry/node/errorTelemetry.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import BaseErrorTelemetry from '../common/errorTelemetry'; +import BaseErrorTelemetry from 'vs/platform/telemetry/common/errorTelemetry'; export default class ErrorTelemetry extends BaseErrorTelemetry { protected installErrorListeners(): void { diff --git a/src/vs/platform/theme/browser/checkbox.ts b/src/vs/platform/theme/browser/checkbox.ts deleted file mode 100644 index c479126e..00000000 --- a/src/vs/platform/theme/browser/checkbox.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CheckboxActionViewItem } from 'vs/base/browser/ui/checkbox/checkbox'; -import { IAction } from 'vs/base/common/actions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { attachCheckboxStyler } from 'vs/platform/theme/common/styler'; -import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; - -export class ThemableCheckboxActionViewItem extends CheckboxActionViewItem { - - constructor(context: any, action: IAction, options: IBaseActionViewItemOptions | undefined, private readonly themeService: IThemeService) { - super(context, action, options); - } - - render(container: HTMLElement): void { - super.render(container); - if (this.checkbox) { - this.disposables.add(attachCheckboxStyler(this.checkbox, this.themeService)); - } - } - -} - diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index c9de28ba..b5239353 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -196,7 +196,7 @@ export const textBlockQuoteBorder = registerColor('textBlockQuote.border', { lig export const textCodeBlockBackground = registerColor('textCodeBlock.background', { light: '#dcdcdc66', dark: '#0a0a0a66', hc: Color.black }, nls.localize('textCodeBlockBackground', "Background color for code blocks in text.")); // ----- widgets -export const widgetShadow = registerColor('widget.shadow', { dark: '#000000', light: '#A8A8A8', hc: null }, nls.localize('widgetShadow', 'Shadow color of widgets such as find/replace inside the editor.')); +export const widgetShadow = registerColor('widget.shadow', { dark: transparent(Color.black, .36), light: transparent(Color.black, .16), hc: null }, nls.localize('widgetShadow', 'Shadow color of widgets such as find/replace inside the editor.')); export const inputBackground = registerColor('input.background', { dark: '#3C3C3C', light: Color.white, hc: Color.black }, nls.localize('inputBoxBackground', "Input box background.")); export const inputForeground = registerColor('input.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('inputBoxForeground', "Input box foreground.")); @@ -243,18 +243,23 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); +export const editorErrorBackground = registerColor('editorError.background', { dark: null, light: null, hc: null }, nls.localize('editorError.background', 'Background color of error text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorErrorForeground = registerColor('editorError.foreground', { dark: '#F48771', light: '#E51400', hc: null }, nls.localize('editorError.foreground', 'Foreground color of error squigglies in the editor.')); export const editorErrorBorder = registerColor('editorError.border', { dark: null, light: null, hc: Color.fromHex('#E47777').transparent(0.8) }, nls.localize('errorBorder', 'Border color of error boxes in the editor.')); +export const editorWarningBackground = registerColor('editorWarning.background', { dark: null, light: null, hc: null }, nls.localize('editorWarning.background', 'Background color of warning text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorWarningForeground = registerColor('editorWarning.foreground', { dark: '#CCA700', light: '#E9A700', hc: null }, nls.localize('editorWarning.foreground', 'Foreground color of warning squigglies in the editor.')); export const editorWarningBorder = registerColor('editorWarning.border', { dark: null, light: null, hc: Color.fromHex('#FFCC00').transparent(0.8) }, nls.localize('warningBorder', 'Border color of warning boxes in the editor.')); +export const editorInfoBackground = registerColor('editorInfo.background', { dark: null, light: null, hc: null }, nls.localize('editorInfo.background', 'Background color of info text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorInfoForeground = registerColor('editorInfo.foreground', { dark: '#75BEFF', light: '#75BEFF', hc: null }, nls.localize('editorInfo.foreground', 'Foreground color of info squigglies in the editor.')); export const editorInfoBorder = registerColor('editorInfo.border', { dark: null, light: null, hc: Color.fromHex('#75BEFF').transparent(0.8) }, nls.localize('infoBorder', 'Border color of info boxes in the editor.')); export const editorHintForeground = registerColor('editorHint.foreground', { dark: Color.fromHex('#eeeeee').transparent(0.7), light: '#6c6c6c', hc: null }, nls.localize('editorHint.foreground', 'Foreground color of hint squigglies in the editor.')); export const editorHintBorder = registerColor('editorHint.border', { dark: null, light: null, hc: Color.fromHex('#eeeeee').transparent(0.8) }, nls.localize('hintBorder', 'Border color of hint boxes in the editor.')); +export const sashHoverBorder = registerColor('sash.hoverBorder', { dark: null, light: null, hc: null }, nls.localize('sashActiveBorder', "Border color of active sashes.")); + /** * Editor background color. * Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254 @@ -353,7 +358,7 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: */ export const listFocusBackground = registerColor('list.focusBackground', { dark: '#062F4A', light: '#D6EBFF', hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hc: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0074E8', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0060C0', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hc: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', { dark: null, light: null, hc: null }, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); diff --git a/src/vs/platform/theme/common/iconRegistry.ts b/src/vs/platform/theme/common/iconRegistry.ts index da0df367..a19a41a8 100644 --- a/src/vs/platform/theme/common/iconRegistry.ts +++ b/src/vs/platform/theme/common/iconRegistry.ts @@ -12,6 +12,7 @@ import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/plat import { RunOnceScheduler } from 'vs/base/common/async'; import * as Codicons from 'vs/base/common/codicons'; + // ------ API types @@ -29,14 +30,14 @@ export interface IconDefinition { export interface IconContribution { id: string; - description: string; + description: string | undefined; deprecationMessage?: string; defaults: IconDefaults; } export interface IIconRegistry { - readonly onDidChangeSchema: Event; + readonly onDidChange: Event; /** * Register a icon to the registry. @@ -44,7 +45,7 @@ export interface IIconRegistry { * @param defaults The default values * @description the description */ - registerIcon(id: string, defaults: IconDefaults, description: string): ThemeIcon; + registerIcon(id: string, defaults: IconDefaults, description?: string): ThemeIcon; /** * Register a icon to the registry. @@ -71,12 +72,17 @@ export interface IIconRegistry { */ getIconReferenceSchema(): IJSONSchema; + /** + * The CSS for all icons + */ + getCSS(): string; + } class IconRegistry implements IIconRegistry { - private readonly _onDidChangeSchema = new Emitter(); - readonly onDidChangeSchema: Event = this._onDidChangeSchema.event; + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; private iconsById: { [key: string]: IconContribution }; private iconSchema: IJSONSchema & { properties: IJSONSchemaMap } = { @@ -101,8 +107,18 @@ class IconRegistry implements IIconRegistry { } public registerIcon(id: string, defaults: IconDefaults, description?: string, deprecationMessage?: string): ThemeIcon { - if (!description) { - description = localize('icon.defaultDescription', 'Icon with identifier \'{0}\'', id); + const existing = this.iconsById[id]; + if (existing) { + if (description && !existing.description) { + existing.description = description; + this.iconSchema.properties[id].markdownDescription = `${description} $(${id})`; + const enumIndex = this.iconReferenceSchema.enum.indexOf(id); + if (enumIndex !== -1) { + this.iconReferenceSchema.enumDescriptions[enumIndex] = description; + } + this._onDidChange.fire(); + } + return existing; } let iconContribution: IconContribution = { id, description, defaults, deprecationMessage }; this.iconsById[id] = iconContribution; @@ -110,12 +126,14 @@ class IconRegistry implements IIconRegistry { if (deprecationMessage) { propertySchema.deprecationMessage = deprecationMessage; } - propertySchema.markdownDescription = `${description}: $(${id})`; + if (description) { + propertySchema.markdownDescription = `${description}: $(${id})`; + } this.iconSchema.properties[id] = propertySchema; this.iconReferenceSchema.enum.push(id); - this.iconReferenceSchema.enumDescriptions.push(description); + this.iconReferenceSchema.enumDescriptions.push(description || ''); - this._onDidChangeSchema.fire(); + this._onDidChange.fire(); return { id }; } @@ -128,7 +146,7 @@ class IconRegistry implements IIconRegistry { this.iconReferenceSchema.enum.splice(index, 1); this.iconReferenceSchema.enumDescriptions.splice(index, 1); } - this._onDidChangeSchema.fire(); + this._onDidChange.fire(); } public getIcons(): IconContribution[] { @@ -147,6 +165,29 @@ class IconRegistry implements IIconRegistry { return this.iconReferenceSchema; } + public getCSS() { + const rules = []; + for (let id in this.iconsById) { + const rule = this.formatRule(id); + if (rule) { + rules.push(rule); + } + } + return rules.join('\n'); + } + + private formatRule(id: string): string | undefined { + let definition = this.iconsById[id].defaults; + while (ThemeIcon.isThemeIcon(definition)) { + const c = this.iconsById[definition.id]; + if (!c) { + return undefined; + } + definition = c.defaults; + } + return `.codicon-${id}:before { content: '${definition.character}'; }`; + } + public toString() { const sorter = (i1: IconContribution, i2: IconContribution) => { const isThemeIcon1 = ThemeIcon.isThemeIcon(i1.defaults); @@ -169,7 +210,7 @@ class IconRegistry implements IIconRegistry { const contributions = Object.keys(this.iconsById).map(key => this.iconsById[key]); for (const i of contributions.sort(sorter)) { - reference.push(`||${i.id}|${ThemeIcon.isThemeIcon(i.defaults) ? i.defaults.id : ''}|`); + reference.push(`||${i.id}|${ThemeIcon.isThemeIcon(i.defaults) ? i.defaults.id : ''}|${i.description || ''}|`); if (!ThemeIcon.isThemeIcon((i.defaults))) { docCss.push(`.codicon-${i.id}:before { content: "${i.defaults.character}" }`); @@ -183,7 +224,7 @@ class IconRegistry implements IIconRegistry { const iconRegistry = new IconRegistry(); platform.Registry.add(Extensions.IconContribution, iconRegistry); -export function registerIcon(id: string, defaults: IconDefaults, description?: string, deprecationMessage?: string): ThemeIcon { +export function registerIcon(id: string, defaults: IconDefaults, description: string, deprecationMessage?: string): ThemeIcon { return iconRegistry.registerIcon(id, defaults, description, deprecationMessage); } @@ -193,20 +234,19 @@ export function getIconRegistry(): IIconRegistry { function initialize() { for (const icon of Codicons.iconRegistry.all) { - registerIcon(icon.id, icon.definition); + iconRegistry.registerIcon(icon.id, icon.definition, icon.description); } - Codicons.iconRegistry.onDidRegister(icon => registerIcon(icon.id, icon.definition)); + Codicons.iconRegistry.onDidRegister(icon => iconRegistry.registerIcon(icon.id, icon.definition, icon.description)); } initialize(); - export const iconsSchemaId = 'vscode://schemas/icons'; let schemaRegistry = platform.Registry.as(JSONExtensions.JSONContribution); schemaRegistry.registerSchema(iconsSchemaId, iconRegistry.getIconSchema()); const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(iconsSchemaId), 200); -iconRegistry.onDidChangeSchema(() => { +iconRegistry.onDidChange(() => { if (!delayer.isScheduled()) { delayer.schedule(); } @@ -214,3 +254,11 @@ iconRegistry.onDidChangeSchema(() => { //setTimeout(_ => console.log(iconRegistry.toString()), 5000); + + +// common icons + +export const widgetClose = registerIcon('widget-close', Codicons.Codicon.close, localize('widgetClose', 'Icon for the close action in widgets.')); + +export const gotoPreviousLocation = registerIcon('goto-previous-location', Codicons.Codicon.arrowUp, localize('previousChangeIcon', 'Icon for goto previous editor location.')); +export const gotoNextLocation = registerIcon('goto-next-location', Codicons.Codicon.arrowDown, localize('nextChangeIcon', 'Icon for goto next editor location.')); diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 0ae9c30f..f82c4576 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -11,6 +11,7 @@ import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { CSSIcon } from 'vs/base/common/codicons'; export const IThemeService = createDecorator('themeService'); @@ -18,6 +19,12 @@ export interface ThemeColor { id: string; } +export namespace ThemeColor { + export function isThemeColor(obj: any): obj is ThemeColor { + return obj && typeof obj === 'object' && typeof (obj).id === 'string'; + } +} + export function themeColorFromId(id: ColorIdentifier) { return { id }; } @@ -29,8 +36,8 @@ export interface ThemeIcon { } export namespace ThemeIcon { - export function isThemeIcon(obj: any): obj is ThemeIcon | { id: string } { - return obj && typeof obj === 'object' && typeof (obj).id === 'string'; + export function isThemeIcon(obj: any): obj is ThemeIcon { + return obj && typeof obj === 'object' && typeof (obj).id === 'string' && (typeof (obj).color === 'undefined' || ThemeColor.isThemeColor((obj).color)); } const _regexFromString = /^\$\(([a-z.]+\/)?([a-z-~]+)\)$/i; @@ -41,26 +48,67 @@ export namespace ThemeIcon { return undefined; } let [, owner, name] = match; - if (!owner) { - owner = `codicon/`; + if (!owner || owner === 'codicon/') { + return { id: name }; } return { id: owner + name }; } + export function modify(icon: ThemeIcon, modifier: 'disabled' | 'spin' | undefined): ThemeIcon { + let id = icon.id; + const tildeIndex = id.lastIndexOf('~'); + if (tildeIndex !== -1) { + id = id.substring(0, tildeIndex); + } + if (modifier) { + id = `${id}~${modifier}`; + } + return { id }; + } + + export function isEqual(ti1: ThemeIcon, ti2: ThemeIcon): boolean { + return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id; + } + const _regexAsClassName = /^(codicon\/)?([a-z-]+)(~[a-z]+)?$/i; - export function asClassName(icon: ThemeIcon): string | undefined { - // todo@martin,joh -> this should go into the ThemeService + export function asClassNameArray(icon: ThemeIcon): string[] { const match = _regexAsClassName.exec(icon.id); if (!match) { - return undefined; + return ['codicon', 'codicon-error']; } let [, , name, modifier] = match; - let className = `codicon codicon-${name}`; + let className = `codicon-${name}`; if (modifier) { - className += ` ${modifier.substr(1)}`; + return ['codicon', className, modifier.substr(1)]; } - return className; + return ['codicon', className]; + } + + + export function asClassName(icon: ThemeIcon): string { + return asClassNameArray(icon).join(' '); + } + + export function asCSSSelector(icon: ThemeIcon): string { + return '.' + asClassNameArray(icon).join('.'); + } + + export function asCSSIcon(icon: ThemeIcon): CSSIcon { + return { + classNames: asClassName(icon) + }; + } + + export function asCodiconLabel(icon: ThemeIcon): string { + return '$(' + icon.id + ')'; + } + + export function revive(icon: any): ThemeIcon | undefined { + if (ThemeIcon.isThemeIcon(icon)) { + return { id: icon.id, color: icon.color ? { id: icon.color.id } : undefined }; + } + return undefined; } } diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index ab705dbc..cf7a8df0 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -522,8 +522,9 @@ function createDefaultTokenClassificationRegistry(): TokenClassificationRegistry registerTokenType('typeParameter', nls.localize('typeParameter', "Style for type parameters."), [['entity.name.type.parameter']]); registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]); - registerTokenType('member', nls.localize('member', "Style for member"), [['entity.name.function.member'], ['support.function']]); - registerTokenType('macro', nls.localize('macro', "Style for macros."), [['entity.name.other.preprocessor.macro']]); + registerTokenType('member', nls.localize('member', "Style for member functions"), [], 'method', 'Deprecated use `method` instead'); + registerTokenType('method', nls.localize('method', "Style for method (member functions)"), [['entity.name.function.member'], ['support.function']]); + registerTokenType('macro', nls.localize('macro', "Style for macros."), [['entity.name.function.preprocessor']]); registerTokenType('variable', nls.localize('variable', "Style for variables."), [['variable.other.readwrite'], ['entity.name.variable']]); registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), [['variable.parameter']]); diff --git a/src/vs/platform/theme/electron-main/themeMainService.ts b/src/vs/platform/theme/electron-main/themeMainService.ts index 7bbacdb3..1d07cf7b 100644 --- a/src/vs/platform/theme/electron-main/themeMainService.ts +++ b/src/vs/platform/theme/electron-main/themeMainService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isWindows, isMacintosh } from 'vs/base/common/platform'; -import { ipcMain as ipc, nativeTheme } from 'electron'; +import { ipcMain, nativeTheme } from 'electron'; import { IStateService } from 'vs/platform/state/node/state'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -28,7 +28,7 @@ export class ThemeMainService implements IThemeMainService { declare readonly _serviceBrand: undefined; constructor(@IStateService private stateService: IStateService) { - ipc.on('vscode:changeColorTheme', (e: Event, windowId: number, broadcast: string) => { + ipcMain.on('vscode:changeColorTheme', (e: Event, windowId: number, broadcast: string) => { // Theme changes if (typeof broadcast === 'string') { this.storeBackgroundColor(JSON.parse(broadcast)); diff --git a/src/vs/platform/undoRedo/common/undoRedo.ts b/src/vs/platform/undoRedo/common/undoRedo.ts index c70e013d..021003ba 100644 --- a/src/vs/platform/undoRedo/common/undoRedo.ts +++ b/src/vs/platform/undoRedo/common/undoRedo.ts @@ -81,6 +81,27 @@ export class UndoRedoGroup { public static None = new UndoRedoGroup(); } +export class UndoRedoSource { + private static _ID = 0; + + public readonly id: number; + private order: number; + + constructor() { + this.id = UndoRedoSource._ID++; + this.order = 1; + } + + public nextOrder(): number { + if (this.id === 0) { + return 0; + } + return this.order++; + } + + public static None = new UndoRedoSource(); +} + export interface IUndoRedoService { readonly _serviceBrand: undefined; @@ -100,7 +121,7 @@ export interface IUndoRedoService { * Add a new element to the `undo` stack. * This will destroy the `redo` stack. */ - pushElement(element: IUndoRedoElement, group?: UndoRedoGroup): void; + pushElement(element: IUndoRedoElement, group?: UndoRedoGroup, source?: UndoRedoSource): void; /** * Get the last pushed element for a resource. @@ -133,9 +154,9 @@ export interface IUndoRedoService { */ restoreSnapshot(snapshot: ResourceEditStackSnapshot): void; - canUndo(resource: URI): boolean; - undo(resource: URI): Promise | void; + canUndo(resource: URI | UndoRedoSource): boolean; + undo(resource: URI | UndoRedoSource): Promise | void; - canRedo(resource: URI): boolean; - redo(resource: URI): Promise | void; + canRedo(resource: URI | UndoRedoSource): boolean; + redo(resource: URI | UndoRedoSource): Promise | void; } diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 2a5bdb84..2fb802fb 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot, UriComparisonKeyComputer, IResourceUndoRedoElement, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot, UriComparisonKeyComputer, IResourceUndoRedoElement, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { onUnexpectedError } from 'vs/base/common/errors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -34,9 +34,11 @@ class ResourceStackElement { public readonly strResources: string[]; public readonly groupId: number; public readonly groupOrder: number; + public readonly sourceId: number; + public readonly sourceOrder: number; public isValid: boolean; - constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string, groupId: number, groupOrder: number) { + constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string, groupId: number, groupOrder: number, sourceId: number, sourceOrder: number) { this.actual = actual; this.label = actual.label; this.resourceLabel = resourceLabel; @@ -45,6 +47,8 @@ class ResourceStackElement { this.strResources = [this.strResource]; this.groupId = groupId; this.groupOrder = groupOrder; + this.sourceId = sourceId; + this.sourceOrder = sourceOrder; this.isValid = true; } @@ -130,16 +134,20 @@ class WorkspaceStackElement { public readonly strResources: string[]; public readonly groupId: number; public readonly groupOrder: number; + public readonly sourceId: number; + public readonly sourceOrder: number; public removedResources: RemovedResources | null; public invalidatedResources: RemovedResources | null; - constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[], groupId: number, groupOrder: number) { + constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[], groupId: number, groupOrder: number, sourceId: number, sourceOrder: number) { this.actual = actual; this.label = actual.label; this.resourceLabels = resourceLabels; this.strResources = strResources; this.groupId = groupId; this.groupOrder = groupOrder; + this.sourceId = sourceId; + this.sourceOrder = sourceOrder; this.removedResources = null; this.invalidatedResources = null; } @@ -490,11 +498,11 @@ export class UndoRedoService implements IUndoRedoService { console.log(str.join('\n')); } - public pushElement(element: IUndoRedoElement, group: UndoRedoGroup = UndoRedoGroup.None): void { + public pushElement(element: IUndoRedoElement, group: UndoRedoGroup = UndoRedoGroup.None, source: UndoRedoSource = UndoRedoSource.None): void { if (element.type === UndoRedoElementType.Resource) { const resourceLabel = getResourceLabel(element.resource); const strResource = this.getUriComparisonKey(element.resource); - this._pushElement(new ResourceStackElement(element, resourceLabel, strResource, group.id, group.nextOrder())); + this._pushElement(new ResourceStackElement(element, resourceLabel, strResource, group.id, group.nextOrder(), source.id, source.nextOrder())); } else { const seen = new Set(); const resourceLabels: string[] = []; @@ -512,9 +520,9 @@ export class UndoRedoService implements IUndoRedoService { } if (resourceLabels.length === 1) { - this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0], group.id, group.nextOrder())); + this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0], group.id, group.nextOrder(), source.id, source.nextOrder())); } else { - this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources, group.id, group.nextOrder())); + this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources, group.id, group.nextOrder(), source.id, source.nextOrder())); } } if (DEBUG) { @@ -558,7 +566,7 @@ export class UndoRedoService implements IUndoRedoService { for (const _element of individualArr) { const resourceLabel = getResourceLabel(_element.resource); const strResource = this.getUriComparisonKey(_element.resource); - const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0); + const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0, 0, 0); individualMap.set(element.strResource, element); } @@ -577,7 +585,7 @@ export class UndoRedoService implements IUndoRedoService { for (const _element of individualArr) { const resourceLabel = getResourceLabel(_element.resource); const strResource = this.getUriComparisonKey(_element.resource); - const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0); + const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0, 0, 0); individualMap.set(element.strResource, element); } @@ -657,8 +665,37 @@ export class UndoRedoService implements IUndoRedoService { return { past: [], future: [] }; } - public canUndo(resource: URI): boolean { - const strResource = this.getUriComparisonKey(resource); + private _findClosestUndoElementWithSource(sourceId: number): [StackElement | null, string | null] { + if (!sourceId) { + return [null, null]; + } + + // find an element with the sourceId and with the highest sourceOrder ready to be undone + let matchedElement: StackElement | null = null; + let matchedStrResource: string | null = null; + + for (const [strResource, editStack] of this._editStacks) { + const candidate = editStack.getClosestPastElement(); + if (!candidate) { + continue; + } + if (candidate.sourceId === sourceId) { + if (!matchedElement || candidate.sourceOrder > matchedElement.sourceOrder) { + matchedElement = candidate; + matchedStrResource = strResource; + } + } + } + + return [matchedElement, matchedStrResource]; + } + + public canUndo(resourceOrSource: URI | UndoRedoSource): boolean { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestUndoElementWithSource(resourceOrSource.id); + return matchedStrResource ? true : false; + } + const strResource = this.getUriComparisonKey(resourceOrSource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; return editStack.hasPastElements(); @@ -774,7 +811,7 @@ export class UndoRedoService implements IUndoRedoService { if (element.canSplit()) { this._splitPastWorkspaceElement(element, ignoreResources); this._notificationService.info(message); - return new WorkspaceVerificationError(this.undo(strResource)); + return new WorkspaceVerificationError(this._undo(strResource)); } else { // Cannot safely split this workspace element => flush all undo/redo stacks for (const strResource of element.strResources) { @@ -922,7 +959,7 @@ export class UndoRedoService implements IUndoRedoService { if (result.choice === 1) { // choice: undo this file this._splitPastWorkspaceElement(element, null); - return this.undo(strResource); + return this._undo(strResource); } // choice: undo in all files @@ -1007,12 +1044,22 @@ export class UndoRedoService implements IUndoRedoService { const [, matchedStrResource] = this._findClosestUndoElementInGroup(groupId); if (matchedStrResource) { - return this.undo(matchedStrResource); + return this._undo(matchedStrResource); } } - public undo(resource: URI | string): Promise | void { - const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource); + public undo(resourceOrSource: URI | UndoRedoSource): Promise | void { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestUndoElementWithSource(resourceOrSource.id); + return matchedStrResource ? this._undo(matchedStrResource, resourceOrSource.id) : undefined; + } + if (typeof resourceOrSource === 'string') { + return this._undo(resourceOrSource); + } + return this._undo(this.getUriComparisonKey(resourceOrSource)); + } + + private _undo(strResource: string, sourceId: number = 0): Promise | void { if (!this._editStacks.has(strResource)) { return; } @@ -1028,10 +1075,15 @@ export class UndoRedoService implements IUndoRedoService { const [matchedElement, matchedStrResource] = this._findClosestUndoElementInGroup(element.groupId); if (element !== matchedElement && matchedStrResource) { // there is an element in the same group that should be undone before this one - return this.undo(matchedStrResource); + return this._undo(matchedStrResource); } } + if (element.sourceId !== sourceId) { + // Hit a different source, prompt for confirmation + return this._confirmDifferentSourceAndContinueUndo(strResource, element); + } + try { if (element.type === UndoRedoElementType.Workspace) { return this._workspaceUndo(strResource, element); @@ -1045,8 +1097,59 @@ export class UndoRedoService implements IUndoRedoService { } } - public canRedo(resource: URI): boolean { - const strResource = this.getUriComparisonKey(resource); + private async _confirmDifferentSourceAndContinueUndo(strResource: string, element: StackElement): Promise { + const result = await this._dialogService.show( + Severity.Info, + nls.localize('confirmDifferentSource', "Would you like to undo '{0}'?", element.label), + [ + nls.localize('confirmDifferentSource.ok', "Undo"), + nls.localize('cancel', "Cancel"), + ], + { + cancelId: 1 + } + ); + + if (result.choice === 1) { + // choice: cancel + return; + } + + // choice: undo + return this._undo(strResource, element.sourceId); + } + + private _findClosestRedoElementWithSource(sourceId: number): [StackElement | null, string | null] { + if (!sourceId) { + return [null, null]; + } + + // find an element with sourceId and with the lowest sourceOrder ready to be redone + let matchedElement: StackElement | null = null; + let matchedStrResource: string | null = null; + + for (const [strResource, editStack] of this._editStacks) { + const candidate = editStack.getClosestFutureElement(); + if (!candidate) { + continue; + } + if (candidate.sourceId === sourceId) { + if (!matchedElement || candidate.sourceOrder < matchedElement.sourceOrder) { + matchedElement = candidate; + matchedStrResource = strResource; + } + } + } + + return [matchedElement, matchedStrResource]; + } + + public canRedo(resourceOrSource: URI | UndoRedoSource): boolean { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestRedoElementWithSource(resourceOrSource.id); + return matchedStrResource ? true : false; + } + const strResource = this.getUriComparisonKey(resourceOrSource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; return editStack.hasFutureElements(); @@ -1058,7 +1161,7 @@ export class UndoRedoService implements IUndoRedoService { if (element.canSplit()) { this._splitFutureWorkspaceElement(element, ignoreResources); this._notificationService.info(message); - return new WorkspaceVerificationError(this.redo(strResource)); + return new WorkspaceVerificationError(this._redo(strResource)); } else { // Cannot safely split this workspace element => flush all undo/redo stacks for (const strResource of element.strResources) { @@ -1230,12 +1333,22 @@ export class UndoRedoService implements IUndoRedoService { const [, matchedStrResource] = this._findClosestRedoElementInGroup(groupId); if (matchedStrResource) { - return this.redo(matchedStrResource); + return this._redo(matchedStrResource); } } - public redo(resource: URI | string): Promise | void { - const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource); + public redo(resourceOrSource: URI | UndoRedoSource | string): Promise | void { + if (resourceOrSource instanceof UndoRedoSource) { + const [, matchedStrResource] = this._findClosestRedoElementWithSource(resourceOrSource.id); + return matchedStrResource ? this._redo(matchedStrResource) : undefined; + } + if (typeof resourceOrSource === 'string') { + return this._redo(resourceOrSource); + } + return this._redo(this.getUriComparisonKey(resourceOrSource)); + } + + private _redo(strResource: string): Promise | void { if (!this._editStacks.has(strResource)) { return; } @@ -1251,7 +1364,7 @@ export class UndoRedoService implements IUndoRedoService { const [matchedElement, matchedStrResource] = this._findClosestRedoElementInGroup(element.groupId); if (element !== matchedElement && matchedStrResource) { // there is an element in the same group that should be redone before this one - return this.redo(matchedStrResource); + return this._redo(matchedStrResource); } } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index ce1033b1..2e18cc8c 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -56,7 +56,8 @@ export class DarwinUpdateService extends AbstractUpdateService { } protected buildUpdateFeedUrl(quality: string): string | undefined { - const url = createUpdateURL('darwin', quality); + const assetID = process.arch === 'x64' ? 'darwin' : 'darwin-arm64'; + const url = createUpdateURL(assetID, quality); try { electron.autoUpdater.setFeedURL({ url }); } catch (e) { diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index 2069fc48..5202226d 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -18,6 +18,8 @@ export interface IOpenURLOptions { * might be shown to the user. */ trusted?: boolean; + + originalUrl?: string; } export interface IURLHandler { diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/common/urlIpc.ts index 08d19ffd..52fad0e2 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/common/urlIpc.ts @@ -19,7 +19,7 @@ export class URLHandlerChannel implements IServerChannel { call(_: unknown, command: string, arg?: any): Promise { switch (command) { - case 'handleURL': return this.handler.handleURL(URI.revive(arg)); + case 'handleURL': return this.handler.handleURL(URI.revive(arg[0]), arg[1]); } throw new Error(`Call not found: ${command}`); @@ -31,7 +31,7 @@ export class URLHandlerChannelClient implements IURLHandler { constructor(private channel: IChannel) { } handleURL(uri: URI, options?: IOpenURLOptions): Promise { - return this.channel.call('handleURL', uri.toJSON()); + return this.channel.call('handleURL', [uri.toJSON(), options]); } } diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index efc15e02..50ca2ea2 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -34,13 +34,13 @@ function uriFromRawUrl(url: string): URI | null { */ export class ElectronURLListener { - private uris: URI[] = []; + private uris: { uri: URI, url: string }[] = []; private retryCount = 0; private flushDisposable: IDisposable = Disposable.None; private disposables = new DisposableStore(); constructor( - initialUrisToHandle: URI[], + initialUrisToHandle: { uri: URI, url: string }[], private readonly urlService: IURLService, windowsMainService: IWindowsMainService, environmentService: IEnvironmentMainService @@ -64,8 +64,15 @@ export class ElectronURLListener { return url; }); - const onOpenUrl = Event.filter(Event.map(onOpenElectronUrl, uriFromRawUrl), (uri): uri is URI => !!uri); - onOpenUrl(this.urlService.open, this.urlService, this.disposables); + this.disposables.add(onOpenElectronUrl(url => { + const uri = uriFromRawUrl(url); + + if (!uri) { + return; + } + + this.urlService.open(uri, { originalUrl: url }); + })); // Send initial links to the window once it has loaded const isWindowReady = windowsMainService.getWindows() @@ -84,13 +91,13 @@ export class ElectronURLListener { return; } - const uris: URI[] = []; + const uris: { uri: URI, url: string }[] = []; - for (const uri of this.uris) { - const handled = await this.urlService.open(uri); + for (const obj of this.uris) { + const handled = await this.urlService.open(obj.uri, { originalUrl: obj.url }); if (!handled) { - uris.push(uri); + uris.push(obj); } } diff --git a/src/vs/platform/userDataSync/common/extensionsStorageSync.ts b/src/vs/platform/userDataSync/common/extensionsStorageSync.ts new file mode 100644 index 00000000..37846e05 --- /dev/null +++ b/src/vs/platform/userDataSync/common/extensionsStorageSync.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; + +export interface IExtensionIdWithVersion { + id: string; + version: string; +} + +export const IExtensionsStorageSyncService = createDecorator('IExtensionsStorageSyncService'); + +export interface IExtensionsStorageSyncService { + + _serviceBrand: any; + + readonly onDidChangeExtensionsStorage: Event; + setKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion, keys: string[]): void; + getKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion): string[] | undefined; + +} + +const EXTENSION_KEYS_ID_VERSION_REGEX = /^extensionKeys\/([^.]+\..+)@(\d+\.\d+\.\d+(-.*)?)$/; + +export class ExtensionsStorageSyncService extends Disposable implements IExtensionsStorageSyncService { + + declare readonly _serviceBrand: undefined; + + private static toKey(extension: IExtensionIdWithVersion): string { + return `extensionKeys/${extension.id}@${extension.version}`; + } + + private static fromKey(key: string): IExtensionIdWithVersion | undefined { + const matches = EXTENSION_KEYS_ID_VERSION_REGEX.exec(key); + if (matches && matches[1]) { + return { id: matches[1], version: matches[2] }; + } + return undefined; + } + + private readonly _onDidChangeExtensionsStorage = this._register(new Emitter()); + readonly onDidChangeExtensionsStorage = this._onDidChangeExtensionsStorage.event; + + private readonly extensionsWithKeysForSync = new Set(); + + constructor( + @IStorageService private readonly storageService: IStorageService, + ) { + super(); + this.initialize(); + this._register(this.storageService.onDidChangeValue(e => this.onDidChangeStorageValue(e))); + } + + private initialize(): void { + const keys = this.storageService.keys(StorageScope.GLOBAL, StorageTarget.MACHINE); + for (const key of keys) { + const extensionIdWithVersion = ExtensionsStorageSyncService.fromKey(key); + if (extensionIdWithVersion) { + this.extensionsWithKeysForSync.add(extensionIdWithVersion.id.toLowerCase()); + } + } + } + + private onDidChangeStorageValue(e: IStorageValueChangeEvent): void { + if (e.scope !== StorageScope.GLOBAL) { + return; + } + + // State of extension with keys for sync has changed + if (this.extensionsWithKeysForSync.has(e.key.toLowerCase())) { + this._onDidChangeExtensionsStorage.fire(); + return; + } + + // Keys for sync of an extension has changed + const extensionIdWithVersion = ExtensionsStorageSyncService.fromKey(e.key); + if (extensionIdWithVersion) { + this.extensionsWithKeysForSync.add(extensionIdWithVersion.id.toLowerCase()); + this._onDidChangeExtensionsStorage.fire(); + return; + } + } + + setKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion, keys: string[]): void { + this.storageService.store(ExtensionsStorageSyncService.toKey(extensionIdWithVersion), JSON.stringify(keys), StorageScope.GLOBAL, StorageTarget.MACHINE); + } + + getKeysForSync(extensionIdWithVersion: IExtensionIdWithVersion): string[] | undefined { + const keysForSyncValue = this.storageService.get(ExtensionsStorageSyncService.toKey(extensionIdWithVersion), StorageScope.GLOBAL); + return keysForSyncValue ? JSON.parse(keysForSyncValue) : undefined; + } +} diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index f34669f8..e716e135 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -21,12 +21,12 @@ import { URI } from 'vs/base/common/uri'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { compare } from 'vs/base/common/strings'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { getErrorMessage } from 'vs/base/common/errors'; import { forEach, IStringDictionary } from 'vs/base/common/collections'; +import { IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; interface IExtensionResourceMergeResult extends IAcceptResult { readonly added: ISyncExtension[]; @@ -105,7 +105,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @ITelemetryService telemetryService: ITelemetryService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + @IExtensionsStorageSyncService private readonly extensionsStorageSyncService: IExtensionsStorageSyncService, ) { super(SyncResource.Extensions, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); this._register( @@ -114,16 +114,14 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse Event.filter(this.extensionManagementService.onDidInstallExtension, (e => !!e.gallery)), Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)), this.extensionEnablementService.onDidChangeEnablement, - this.storageKeysSyncRegistryService.onDidChangeExtensionStorageKeys, - Event.filter(this.storageService.onDidChangeStorage, e => e.scope === StorageScope.GLOBAL - && this.storageKeysSyncRegistryService.extensionsStorageKeys.some(([extensionIdentifier]) => areSameExtensions(extensionIdentifier, { id: e.key })))), + this.extensionsStorageSyncService.onDidChangeExtensionsStorage), () => undefined, 500)(() => this.triggerLocalChange())); } protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? await parseAndMigrateExtensions(remoteUserData.syncData, this.extensionManagementService) : null; - const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : []; - const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? await parseAndMigrateExtensions(lastSyncUserData.syncData!, this.extensionManagementService) : null; + const skippedExtensions: ISyncExtension[] = lastSyncUserData?.skippedExtensions || []; + const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData?.syncData ? await parseAndMigrateExtensions(lastSyncUserData.syncData, this.extensionManagementService) : null; const installedExtensions = await this.extensionManagementService.getInstalled(); const localExtensions = this.getLocalExtensions(installedExtensions); @@ -352,7 +350,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse const extensionsToRemove = installedExtensions.filter(({ identifier, isBuiltin }) => !isBuiltin && removed.some(r => areSameExtensions(identifier, r))); await Promise.all(extensionsToRemove.map(async extensionToRemove => { this.logService.trace(`${this.syncResourceLogLabel}: Uninstalling local extension...`, extensionToRemove.identifier.id); - await this.extensionManagementService.uninstall(extensionToRemove); + await this.extensionManagementService.uninstall(extensionToRemove, { donotIncludePack: true, donotCheckDependents: true }); this.logService.info(`${this.syncResourceLogLabel}: Uninstalled local extension.`, extensionToRemove.identifier.id); removeFromSkipped.push(extensionToRemove.identifier); })); @@ -365,9 +363,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse // Builtin Extension Sync: Enablement & State if (installedExtension && installedExtension.isBuiltin) { if (e.state && installedExtension.manifest.version === e.version) { - const extensionState = JSON.parse(this.storageService.get(e.identifier.id, StorageScope.GLOBAL) || '{}'); - forEach(e.state, ({ key, value }) => extensionState[key] = value); - this.storageService.store(e.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL); + this.updateExtensionState(e.state, e.identifier.id, installedExtension.manifest.version); } if (e.disabled) { this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...`, e.identifier.id); @@ -393,9 +389,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse (installedExtension ? installedExtension.manifest.version === e.version /* Installed and has same version */ : !!extension /* Installable */) ) { - const extensionState = JSON.parse(this.storageService.get(e.identifier.id, StorageScope.GLOBAL) || '{}'); - forEach(e.state, ({ key, value }) => extensionState[key] = value); - this.storageService.store(e.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL); + this.updateExtensionState(e.state, e.identifier.id, installedExtension?.manifest.version); } if (extension) { @@ -413,7 +407,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse // Install only if the extension does not exist if (!installedExtension) { this.logService.trace(`${this.syncResourceLogLabel}: Installing extension...`, e.identifier.id, extension.version); - await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false } /* pass options to prevent install and sync dialog in web */); + await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false, donotIncludePackAndDependencies: true } /* pass options to prevent install and sync dialog in web */); this.logService.info(`${this.syncResourceLogLabel}: Installed extension.`, e.identifier.id, extension.version); removeFromSkipped.push(extension.identifier); } @@ -442,6 +436,17 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse return newSkippedExtensions; } + private updateExtensionState(state: IStringDictionary, id: string, version?: string): void { + const extensionState = JSON.parse(this.storageService.get(id, StorageScope.GLOBAL) || '{}'); + const keys = version ? this.extensionsStorageSyncService.getKeysForSync({ id, version }) : undefined; + if (keys) { + keys.forEach(key => extensionState[key] = state[key]); + } else { + forEach(state, ({ key, value }) => extensionState[key] = value); + } + this.storageService.store(id, JSON.stringify(extensionState), StorageScope.GLOBAL, StorageTarget.MACHINE); + } + private parseExtensions(syncData: ISyncData): ISyncExtension[] { return JSON.parse(syncData.content); } @@ -457,10 +462,10 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse if (!isBuiltin) { syncExntesion.installed = true; } - const keys = this.storageKeysSyncRegistryService.getExtensioStorageKeys({ id: identifier.id, version: manifest.version }); - if (keys) { - const extensionStorageValue = this.storageService.get(identifier.id, StorageScope.GLOBAL) || '{}'; - try { + try { + const keys = this.extensionsStorageSyncService.getKeysForSync({ id: identifier.id, version: manifest.version }); + if (keys) { + const extensionStorageValue = this.storageService.get(identifier.id, StorageScope.GLOBAL) || '{}'; const extensionStorageState = JSON.parse(extensionStorageValue); syncExntesion.state = Object.keys(extensionStorageState).reduce((state: IStringDictionary, key) => { if (keys.includes(key)) { @@ -468,9 +473,9 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse } return state; }, {}); - } catch (error) { - this.logService.info(`${this.syncResourceLogLabel}: Error while parsing extension state`, getErrorMessage(error)); } + } catch (error) { + this.logService.info(`${this.syncResourceLogLabel}: Error while parsing extension state`, getErrorMessage(error)); } return syncExntesion; }); @@ -499,6 +504,11 @@ export class ExtensionsInitializer extends AbstractInitializer { return; } + await this.initializeRemoteExtensions(remoteExtensions); + } + + protected async initializeRemoteExtensions(remoteExtensions: ISyncExtension[]): Promise { + const newlyEnabledExtensions: ILocalExtension[] = []; const installedExtensions = await this.extensionManagementService.getInstalled(); const newExtensionsToSync = new Map(); const installedExtensionsToSync: ISyncExtension[] = []; @@ -528,10 +538,13 @@ export class ExtensionsInitializer extends AbstractInitializer { try { const extensionToSync = newExtensionsToSync.get(galleryExtension.identifier.id.toLowerCase())!; if (extensionToSync.state) { - this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionToSync.state), StorageScope.GLOBAL); + this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionToSync.state), StorageScope.GLOBAL, StorageTarget.MACHINE); } this.logService.trace(`Installing extension...`, galleryExtension.identifier.id); - await this.extensionManagementService.installFromGallery(galleryExtension, { isMachineScoped: false } /* pass options to prevent install and sync dialog in web */); + const local = await this.extensionManagementService.installFromGallery(galleryExtension, { isMachineScoped: false } /* pass options to prevent install and sync dialog in web */); + if (!toDisable.some(identifier => areSameExtensions(identifier, galleryExtension.identifier))) { + newlyEnabledExtensions.push(local); + } this.logService.info(`Installed extension.`, galleryExtension.identifier.id); } catch (error) { this.logService.error(error); @@ -551,11 +564,11 @@ export class ExtensionsInitializer extends AbstractInitializer { if (extensionToSync.state) { const extensionState = JSON.parse(this.storageService.get(extensionToSync.identifier.id, StorageScope.GLOBAL) || '{}'); forEach(extensionToSync.state, ({ key, value }) => extensionState[key] = value); - this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL); + this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL, StorageTarget.MACHINE); } } + + return newlyEnabledExtensions; } } - - diff --git a/src/vs/platform/userDataSync/common/globalStateMerge.ts b/src/vs/platform/userDataSync/common/globalStateMerge.ts index 8af4c4de..e519854a 100644 --- a/src/vs/platform/userDataSync/common/globalStateMerge.ts +++ b/src/vs/platform/userDataSync/common/globalStateMerge.ts @@ -6,24 +6,22 @@ import * as objects from 'vs/base/common/objects'; import { IStorageValue } from 'vs/platform/userDataSync/common/userDataSync'; import { IStringDictionary } from 'vs/base/common/collections'; -import { IStorageKey } from 'vs/platform/userDataSync/common/storageKeys'; import { ILogService } from 'vs/platform/log/common/log'; export interface IMergeResult { local: { added: IStringDictionary, removed: string[], updated: IStringDictionary }; remote: IStringDictionary | null; - skipped: string[]; } -export function merge(localStorage: IStringDictionary, remoteStorage: IStringDictionary | null, baseStorage: IStringDictionary | null, storageKeys: ReadonlyArray, previouslySkipped: string[], logService: ILogService): IMergeResult { +export function merge(localStorage: IStringDictionary, remoteStorage: IStringDictionary | null, baseStorage: IStringDictionary | null, storageKeys: { machine: ReadonlyArray, unregistered: ReadonlyArray }, logService: ILogService): IMergeResult { if (!remoteStorage) { - return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} }, skipped: [] }; + return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} } }; } const localToRemote = compare(localStorage, remoteStorage); if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { // No changes found between local and remote. - return { remote: null, local: { added: {}, removed: [], updated: {} }, skipped: [] }; + return { remote: null, local: { added: {}, removed: [], updated: {} } }; } const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: Object.keys(remoteStorage).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; @@ -31,26 +29,48 @@ export function merge(localStorage: IStringDictionary, remoteStor const local: { added: IStringDictionary, removed: string[], updated: IStringDictionary } = { added: {}, removed: [], updated: {} }; const remote: IStringDictionary = objects.deepClone(remoteStorage); - const skipped: string[] = []; + + // Added in local + for (const key of baseToLocal.added.values()) { + // Skip if local was not synced before and remote also has the key + // In this case, remote gets precedence + if (!baseStorage && baseToRemote.added.has(key)) { + continue; + } else { + remote[key] = localStorage[key]; + } + } + + // Updated in local + for (const key of baseToLocal.updated.values()) { + remote[key] = localStorage[key]; + } + + // Removed in local + for (const key of baseToLocal.removed.values()) { + // Do not remove from remote if key is not registered. + if (storageKeys.unregistered.includes(key)) { + continue; + } + delete remote[key]; + } // Added in remote for (const key of baseToRemote.added.values()) { const remoteValue = remoteStorage[key]; - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - if (!storageKey) { - skipped.push(key); - logService.trace(`GlobalState: Skipped adding ${key} in local storage as it is not registered.`); + if (storageKeys.machine.includes(key)) { + logService.info(`GlobalState: Skipped adding ${key} in local storage because it is declared as machine scoped.`); continue; } - if (storageKey.version !== remoteValue.version) { - logService.info(`GlobalState: Skipped adding ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`); + // Skip if the value is also added in local from the time it is last synced + if (baseStorage && baseToLocal.added.has(key)) { continue; } const localValue = localStorage[key]; if (localValue && localValue.value === remoteValue.value) { continue; } - if (baseToLocal.added.has(key)) { + if (localValue) { local.updated[key] = remoteValue; } else { local.added[key] = remoteValue; @@ -60,14 +80,12 @@ export function merge(localStorage: IStringDictionary, remoteStor // Updated in Remote for (const key of baseToRemote.updated.values()) { const remoteValue = remoteStorage[key]; - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - if (!storageKey) { - skipped.push(key); - logService.trace(`GlobalState: Skipped updating ${key} in local storage as is not registered.`); + if (storageKeys.machine.includes(key)) { + logService.info(`GlobalState: Skipped updating ${key} in local storage because it is declared as machine scoped.`); continue; } - if (storageKey.version !== remoteValue.version) { - logService.info(`GlobalState: Skipped updating ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`); + // Skip if the value is also updated or removed in local + if (baseToLocal.updated.has(key) || baseToLocal.removed.has(key)) { continue; } const localValue = localStorage[key]; @@ -79,67 +97,18 @@ export function merge(localStorage: IStringDictionary, remoteStor // Removed in remote for (const key of baseToRemote.removed.values()) { - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - if (!storageKey) { - logService.trace(`GlobalState: Skipped removing ${key} in local storage. It is not registered to sync.`); + if (storageKeys.machine.includes(key)) { + logService.trace(`GlobalState: Skipped removing ${key} in local storage because it is declared as machine scoped.`); + continue; + } + // Skip if the value is also updated or removed in local + if (baseToLocal.updated.has(key) || baseToLocal.removed.has(key)) { continue; } local.removed.push(key); } - // Added in local - for (const key of baseToLocal.added.values()) { - if (!baseToRemote.added.has(key)) { - remote[key] = localStorage[key]; - } - } - - // Updated in local - for (const key of baseToLocal.updated.values()) { - if (baseToRemote.updated.has(key) || baseToRemote.removed.has(key)) { - continue; - } - const remoteValue = remote[key]; - const localValue = localStorage[key]; - if (localValue.version < remoteValue.version) { - logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${localValue.version}' and remote version '${remoteValue.version} are not same.`); - continue; - } - remote[key] = localValue; - } - - // Removed in local - for (const key of baseToLocal.removed.values()) { - // do not remove from remote if it is updated in remote - if (baseToRemote.updated.has(key)) { - continue; - } - - const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0]; - // do not remove from remote if storage key is not found - if (!storageKey) { - skipped.push(key); - logService.trace(`GlobalState: Skipped removing ${key} in remote storage. It is not registered to sync.`); - continue; - } - - const remoteValue = remote[key]; - // do not remove from remote if local data version is old - if (storageKey.version < remoteValue.version) { - logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`); - continue; - } - - // add to local if it was skipped before - if (previouslySkipped.indexOf(key) !== -1) { - local.added[key] = remote[key]; - continue; - } - - delete remote[key]; - } - - return { local, remote: areSame(remote, remoteStorage) ? null : remote, skipped }; + return { local, remote: areSame(remote, remoteStorage) ? null : remote }; } function compare(from: IStringDictionary, to: IStringDictionary): { added: Set, removed: Set, updated: Set } { diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index c1f4daa7..7c69ad46 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -21,29 +21,34 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { URI } from 'vs/base/common/uri'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService, IStorageKey } from 'vs/platform/userDataSync/common/storageKeys'; -import { equals } from 'vs/base/common/arrays'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { CancellationToken } from 'vs/base/common/cancellation'; const argvStoragePrefx = 'globalState.argv.'; const argvProperties: string[] = ['locale']; +type StorageKeys = { machine: string[], user: string[], unregistered: string[] }; + interface IGlobalStateResourceMergeResult extends IAcceptResult { readonly local: { added: IStringDictionary, removed: string[], updated: IStringDictionary }; readonly remote: IStringDictionary | null; } export interface IGlobalStateResourcePreview extends IResourcePreview { - readonly skippedStorageKeys: string[]; readonly localUserData: IGlobalState; readonly previewResult: IGlobalStateResourceMergeResult; + readonly storageKeys: StorageKeys; } -interface ILastSyncUserData extends IRemoteUserData { - skippedStorageKeys: string[] | undefined; -} - +/** + * Synchronises global state that includes + * - Global storage with user scope + * - Locale from argv properties + * + * Global storage is synced without checking version just like other resources (settings, keybindings). + * If there is a change in format of the value of a storage key which requires migration then + * Owner of that key should remove that key from user scope and replace that with new user scoped key. + */ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser { private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/globalState.json` }); @@ -63,23 +68,22 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(SyncResource.GlobalState, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); - this._register(this.fileService.watch(this.extUri.dirname(this.environmentService.argvResource))); + this._register(fileService.watch(this.extUri.dirname(this.environmentService.argvResource))); this._register( Event.any( /* Locale change */ - Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource)), - /* Storage change */ - Event.filter(this.storageService.onDidChangeStorage, e => storageKeysSyncRegistryService.storageKeys.some(({ key }) => e.key === key)), - /* Storage key registered */ - this.storageKeysSyncRegistryService.onDidChangeStorageKeys + Event.filter(fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource)), + /* Global storage with user target has changed */ + Event.filter(storageService.onDidChangeValue, e => e.scope === StorageScope.GLOBAL && e.target !== undefined ? e.target === StorageTarget.USER : storageService.keys(StorageScope.GLOBAL, StorageTarget.USER).includes(e.key)), + /* Storage key target has changed */ + this.storageService.onDidChangeTarget )((() => this.triggerLocalChange())) ); } - protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, token: CancellationToken): Promise { + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null; const lastSyncGlobalState: IGlobalState | null = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null; @@ -91,7 +95,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`); } - const { local, remote, skipped } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const storageKeys = this.getStorageKeys(lastSyncGlobalState); + const { local, remote } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, storageKeys, this.logService); const previewResult: IGlobalStateResourceMergeResult = { content: null, local, @@ -101,7 +106,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs }; return [{ - skippedStorageKeys: skipped, localResource: this.localResource, localContent: this.format(localGloablState), localUserData: localGloablState, @@ -112,6 +116,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs localChange: previewResult.localChange, remoteChange: previewResult.remoteChange, acceptedResource: this.acceptedResource, + storageKeys }]; } @@ -152,7 +157,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs private async acceptRemote(resourcePreview: IGlobalStateResourcePreview): Promise { if (resourcePreview.remoteContent !== null) { const remoteGlobalState: IGlobalState = JSON.parse(resourcePreview.remoteContent); - const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), resourcePreview.skippedStorageKeys, this.logService); + const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, resourcePreview.storageKeys, this.logService); return { content: resourcePreview.remoteContent, local, @@ -171,8 +176,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } } - protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, resourcePreviews: [IGlobalStateResourcePreview, IGlobalStateResourceMergeResult][], force: boolean): Promise { - let { localUserData, skippedStorageKeys } = resourcePreviews[0][0]; + protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [IGlobalStateResourcePreview, IGlobalStateResourceMergeResult][], force: boolean): Promise { + let { localUserData } = resourcePreviews[0][0]; let { local, remote, localChange, remoteChange } = resourcePreviews[0][1]; if (localChange === Change.None && remoteChange === Change.None) { @@ -195,10 +200,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`); } - if (lastSyncUserData?.ref !== remoteUserData.ref || !equals(lastSyncUserData.skippedStorageKeys, skippedStorageKeys)) { + if (lastSyncUserData?.ref !== remoteUserData.ref) { // update last sync this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized ui state...`); - await this.updateLastSyncUserData(remoteUserData, { skippedStorageKeys }); + await this.updateLastSyncUserData(remoteUserData); this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized ui state`); } } @@ -267,10 +272,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs storage[`${argvStoragePrefx}${argvProperty}`] = { version: 1, value: argvValue[argvProperty] }; } } - for (const { key, version } of this.storageKeysSyncRegistryService.storageKeys) { + for (const key of this.storageService.keys(StorageScope.GLOBAL, StorageTarget.USER)) { const value = this.storageService.get(key, StorageScope.GLOBAL); if (value) { - storage[key] = { version, value }; + storage[key] = { version: 1, value }; } } return { storage }; @@ -317,7 +322,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs if (updatedStorageKeys.length) { this.logService.trace(`${this.syncResourceLogLabel}: Updating global state...`); for (const key of Object.keys(updatedStorage)) { - this.storageService.store(key, updatedStorage[key], StorageScope.GLOBAL); + this.storageService.store(key, updatedStorage[key], StorageScope.GLOBAL, StorageTarget.USER); } this.logService.info(`${this.syncResourceLogLabel}: Updated global state`, Object.keys(updatedStorage)); } @@ -336,8 +341,12 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } } - private getSyncStorageKeys(): IStorageKey[] { - return [...this.storageKeysSyncRegistryService.storageKeys, ...argvProperties.map(argvProprety => ({ key: `${argvStoragePrefx}${argvProprety}`, version: 1 }))]; + private getStorageKeys(lastSyncGlobalState: IGlobalState | null): StorageKeys { + const user = this.storageService.keys(StorageScope.GLOBAL, StorageTarget.USER); + const machine = this.storageService.keys(StorageScope.GLOBAL, StorageTarget.MACHINE); + const registered = [...user, ...machine]; + const unregistered = lastSyncGlobalState?.storage ? Object.keys(lastSyncGlobalState.storage).filter(key => !key.startsWith(argvStoragePrefx) && !registered.includes(key) && this.storageService.get(key, StorageScope.GLOBAL) !== undefined) : []; + return { user, machine, unregistered }; } } @@ -385,7 +394,7 @@ export class GlobalStateInitializer extends AbstractInitializer { if (Object.keys(storage).length) { for (const key of Object.keys(storage)) { - this.storageService.store(key, storage[key], StorageScope.GLOBAL); + this.storageService.store(key, storage[key], StorageScope.GLOBAL, StorageTarget.USER); } } } diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 80f54b97..7f936367 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -23,6 +23,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { VSBuffer } from 'vs/base/common/buffer'; +import { Event } from 'vs/base/common/event'; interface ISyncContent { mac?: string; @@ -35,6 +36,10 @@ interface IKeybindingsResourcePreview extends IFileResourcePreview { previewResult: IMergeResult; } +interface ILastSyncUserData extends IRemoteUserData { + platformSpecific?: boolean; +} + export function getKeybindingsContentFromSyncContent(syncContent: string, platformSpecific: boolean): string | null { const parsed = JSON.parse(syncContent); if (!platformSpecific) { @@ -72,11 +77,12 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem @ITelemetryService telemetryService: ITelemetryService, ) { super(environmentService.keybindingsResource, SyncResource.Keybindings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); + this._register(Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('settingsSync.keybindingsPerPlatform'))(() => this.triggerLocalChange())); } - protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { + protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, token: CancellationToken): Promise { const remoteContent = remoteUserData.syncData ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null; - const lastSyncContent: string | null = lastSyncUserData && lastSyncUserData.syncData ? this.getKeybindingsContentFromSyncContent(lastSyncUserData.syncData.content) : null; + const lastSyncContent: string | null = lastSyncUserData ? this.getKeybindingsContentFromLastSyncUserData(lastSyncUserData) : null; // Get file content last to get the latest const fileContent = await this.getLocalFileContent(); @@ -204,7 +210,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem if (localChange !== Change.None) { this.logService.trace(`${this.syncResourceLogLabel}: Updating local keybindings...`); if (fileContent) { - await this.backupLocal(this.toSyncContent(fileContent.value.toString(), null)); + await this.backupLocal(this.toSyncContent(fileContent.value.toString())); } await this.updateLocalFileContent(content || '[]', fileContent, force); this.logService.info(`${this.syncResourceLogLabel}: Updated local keybindings`); @@ -212,7 +218,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem if (remoteChange !== Change.None) { this.logService.trace(`${this.syncResourceLogLabel}: Updating remote keybindings...`); - const remoteContents = this.toSyncContent(content || '[]', remoteUserData.syncData ? remoteUserData.syncData.content : null); + const remoteContents = this.toSyncContent(content || '[]', remoteUserData.syncData?.content); remoteUserData = await this.updateRemoteUserData(remoteContents, force ? null : remoteUserData.ref); this.logService.info(`${this.syncResourceLogLabel}: Updated remote keybindings`); } @@ -224,15 +230,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem if (lastSyncUserData?.ref !== remoteUserData.ref) { this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized keybindings...`); - const lastSyncContent = content !== null ? this.toSyncContent(content, null) : remoteUserData.syncData?.content; - await this.updateLastSyncUserData({ - ref: remoteUserData.ref, - syncData: lastSyncContent ? { - version: remoteUserData.syncData ? remoteUserData.syncData.version : this.version, - machineId: remoteUserData.syncData!.machineId, - content: lastSyncContent - } : null - }); + await this.updateLastSyncUserData(remoteUserData, { platformSpecific: this.syncKeybindingsPerPlatform() }); this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized keybindings`); } @@ -281,6 +279,19 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem return null; } + private getKeybindingsContentFromLastSyncUserData(lastSyncUserData: ILastSyncUserData): string | null { + if (!lastSyncUserData.syncData) { + return null; + } + + // Return null if there is a change in platform specific property from last time sync. + if (lastSyncUserData.platformSpecific !== undefined && lastSyncUserData.platformSpecific !== this.syncKeybindingsPerPlatform()) { + return null; + } + + return this.getKeybindingsContentFromSyncContent(lastSyncUserData.syncData.content); + } + private getKeybindingsContentFromSyncContent(syncContent: string): string | null { try { return getKeybindingsContentFromSyncContent(syncContent, this.syncKeybindingsPerPlatform()); @@ -290,7 +301,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem } } - private toSyncContent(keybindingsContent: string, syncContent: string | null): string { + private toSyncContent(keybindingsContent: string, syncContent?: string): string { let parsed: ISyncContent = {}; try { parsed = JSON.parse(syncContent || '{}'); diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 6b583f9c..d4e31ee0 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -13,7 +13,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { localize } from 'vs/nls'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; import { updateIgnoredSettings, merge, getIgnoredSettings, isEmpty } from 'vs/platform/userDataSync/common/settingsMerge'; import { edit } from 'vs/platform/userDataSync/common/content'; @@ -198,6 +198,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement await this.backupLocal(JSON.stringify(this.toSettingsSyncContent(fileContent.value.toString()))); } await this.updateLocalFileContent(content, fileContent, force); + await this.configurationService.reloadConfiguration(ConfigurationTarget.USER_LOCAL); this.logService.info(`${this.syncResourceLogLabel}: Updated local settings`); } diff --git a/src/vs/platform/userDataSync/common/storageKeys.ts b/src/vs/platform/userDataSync/common/storageKeys.ts deleted file mode 100644 index 5ffe0786..00000000 --- a/src/vs/platform/userDataSync/common/storageKeys.ts +++ /dev/null @@ -1,134 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export interface IStorageKey { - - readonly key: string; - readonly version: number; - -} - -export interface IExtensionIdWithVersion { - id: string; - version: string; -} - -export namespace ExtensionIdWithVersion { - - const EXTENSION_ID_VERSION_REGEX = /^([^.]+\..+)@(\d+\.\d+\.\d+(-.*)?)$/; - - export function toKey(extension: IExtensionIdWithVersion): string { - return `${extension.id}@${extension.version}`; - } - - export function fromKey(key: string): IExtensionIdWithVersion | undefined { - const matches = EXTENSION_ID_VERSION_REGEX.exec(key); - if (matches && matches[1]) { - return { id: matches[1], version: matches[2] }; - } - return undefined; - } -} - -export const IStorageKeysSyncRegistryService = createDecorator('IStorageKeysSyncRegistryService'); - -export interface IStorageKeysSyncRegistryService { - - _serviceBrand: any; - - /** - * All registered storage keys - */ - readonly storageKeys: ReadonlyArray; - - /** - * Event that is triggered when storage keys are changed - */ - readonly onDidChangeStorageKeys: Event>; - - /** - * Register a storage key that has to be synchronized during sync. - */ - registerStorageKey(key: IStorageKey): void; - - /** - * All registered extensions storage keys - */ - readonly extensionsStorageKeys: ReadonlyArray<[IExtensionIdentifierWithVersion, ReadonlyArray]>; - - /** - * Event that is triggered when extension storage keys are changed - */ - onDidChangeExtensionStorageKeys: Event<[IExtensionIdWithVersion, ReadonlyArray]>; - - /** - * Register storage keys that has to be synchronized for the given extension. - */ - registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void; - - /** - * Returns storage keys of the given extension that has to be synchronized. - */ - getExtensioStorageKeys(extension: IExtensionIdWithVersion): ReadonlyArray | undefined; -} - -export abstract class AbstractStorageKeysSyncRegistryService extends Disposable implements IStorageKeysSyncRegistryService { - - declare readonly _serviceBrand: undefined; - - protected readonly _storageKeys = new Map(); - get storageKeys(): ReadonlyArray { return [...this._storageKeys.values()]; } - - protected readonly _onDidChangeStorageKeys: Emitter> = this._register(new Emitter>()); - readonly onDidChangeStorageKeys = this._onDidChangeStorageKeys.event; - - protected readonly _extensionsStorageKeys = new Map(); - get extensionsStorageKeys() { - const result: [IExtensionIdWithVersion, ReadonlyArray][] = []; - this._extensionsStorageKeys.forEach((keys, extension) => result.push([ExtensionIdWithVersion.fromKey(extension)!, keys])); - return result; - } - protected readonly _onDidChangeExtensionStorageKeys = this._register(new Emitter<[IExtensionIdWithVersion, ReadonlyArray]>()); - readonly onDidChangeExtensionStorageKeys = this._onDidChangeExtensionStorageKeys.event; - - constructor() { - super(); - this._register(toDisposable(() => this._storageKeys.clear())); - } - - getExtensioStorageKeys(extension: IExtensionIdWithVersion): ReadonlyArray | undefined { - return this._extensionsStorageKeys.get(ExtensionIdWithVersion.toKey(extension)); - } - - protected updateExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void { - this._extensionsStorageKeys.set(ExtensionIdWithVersion.toKey(extension), keys); - this._onDidChangeExtensionStorageKeys.fire([extension, keys]); - } - - abstract registerStorageKey(key: IStorageKey): void; - abstract registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void; -} - -export class StorageKeysSyncRegistryService extends AbstractStorageKeysSyncRegistryService { - - _serviceBrand: any; - - registerStorageKey(storageKey: IStorageKey): void { - if (!this._storageKeys.has(storageKey.key)) { - this._storageKeys.set(storageKey.key, storageKey); - this._onDidChangeStorageKeys.fire(this.storageKeys); - } - } - - registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void { - this.updateExtensionStorageKeys(extension, keys); - } - -} diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index cb2342a2..e1960cf0 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -11,7 +11,7 @@ import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/use import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { localize } from 'vs/nls'; @@ -55,7 +55,7 @@ export class UserDataAutoSyncEnablementService extends Disposable implements _IU @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, ) { super(); - this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e))); } isEnabled(defaultEnablement?: boolean): boolean { @@ -73,15 +73,15 @@ export class UserDataAutoSyncEnablementService extends Disposable implements _IU } setEnablement(enabled: boolean): void { - this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL); + this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); } - private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { - if (workspaceStorageChangeEvent.scope !== StorageScope.GLOBAL) { + private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { + if (storageChangeEvent.scope !== StorageScope.GLOBAL) { return; } - if (enablementKey === workspaceStorageChangeEvent.key) { + if (enablementKey === storageChangeEvent.key) { this._onDidChangeEnablement.fire(this.isEnabled()); return; } @@ -110,7 +110,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private set syncUrl(syncUrl: URI | undefined) { if (syncUrl) { - this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL); + this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove(storeUrlKey, StorageScope.GLOBAL); } @@ -325,7 +325,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private async disableMachineEventually(): Promise { - this.storageService.store(disableMachineEventuallyKey, true, StorageScope.GLOBAL); + this.storageService.store(disableMachineEventuallyKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE); await timeout(1000 * 60 * 10); // Return if got stopped meanwhile. @@ -526,7 +526,7 @@ class AutoSync extends Disposable { // Update local session id if (manifest && manifest.session !== sessionId) { - this.storageService.store(sessionIdKey, manifest.session, StorageScope.GLOBAL); + this.storageService.store(sessionIdKey, manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); } // Return if cancellation is requested diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 35a10cfd..6d9d58d1 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -220,7 +220,9 @@ export enum UserDataSyncErrorCode { TooManyRequestsAndRetryAfter = 'TooManyRequestsAndRetryAfter', /* 429 + Retry-After */ // Local Errors - ConnectionRefused = 'ConnectionRefused', + RequestFailed = 'RequestFailed', + RequestCanceled = 'RequestCanceled', + RequestTimeout = 'RequestTimeout', NoRef = 'NoRef', TurnedOff = 'TurnedOff', SessionExpired = 'SessionExpired', @@ -252,7 +254,7 @@ export class UserDataSyncError extends Error { } export class UserDataSyncStoreError extends UserDataSyncError { - constructor(message: string, code: UserDataSyncErrorCode, readonly operationId: string | undefined) { + constructor(message: string, readonly url: string, code: UserDataSyncErrorCode, readonly operationId: string | undefined) { super(message, code, undefined, operationId); } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index c9088b8d..f67dfb1c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -9,11 +9,9 @@ import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncServic import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; -import { IStorageKeysSyncRegistryService, IStorageKey, IExtensionIdWithVersion, AbstractStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ILogService } from 'vs/platform/log/common/log'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { IExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; export class UserDataSyncChannel implements IServerChannel { @@ -175,68 +173,6 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { } -type StorageKeysSyncRegistryServiceInitData = { storageKeys: ReadonlyArray, extensionsStorageKeys: ReadonlyArray<[IExtensionIdWithVersion, ReadonlyArray]> }; - -export class StorageKeysSyncRegistryChannel implements IServerChannel { - - constructor(private readonly service: IStorageKeysSyncRegistryService) { } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidChangeStorageKeys': return this.service.onDidChangeStorageKeys; - case 'onDidChangeExtensionStorageKeys': return this.service.onDidChangeExtensionStorageKeys; - } - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case '_getInitialData': return Promise.resolve({ storageKeys: this.service.storageKeys, extensionsStorageKeys: this.service.extensionsStorageKeys }); - case 'registerStorageKey': return Promise.resolve(this.service.registerStorageKey(args[0])); - case 'registerExtensionStorageKeys': return Promise.resolve(this.service.registerExtensionStorageKeys(args[0], args[1])); - } - throw new Error('Invalid call'); - } -} - -export class StorageKeysSyncRegistryChannelClient extends AbstractStorageKeysSyncRegistryService { - - declare readonly _serviceBrand: undefined; - - constructor(private readonly channel: IChannel) { - super(); - this.channel.call('_getInitialData').then(({ storageKeys, extensionsStorageKeys }) => { - this.updateStorageKeys(storageKeys); - this.updateExtensionsStorageKeys(extensionsStorageKeys); - this._register(this.channel.listen>('onDidChangeStorageKeys')(storageKeys => this.updateStorageKeys(storageKeys))); - this._register(this.channel.listen<[IExtensionIdentifierWithVersion, string[]]>('onDidChangeExtensionStorageKeys')(e => this.updateExtensionStorageKeys(e[0], e[1]))); - }); - } - - private async updateStorageKeys(storageKeys: ReadonlyArray): Promise { - this._storageKeys.clear(); - for (const storageKey of storageKeys) { - this._storageKeys.set(storageKey.key, storageKey); - } - this._onDidChangeStorageKeys.fire(this.storageKeys); - } - - private async updateExtensionsStorageKeys(extensionStorageKeys: ReadonlyArray<[IExtensionIdentifierWithVersion, ReadonlyArray]>): Promise { - for (const [extension, keys] of extensionStorageKeys) { - this.updateExtensionStorageKeys(extension, [...keys]); - } - } - - registerStorageKey(storageKey: IStorageKey): void { - this.channel.call('registerStorageKey', [storageKey]); - } - - registerExtensionStorageKeys(extension: IExtensionIdentifierWithVersion, keys: string[]): void { - this.channel.call('registerExtensionStorageKeys', [extension, keys]); - } - -} - export class UserDataSyncMachinesServiceChannel implements IServerChannel { constructor(private readonly service: IUserDataSyncMachinesService) { } diff --git a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts index 1b9d608b..f50ad491 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IUserDataSyncStoreService, IUserData, IUserDataSyncLogService, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync'; import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -103,7 +103,7 @@ export class UserDataSyncMachinesService extends Disposable implements IUserData machine.name = name; await this.writeMachinesData(machineData); if (machineData.machines.some(({ id }) => id === currentMachineId)) { - this.storageService.store(currentMachineNameKey, name, StorageScope.GLOBAL); + this.storageService.store(currentMachineNameKey, name, StorageScope.GLOBAL, StorageTarget.MACHINE); } } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts index a84f2174..4fd2b3c3 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts @@ -6,7 +6,7 @@ import { IUserDataSyncResourceEnablementService, ALL_SYNC_RESOURCES, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; type SyncEnablementClassification = { @@ -28,7 +28,7 @@ export class UserDataSyncResourceEnablementService extends Disposable implements @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); - this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e))); } isResourceEnabled(resource: SyncResource): boolean { @@ -39,13 +39,13 @@ export class UserDataSyncResourceEnablementService extends Disposable implements if (this.isResourceEnabled(resource) !== enabled) { const resourceEnablementKey = getEnablementKey(resource); this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(resourceEnablementKey, { enabled }); - this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL); + this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); } } - private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { - if (workspaceStorageChangeEvent.scope === StorageScope.GLOBAL) { - const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === workspaceStorageChangeEvent.key)[0]; + private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { + if (storageChangeEvent.scope === StorageScope.GLOBAL) { + const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === storageChangeEvent.key)[0]; if (resourceKey) { this._onDidChangeResourceEnablement.fire([resourceKey, this.isResourceEnabled(resourceKey)]); return; diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 50ea1f33..2cf4ac37 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -5,7 +5,7 @@ import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncErrorCode, - UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change, IUserDataSyncStoreManagementService + UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change, IUserDataSyncStoreManagementService, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -16,7 +16,7 @@ import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalS import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { equals } from 'vs/base/common/arrays'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; import { isEqual } from 'vs/base/common/resources'; @@ -30,6 +30,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; type SyncErrorClassification = { code: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; service: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + url?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; resource?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; executionId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; @@ -118,9 +119,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } manifest = await this.userDataSyncStoreService.manifest(syncHeaders); } catch (error) { - error = UserDataSyncError.toUserDataSyncError(error); - this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); - throw error; + const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); + this.reportUserDataSyncError(userDataSyncError, executionId); + throw userDataSyncError; } let executed = false; @@ -156,9 +157,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ try { manifest = await this.userDataSyncStoreService.manifest(syncHeaders); } catch (error) { - error = UserDataSyncError.toUserDataSyncError(error); - this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); - throw error; + const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); + this.reportUserDataSyncError(userDataSyncError, executionId); + throw userDataSyncError; } return new ManualSyncTask(executionId, manifest, syncHeaders, this.synchronisers, this.logService); @@ -202,9 +203,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); this.updateLastSyncTime(); } catch (error) { - error = UserDataSyncError.toUserDataSyncError(error); - this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); - throw error; + const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); + this.reportUserDataSyncError(userDataSyncError, executionId); + throw userDataSyncError; } finally { this.updateStatus(); this._onSyncErrors.fire(this._syncErrors); @@ -365,7 +366,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private updateLastSyncTime(): void { if (this.status === SyncStatus.Idle) { this._lastSyncTime = new Date().getTime(); - this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.GLOBAL); + this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.GLOBAL, StorageTarget.MACHINE); this._onDidChangeLastSyncTime.fire(this._lastSyncTime); } } @@ -390,6 +391,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.logService.error(`${source}: ${toErrorMessage(e)}`); } + private reportUserDataSyncError(userDataSyncError: UserDataSyncError, executionId: string) { + this.telemetryService.publicLog2<{ code: string, service: string, url?: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', + { code: userDataSyncError.code, url: userDataSyncError instanceof UserDataSyncStoreError ? userDataSyncError.url : undefined, resource: userDataSyncError.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() }); + } + private computeConflicts(): [SyncResource, IResourcePreview[]][] { return this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts) .map(s => ([s.resource, s.conflicts.map(toStrictResourcePreview)])); diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index fe6f311f..40958689 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -14,13 +14,14 @@ import { IProductService, ConfigurationSyncStore } from 'vs/platform/product/com import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { generateUuid } from 'vs/base/common/uuid'; import { isWeb } from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import { createCancelablePromise, timeout, CancelablePromise } from 'vs/base/common/async'; import { isString, isObject, isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; const SYNC_SERVICE_URL_TYPE = 'sync.store.url.type'; const SYNC_PREVIOUS_STORE = 'sync.previous.store'; @@ -67,10 +68,10 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa const syncStore = value as ConfigurationSyncStore; const canSwitch = !!syncStore.canSwitch && !configuredStore?.url; const type: UserDataSyncStoreType | undefined = canSwitch ? this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL) as UserDataSyncStoreType : undefined; - const url = configuredStore?.url - || type === 'insiders' ? syncStore.insidersUrl - : type === 'stable' ? syncStore.stableUrl - : syncStore.url; + const url = configuredStore?.url || + (type === 'insiders' ? syncStore.insidersUrl + : type === 'stable' ? syncStore.stableUrl + : syncStore.url); return { url: URI.parse(url), type, @@ -111,7 +112,7 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor const syncStore = this.productService[CONFIGURATION_SYNC_STORE_KEY]; if (syncStore) { - this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.GLOBAL); + this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); } @@ -122,7 +123,7 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor if (type === this.userDataSyncStore.defaultType) { this.storageService.remove(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL); } else { - this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL); + this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL, StorageTarget.MACHINE); } this.updateUserDataSyncStore(); } @@ -207,7 +208,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } if (this._donotMakeRequestsUntil) { - this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.GLOBAL); + this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.GLOBAL, StorageTarget.MACHINE); this.resetDonotMakeRequestsUntilPromise = createCancelablePromise(token => timeout(this._donotMakeRequestsUntil!.getTime() - Date.now(), token).then(() => this.setDonotMakeRequestsUntil(undefined))); } else { this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); @@ -225,7 +226,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const uri = joinPath(this.userDataSyncStoreUrl, 'resource', resource); const headers: IHeaders = {}; - const context = await this.request({ type: 'GET', url: uri.toString(), headers }, [], CancellationToken.None); + const context = await this.request(uri.toString(), { type: 'GET', headers }, [], CancellationToken.None); const result = await asJson<{ url: string, created: number }[]>(context) || []; return result.map(({ url, created }) => ({ ref: relativePath(uri, uri.with({ path: url }))!, created: created * 1000 /* Server returns in seconds */ })); @@ -240,7 +241,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const headers: IHeaders = {}; headers['Cache-Control'] = 'no-cache'; - const context = await this.request({ type: 'GET', url, headers }, [], CancellationToken.None); + const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None); const content = await asText(context); return content; } @@ -253,7 +254,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource).toString(); const headers: IHeaders = {}; - await this.request({ type: 'DELETE', url, headers }, [], CancellationToken.None); + await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None); } async read(resource: ServerResource, oldValue: IUserData | null, headers: IHeaders = {}): Promise { @@ -269,7 +270,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync headers['If-None-Match'] = oldValue.ref; } - const context = await this.request({ type: 'GET', url, headers }, [304], CancellationToken.None); + const context = await this.request(url, { type: 'GET', headers }, [304], CancellationToken.None); if (context.res.statusCode === 304) { // There is no new value. Hence return the old value. @@ -278,7 +279,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const ref = context.res.headers['etag']; if (!ref) { - throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); + throw new UserDataSyncStoreError('Server did not return the ref', url, UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); } const content = await asText(context); return { ref, content }; @@ -296,11 +297,11 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync headers['If-Match'] = ref; } - const context = await this.request({ type: 'POST', url, data, headers }, [], CancellationToken.None); + const context = await this.request(url, { type: 'POST', data, headers }, [], CancellationToken.None); const newRef = context.res.headers['etag']; if (!newRef) { - throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); + throw new UserDataSyncStoreError('Server did not return the ref', url, UserDataSyncErrorCode.NoRef, context.res.headers[HEADER_OPERATION_ID]); } return newRef; } @@ -314,7 +315,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync headers = { ...headers }; headers['Content-Type'] = 'application/json'; - const context = await this.request({ type: 'GET', url, headers }, [], CancellationToken.None); + const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None); const manifest = await asJson(context); const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); @@ -331,7 +332,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync if (manifest) { // update session - this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL); + this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); } return manifest; @@ -345,7 +346,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync const url = joinPath(this.userDataSyncStoreUrl, 'resource').toString(); const headers: IHeaders = { 'Content-Type': 'text/plain' }; - await this.request({ type: 'DELETE', url, headers }, [], CancellationToken.None); + await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None); // clear cached session. this.clearSession(); @@ -356,13 +357,13 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync this.storageService.remove(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); } - private async request(options: IRequestOptions, successCodes: number[], token: CancellationToken): Promise { + private async request(url: string, options: IRequestOptions, successCodes: number[], token: CancellationToken): Promise { if (!this.authToken) { - throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, undefined); + throw new UserDataSyncStoreError('No Auth Token Available', url, UserDataSyncErrorCode.Unauthorized, undefined); } if (this._donotMakeRequestsUntil && Date.now() < this._donotMakeRequestsUntil.getTime()) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, undefined); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, undefined); } this.setDonotMakeRequestsUntil(undefined); @@ -377,21 +378,23 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync // Add session headers this.addSessionHeaders(options.headers); - this.logService.trace('Sending request to server', { url: options.url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } }); + this.logService.trace('Sending request to server', { url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } }); let context; try { - context = await this.session.request(options, token); + context = await this.session.request(url, options, token); } catch (e) { if (!(e instanceof UserDataSyncStoreError)) { - e = new UserDataSyncStoreError(`Connection refused for the request '${options.url?.toString()}'.`, UserDataSyncErrorCode.ConnectionRefused, undefined); + const code = isPromiseCanceledError(e) ? UserDataSyncErrorCode.RequestCanceled + : getErrorMessage(e).startsWith('XHR timeout') ? UserDataSyncErrorCode.RequestTimeout : UserDataSyncErrorCode.RequestFailed; + e = new UserDataSyncStoreError(`Connection refused for the request '${url}'.`, url, code, undefined); } - this.logService.info('Request failed', options.url); + this.logService.info('Request failed', url); throw e; } const operationId = context.res.headers[HEADER_OPERATION_ID]; - const requestInfo = { url: options.url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId }; + const requestInfo = { url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId }; const isSuccess = isSuccessContext(context) || (context.res.statusCode && successCodes.indexOf(context.res.statusCode) !== -1); if (isSuccess) { this.logService.trace('Request succeeded', requestInfo); @@ -402,43 +405,43 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync if (context.res.statusCode === 401) { this.authToken = undefined; this._onTokenFailed.fire(); - throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' failed because of Unauthorized (401).`, UserDataSyncErrorCode.Unauthorized, operationId); + throw new UserDataSyncStoreError(`Request '${url}' failed because of Unauthorized (401).`, url, UserDataSyncErrorCode.Unauthorized, operationId); } this._onTokenSucceed.fire(); if (context.res.statusCode === 409) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.Conflict, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, url, UserDataSyncErrorCode.Conflict, operationId); } if (context.res.statusCode === 410) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because the requested resource is not longer available (410).`, UserDataSyncErrorCode.Gone, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested resource is not longer available (410).`, url, UserDataSyncErrorCode.Gone, operationId); } if (context.res.statusCode === 412) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.PreconditionFailed, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`, url, UserDataSyncErrorCode.PreconditionFailed, operationId); } if (context.res.statusCode === 413) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too large payload (413).`, UserDataSyncErrorCode.TooLarge, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too large payload (413).`, url, UserDataSyncErrorCode.TooLarge, operationId); } if (context.res.statusCode === 426) { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed with status Upgrade Required (426). Please upgrade the client and try again.`, UserDataSyncErrorCode.UpgradeRequired, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed with status Upgrade Required (426). Please upgrade the client and try again.`, url, UserDataSyncErrorCode.UpgradeRequired, operationId); } if (context.res.statusCode === 429) { const retryAfter = context.res.headers['retry-after']; if (retryAfter) { this.setDonotMakeRequestsUntil(new Date(Date.now() + (parseInt(retryAfter) * 1000))); - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, UserDataSyncErrorCode.TooManyRequestsAndRetryAfter, operationId); } else { - throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of too many requests (429).`, UserDataSyncErrorCode.TooManyRequests, operationId); + throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, UserDataSyncErrorCode.TooManyRequests, operationId); } } if (!isSuccess) { - throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, operationId); + throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, url, UserDataSyncErrorCode.Unknown, operationId); } return context; @@ -448,7 +451,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); if (machineSessionId === undefined) { machineSessionId = generateUuid(); - this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.GLOBAL); + this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); } headers['X-Machine-Session-Id'] = machineSessionId; @@ -490,18 +493,20 @@ export class RequestsSession { private readonly logService: IUserDataSyncLogService, ) { } - request(options: IRequestOptions, token: CancellationToken): Promise { + request(url: string, options: IRequestOptions, token: CancellationToken): Promise { if (this.isExpired()) { this.reset(); } + options.url = url; + if (this.requests.length >= this.limit) { this.logService.info('Too many requests', ...this.requests); - throw new UserDataSyncStoreError(`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests, undefined); + throw new UserDataSyncStoreError(`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`, url, UserDataSyncErrorCode.LocalTooManyRequests, undefined); } this.startTime = this.startTime || new Date(); - this.requests.push(options.url!); + this.requests.push(url); return this.requestService.request(options, token); } diff --git a/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts b/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts index e89763a4..27fb0a23 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateMerge.test.ts @@ -9,11 +9,11 @@ import { NullLogService } from 'vs/platform/log/common/log'; suite('GlobalStateMerge', () => { - test('merge when local and remote are same with one value', async () => { + test('merge when local and remote are same with one value and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -21,11 +21,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when local and remote are same with multiple entries', async () => { + test('merge when local and remote are same with multiple entries and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -33,11 +33,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when local and remote are same with multiple entries in different order', async () => { + test('merge when local and remote are same with multiple entries in different order and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -50,7 +50,7 @@ suite('GlobalStateMerge', () => { const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; const base = { 'b': { version: 1, value: 'a' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -58,11 +58,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when a new entry is added to remote', async () => { + test('merge when a new entry is added to remote and local has not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' } }); assert.deepEqual(actual.local.updated, {}); @@ -70,11 +70,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when multiple new entries are added to remote', async () => { + test('merge when multiple new entries are added to remote and local is not synced yet', async () => { const local = {}; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }); assert.deepEqual(actual.local.updated, {}); @@ -86,7 +86,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' } }); assert.deepEqual(actual.local.updated, {}); @@ -98,7 +98,7 @@ suite('GlobalStateMerge', () => { const local = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -110,7 +110,7 @@ suite('GlobalStateMerge', () => { const local = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; const remote = {}; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -122,7 +122,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); @@ -134,7 +134,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'd' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'c': { version: 1, value: 'c' } }); assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'd' } }); @@ -142,11 +142,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when new entries are added to local', async () => { + test('merge when new entries are added to local and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -158,7 +158,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' }, 'c': { version: 1, value: 'c' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -170,7 +170,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -182,7 +182,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -194,7 +194,7 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'd' }, 'b': { version: 1, value: 'b' } }; const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, remote, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -202,11 +202,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, local); }); - test('merge when local and remote with one entry but different value', async () => { + test('merge when local and remote with one entry but different value and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); @@ -219,12 +219,12 @@ suite('GlobalStateMerge', () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'd' } }; const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, { 'c': { version: 1, value: 'c' } }); assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, ['b']); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.local.removed, []); + assert.deepEqual(actual.remote, { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' }, 'b': { version: 1, value: 'd' } }); }); test('merge with single entry and local is empty', async () => { @@ -232,32 +232,32 @@ suite('GlobalStateMerge', () => { const local = {}; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); + assert.deepEqual(actual.local.updated, {}); assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote, local); }); - test('merge when local and remote has moved forwareded with conflicts', async () => { + test('merge when local and remote has moved forward with conflicts', async () => { const base = { 'a': { version: 1, value: 'a' } }; const local = { 'a': { version: 1, value: 'd' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } }); + assert.deepEqual(actual.local.updated, {}); assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote, local); }); - test('merge when a new entry is added to remote but not a registered key', async () => { + test('merge when a new entry is added to remote but scoped to machine locally and local is not synced yet', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, null, { machine: ['b'], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -265,23 +265,11 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when a new entry is added to remote but different version', async () => { - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when an entry is updated to remote but not a registered key', async () => { + test('merge when an entry is updated to remote but scoped to machine locally', async () => { const local = { 'a': { version: 1, value: 'a' } }; const remote = { 'a': { version: 1, value: 'b' } }; - const actual = merge(local, remote, local, [], [], new NullLogService()); + const actual = merge(local, remote, local, { machine: ['a'], unregistered: [] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); @@ -289,92 +277,43 @@ suite('GlobalStateMerge', () => { assert.deepEqual(actual.remote, null); }); - test('merge when a new entry is updated to remote but different version', async () => { + test('merge when a local value is removed and scoped to machine locally', async () => { + const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; + const local = { 'a': { version: 1, value: 'a' } }; + const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; + + const actual = merge(local, remote, base, { machine: ['b'], unregistered: [] }, new NullLogService()); + + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); + assert.deepEqual(actual.remote, local); + }); + + test('merge when local moved forwared by changing a key to machine scope', async () => { + const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; + const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; + const local = { 'a': { version: 1, value: 'a' } }; + + const actual = merge(local, remote, base, { machine: ['b'], unregistered: [] }, new NullLogService()); + + assert.deepEqual(actual.local.added, {}); + assert.deepEqual(actual.local.updated, {}); + assert.deepEqual(actual.local.removed, []); + assert.deepEqual(actual.remote, local); + }); + + test('merge should not remove remote keys if not registered', async () => { const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; + const base = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; + const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } }; - const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); + const actual = merge(local, remote, base, { machine: [], unregistered: ['c'] }, new NullLogService()); assert.deepEqual(actual.local.added, {}); assert.deepEqual(actual.local.updated, {}); assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is update with lower version', async () => { - const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'c' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is update with higher version', async () => { - const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 2, value: 'c' } }; - const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 2 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, local); - }); - - test('merge when a local value is removed but not registered', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is removed with lower version', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); - }); - - test('merge when a local value is removed with higher version', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 2 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, local); - }); - - test('merge when a local value is not yet registered', async () => { - const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } }; - const local = { 'a': { version: 1, value: 'a' } }; - const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } }; - - const actual = merge(local, remote, base, [{ key: 'a', version: 1 }], [], new NullLogService()); - - assert.deepEqual(actual.local.added, {}); - assert.deepEqual(actual.local.updated, {}); - assert.deepEqual(actual.local.removed, []); - assert.deepEqual(actual.remote, null); + assert.deepEqual(actual.remote, { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' }, 'c': { version: 1, value: 'c' } }); }); }); diff --git a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts index 2d5594c0..324469b7 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts @@ -10,10 +10,9 @@ import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; suite('GlobalStateSync', () => { @@ -28,17 +27,11 @@ suite('GlobalStateSync', () => { setup(async () => { testClient = disposableStore.add(new UserDataSyncClient(server)); await testClient.setUp(true); - let storageKeysSyncRegistryService = testClient.instantiationService.get(IStorageKeysSyncRegistryService); - storageKeysSyncRegistryService.registerStorageKey({ key: 'a', version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: 'b', version: 1 }); testObject = (testClient.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.GlobalState) as GlobalStateSynchroniser; disposableStore.add(toDisposable(() => testClient.instantiationService.get(IUserDataSyncStoreService).clear())); client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - storageKeysSyncRegistryService = client2.instantiationService.get(IStorageKeysSyncRegistryService); - storageKeysSyncRegistryService.registerStorageKey({ key: 'a', version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: 'b', version: 1 }); }); teardown(() => disposableStore.clear()); @@ -72,7 +65,7 @@ suite('GlobalStateSync', () => { test('when global state is created after first sync', async () => { await testObject.sync(await testClient.manifest()); - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); let lastSyncUserData = await testObject.getLastSyncUserData(); const manifest = await testClient.manifest(); @@ -91,7 +84,8 @@ suite('GlobalStateSync', () => { }); test('first time sync - outgoing to server (no state)', async () => { - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); + updateMachineStorage('b', 'value1', testClient); await updateLocale(testClient); await testObject.sync(await testClient.manifest()); @@ -105,7 +99,7 @@ suite('GlobalStateSync', () => { }); test('first time sync - incoming from server (no state)', async () => { - updateStorage('a', 'value1', client2); + updateUserStorage('a', 'value1', client2); await updateLocale(client2); await client2.sync(); @@ -118,10 +112,10 @@ suite('GlobalStateSync', () => { }); test('first time sync when storage exists', async () => { - updateStorage('a', 'value1', client2); + updateUserStorage('a', 'value1', client2); await client2.sync(); - updateStorage('b', 'value2', testClient); + updateUserStorage('b', 'value2', testClient); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -136,10 +130,10 @@ suite('GlobalStateSync', () => { }); test('first time sync when storage exists - has conflicts', async () => { - updateStorage('a', 'value1', client2); + updateUserStorage('a', 'value1', client2); await client2.sync(); - updateStorage('a', 'value2', client2); + updateUserStorage('a', 'value2', client2); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); @@ -154,10 +148,10 @@ suite('GlobalStateSync', () => { }); test('sync adding a storage value', async () => { - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); await testObject.sync(await testClient.manifest()); - updateStorage('b', 'value2', testClient); + updateUserStorage('b', 'value2', testClient); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -172,10 +166,10 @@ suite('GlobalStateSync', () => { }); test('sync updating a storage value', async () => { - updateStorage('a', 'value1', testClient); + updateUserStorage('a', 'value1', testClient); await testObject.sync(await testClient.manifest()); - updateStorage('a', 'value2', testClient); + updateUserStorage('a', 'value2', testClient); await testObject.sync(await testClient.manifest()); assert.equal(testObject.status, SyncStatus.Idle); assert.deepEqual(testObject.conflicts, []); @@ -189,8 +183,8 @@ suite('GlobalStateSync', () => { }); test('sync removing a storage value', async () => { - updateStorage('a', 'value1', testClient); - updateStorage('b', 'value2', testClient); + updateUserStorage('a', 'value1', testClient); + updateUserStorage('b', 'value2', testClient); await testObject.sync(await testClient.manifest()); removeStorage('b', testClient); @@ -218,9 +212,14 @@ suite('GlobalStateSync', () => { await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'en' }))); } - function updateStorage(key: string, value: string, client: UserDataSyncClient): void { + function updateUserStorage(key: string, value: string, client: UserDataSyncClient): void { const storageService = client.instantiationService.get(IStorageService); - storageService.store(key, value, StorageScope.GLOBAL); + storageService.store(key, value, StorageScope.GLOBAL, StorageTarget.USER); + } + + function updateMachineStorage(key: string, value: string, client: UserDataSyncClient): void { + const storageService = client.instantiationService.get(IStorageService); + storageService.store(key, value, StorageScope.GLOBAL, StorageTarget.MACHINE); } function removeStorage(key: string, client: UserDataSyncClient): void { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 6e821b1b..32204697 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -36,10 +36,10 @@ import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/plat import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; +import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; export class UserDataSyncClient extends Disposable { @@ -104,9 +104,9 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IUserDataSyncBackupStoreService, this.instantiationService.createInstance(UserDataSyncBackupStoreService)); this.instantiationService.stub(IUserDataSyncUtilService, new TestUserDataSyncUtilService()); this.instantiationService.stub(IUserDataSyncResourceEnablementService, this.instantiationService.createInstance(UserDataSyncResourceEnablementService)); - this.instantiationService.stub(IStorageKeysSyncRegistryService, this.instantiationService.createInstance(StorageKeysSyncRegistryService)); this.instantiationService.stub(IGlobalExtensionEnablementService, this.instantiationService.createInstance(GlobalExtensionEnablementService)); + this.instantiationService.stub(IExtensionsStorageSyncService, this.instantiationService.createInstance(ExtensionsStorageSyncService)); this.instantiationService.stub(IIgnoredExtensionsManagementService, this.instantiationService.createInstance(IgnoredExtensionsManagementService)); this.instantiationService.stub(IExtensionManagementService, >{ async getInstalled() { return []; }, diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 3277d22b..2c7d1734 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -464,10 +464,10 @@ suite('UserDataSyncRequestsSession', () => { test('too many requests are thrown when limit exceeded', async () => { const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); try { - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); } catch (error) { assert.ok(error instanceof UserDataSyncStoreError); assert.equal((error).code, UserDataSyncErrorCode.LocalTooManyRequests); @@ -478,19 +478,19 @@ suite('UserDataSyncRequestsSession', () => { test('requests are handled after session is expired', async () => { const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); await timeout(600); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); }); test('too many requests are thrown after session is expired', async () => { const testObject = new RequestsSession(1, 500, requestService, new NullLogService()); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); await timeout(600); - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); try { - await testObject.request({}, CancellationToken.None); + await testObject.request('url', {}, CancellationToken.None); } catch (error) { assert.ok(error instanceof UserDataSyncStoreError); assert.equal((error).code, UserDataSyncErrorCode.LocalTooManyRequests); diff --git a/src/vs/platform/webview/common/webviewManagerService.ts b/src/vs/platform/webview/common/webviewManagerService.ts index 8963865f..476201f8 100644 --- a/src/vs/platform/webview/common/webviewManagerService.ts +++ b/src/vs/platform/webview/common/webviewManagerService.ts @@ -11,6 +11,14 @@ import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMappi export const IWebviewManagerService = createDecorator('webviewManagerService'); +export interface WebviewWebContentsId { + readonly webContentsId: number; +} + +export interface WebviewWindowId { + readonly windowId: number; +} + export interface IWebviewManagerService { _serviceBrand: unknown; @@ -20,7 +28,7 @@ export interface IWebviewManagerService { didLoadResource(requestId: number, content: VSBuffer | undefined): void; - setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise; + setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise; } export interface RegisterWebviewMetadata { diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index c3b49724..03a6e306 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { webContents } from 'electron'; +import { WebContents, webContents } from 'electron'; import { VSBuffer } from 'vs/base/common/buffer'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { IRequestService } from 'vs/platform/request/common/request'; -import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService'; +import { IWebviewManagerService, RegisterWebviewMetadata, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService'; import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider'; import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; @@ -26,7 +26,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer @IFileService fileService: IFileService, @IRequestService requestService: IRequestService, @ITunnelService tunnelService: ITunnelService, - @IWindowsMainService windowsMainService: IWindowsMainService, + @IWindowsMainService private readonly windowsMainService: IWindowsMainService, ) { super(); this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, requestService, windowsMainService)); @@ -70,11 +70,24 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer }); } - public async setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise { - const contents = webContents.fromId(webContentsId); - if (!contents) { - throw new Error(`Invalid webContentsId: ${webContentsId}`); + public async setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise { + let contents: WebContents | undefined; + + if (typeof (id as WebviewWindowId).windowId === 'number') { + const { windowId } = (id as WebviewWindowId); + const window = this.windowsMainService.getWindowById(windowId); + if (!window) { + throw new Error(`Invalid windowId: ${windowId}`); + } + contents = window.win.webContents; + } else { + const { webContentsId } = (id as WebviewWebContentsId); + contents = webContents.fromId(webContentsId); + if (!contents) { + throw new Error(`Invalid webContentsId: ${webContentsId}`); + } } + if (!contents.isDestroyed()) { contents.setIgnoreMenuShortcuts(enabled); } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 9d614c54..40c95213 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { isMacintosh, isLinux, isWeb, IProcessEnvironment } from 'vs/base/common/platform'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -85,8 +84,8 @@ export function isFileToOpen(uriToOpen: IWindowOpenable): uriToOpen is IFileToOp export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden' | 'compact'; -export function getMenuBarVisibility(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): MenuBarVisibility { - const titleBarStyle = getTitleBarStyle(configurationService, environment, isExtensionDevelopment); +export function getMenuBarVisibility(configurationService: IConfigurationService): MenuBarVisibility { + const titleBarStyle = getTitleBarStyle(configurationService); const menuBarVisibility = configurationService.getValue('window.menuBarVisibility'); if (titleBarStyle === 'native' && menuBarVisibility === 'compact') { @@ -104,7 +103,7 @@ export interface IWindowSettings { openFilesInNewWindow: 'on' | 'off' | 'default'; openFoldersInNewWindow: 'on' | 'off' | 'default'; openWithoutArgumentsInNewWindow: 'on' | 'off'; - restoreWindows: 'all' | 'folders' | 'one' | 'none'; + restoreWindows: 'preserve' | 'all' | 'folders' | 'one' | 'none'; restoreFullscreen: boolean; zoomLevel: number; titleBarStyle: 'native' | 'custom'; @@ -119,17 +118,12 @@ export interface IWindowSettings { enableExperimentalProxyLoginDialog: boolean; } -export function getTitleBarStyle(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): 'native' | 'custom' { +export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' { if (isWeb) { return 'custom'; } - const configuration = configurationService.getValue('window'); - - const isDev = !environment.isBuilt || isExtensionDevelopment; - if (isMacintosh && isDev) { - return 'native'; // not enabled when developing due to https://github.com/electron/electron/issues/3647 - } + const configuration = configurationService.getValue('window'); if (configuration) { const useNativeTabs = isMacintosh && configuration.nativeTabs === true; @@ -230,6 +224,10 @@ export interface IWindowConfiguration { filesToDiff?: IPath[]; } +export interface IOSConfiguration { + release: string; +} + export interface INativeWindowConfiguration extends IWindowConfiguration, NativeParsedArgs { mainPid: number; @@ -256,6 +254,8 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native userEnv: IProcessEnvironment; filesToWait?: IPathsToWaitFor; + + os: IOSConfiguration; } /** diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index e1f5be82..83eccca7 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -12,8 +12,9 @@ import { IProcessEnvironment } from 'vs/base/common/platform'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; -import { Rectangle, BrowserWindow } from 'electron'; +import { Rectangle, BrowserWindow, WebContents } from 'electron'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IWindowState { width?: number; @@ -33,6 +34,11 @@ export const enum WindowMode { export interface ICodeWindow extends IDisposable { + readonly onLoad: Event; + readonly onReady: Event; + readonly onClose: Event; + readonly onDestroy: Event; + readonly whenClosedOrLoaded: Promise; readonly id: number; @@ -59,7 +65,7 @@ export interface ICodeWindow extends IDisposable { addTabbedWindow(window: ICodeWindow): void; load(config: INativeWindowConfiguration, isReload?: boolean): void; - reload(configuration?: INativeWindowConfiguration, cli?: NativeParsedArgs): void; + reload(cli?: NativeParsedArgs): void; focus(options?: { force: boolean }): void; close(): void; @@ -67,7 +73,7 @@ export interface ICodeWindow extends IDisposable { getBounds(): Rectangle; send(channel: string, ...args: any[]): void; - sendWhenReady(channel: string, ...args: any[]): void; + sendWhenReady(channel: string, token: CancellationToken, ...args: any[]): void; readonly isFullScreen: boolean; toggleFullScreen(): void; @@ -113,6 +119,7 @@ export interface IWindowsMainService { getLastActiveWindow(): ICodeWindow | undefined; getWindowById(windowId: number): ICodeWindow | undefined; + getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined; getWindows(): ICodeWindow[]; getWindowCount(): number; } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index b87cc374..6bcedb5c 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -3,18 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; +import { statSync, unlink } from 'fs'; import { basename, normalize, join, posix } from 'vs/base/common/path'; import { localize } from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; -import { mixin } from 'vs/base/common/objects'; +import { coalesce, distinct, firstOrDefault } from 'vs/base/common/arrays'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IStateService } from 'vs/platform/state/node/state'; import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; -import { screen, BrowserWindow, MessageBoxOptions, Display, app } from 'electron'; +import { screen, BrowserWindow, MessageBoxOptions, Display, app, WebContents } from 'electron'; import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; @@ -40,6 +39,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware } from 'vs/base/common/extpath'; import { CharCode } from 'vs/base/common/charCode'; import { getPathLabel } from 'vs/base/common/labels'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IWindowState { workspace?: IWorkspaceIdentifier; @@ -59,7 +59,7 @@ interface INewWindowState extends ISingleWindowState { hasDefaultState?: boolean; } -type RestoreWindowsSetting = 'all' | 'folders' | 'one' | 'none'; +type RestoreWindowsSetting = 'preserve' | 'all' | 'folders' | 'one' | 'none'; interface IOpenBrowserWindowOptions { userEnv?: IProcessEnvironment; @@ -72,7 +72,7 @@ interface IOpenBrowserWindowOptions { initialStartup?: boolean; - fileInputs?: IFileInputs; + filesToOpen?: IFilesToOpen; forceNewWindow?: boolean; forceNewTabbedWindow?: boolean; @@ -87,7 +87,7 @@ interface IPathParseOptions { remoteAuthority?: string; } -interface IFileInputs { +interface IFilesToOpen { filesToOpenOrCreate: IPath[]; filesToDiff: IPath[]; filesToWait?: IPathsToWaitFor; @@ -307,7 +307,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow); } - // 3.) All windows (except extension host) for N >= 2 to support restoreWindows: all or for auto update + // 3.) All windows (except extension host) for N >= 2 to support `restoreWindows: all` or for auto update // // Careful here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0) // so if we ever want to persist the UI state of the last closed window (window count === 1), it has @@ -385,17 +385,17 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic this.logService.trace('windowsManager#open'); openConfig = this.validateOpenConfig(openConfig); - const pathsToOpen = this.getPathsToOpen(openConfig); - this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen); - const foldersToAdd: IFolderPathToOpen[] = []; const foldersToOpen: IFolderPathToOpen[] = []; const workspacesToOpen: IWorkspacePathToOpen[] = []; const workspacesToRestore: IWorkspacePathToOpen[] = []; - const emptyToRestore: IEmptyWindowBackupInfo[] = []; // empty windows with backupPath + const emptyToRestore: IEmptyWindowBackupInfo[] = []; + let filesToOpen: IFilesToOpen | undefined; + let emptyToOpen = 0; - let emptyToOpen: number = 0; - let fileInputs: IFileInputs | undefined; // collect all file inputs + // Identify things to open from open config + const pathsToOpen = this.getPathsToOpen(openConfig); + this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen); for (const path of pathsToOpen) { if (isFolderPathToOpen(path)) { if (openConfig.addMode) { @@ -408,10 +408,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } else if (isWorkspacePathToOpen(path)) { workspacesToOpen.push(path); } else if (path.fileUri) { - if (!fileInputs) { - fileInputs = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; + if (!filesToOpen) { + filesToOpen = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; } - fileInputs.filesToOpenOrCreate.push(path); + filesToOpen.filesToOpenOrCreate.push(path); } else if (path.backupPath) { emptyToRestore.push({ backupFolder: basename(path.backupPath), remoteAuthority: path.remoteAuthority }); } else { @@ -421,14 +421,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // When run with --diff, take the files to open as files to diff // if there are exactly two files provided. - if (fileInputs && openConfig.diffMode && fileInputs.filesToOpenOrCreate.length === 2) { - fileInputs.filesToDiff = fileInputs.filesToOpenOrCreate; - fileInputs.filesToOpenOrCreate = []; + if (openConfig.diffMode && filesToOpen?.filesToOpenOrCreate.length === 2) { + filesToOpen.filesToDiff = filesToOpen.filesToOpenOrCreate; + filesToOpen.filesToOpenOrCreate = []; } // When run with --wait, make sure we keep the paths to wait for - if (fileInputs && openConfig.waitMarkerFileURI) { - fileInputs.filesToWait = { paths: [...fileInputs.filesToDiff, ...fileInputs.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI }; + if (filesToOpen && openConfig.waitMarkerFileURI) { + filesToOpen.filesToWait = { paths: [...filesToOpen.filesToDiff, ...filesToOpen.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI }; } // @@ -447,52 +447,61 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Open based on config - const usedWindows = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, fileInputs, foldersToAdd); + const { windows: usedWindows, filesOpenedInWindow } = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, filesToOpen, foldersToAdd); this.logService.trace(`windowsManager#open used window count ${usedWindows.length} (workspacesToOpen: ${workspacesToOpen.length}, foldersToOpen: ${foldersToOpen.length}, emptyToRestore: ${emptyToRestore.length}, emptyToOpen: ${emptyToOpen})`); // Make sure to pass focus to the most relevant of the windows if we open multiple if (usedWindows.length > 1) { - const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length); - let focusLastOpened = true; - let focusLastWindow = true; - // 1.) focus last active window if we are not instructed to open any paths - if (focusLastActive) { - const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath); - if (lastActiveWindow.length) { - lastActiveWindow[0].focus(); - focusLastOpened = false; - focusLastWindow = false; - } + // 1.) focus window we opened files in always with highest priority + if (filesOpenedInWindow) { + filesOpenedInWindow.focus(); } - // 2.) if instructed to open paths, focus last window which is not restored - if (focusLastOpened) { - for (let i = usedWindows.length - 1; i >= 0; i--) { - const usedWindow = usedWindows[i]; - if ( - (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => usedWindow.openedWorkspace && workspace.workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace - (usedWindow.backupPath && emptyToRestore.some(empty => usedWindow.backupPath && empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window - ) { - continue; + // Otherwise, find a good window based on open params + else { + const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length); + let focusLastOpened = true; + let focusLastWindow = true; + + // 2.) focus last active window if we are not instructed to open any paths + if (focusLastActive) { + const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath); + if (lastActiveWindow.length) { + lastActiveWindow[0].focus(); + focusLastOpened = false; + focusLastWindow = false; } - - usedWindow.focus(); - focusLastWindow = false; - break; } - } - // 3.) finally, always ensure to have at least last used window focused - if (focusLastWindow) { - usedWindows[usedWindows.length - 1].focus(); + // 3.) if instructed to open paths, focus last window which is not restored + if (focusLastOpened) { + for (let i = usedWindows.length - 1; i >= 0; i--) { + const usedWindow = usedWindows[i]; + if ( + (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => usedWindow.openedWorkspace && workspace.workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace + (usedWindow.backupPath && emptyToRestore.some(empty => usedWindow.backupPath && empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window + ) { + continue; + } + + usedWindow.focus(); + focusLastWindow = false; + break; + } + } + + // 4.) finally, always ensure to have at least last used window focused + if (focusLastWindow) { + usedWindows[usedWindows.length - 1].focus(); + } } } // Remember in recent document list (unless this opens for extension development) // Also do not add paths when files are opened for diffing, only if opened individually - const isDiff = fileInputs && fileInputs.filesToDiff.length > 0; + const isDiff = filesToOpen && filesToOpen.filesToDiff.length > 0; if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !openConfig.noRecentEntry) { const recents: IRecent[] = []; for (let pathToOpen of pathsToOpen) { @@ -513,7 +522,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // process can continue. We do this by deleting the waitMarkerFilePath. const waitMarkerFileURI = openConfig.waitMarkerFileURI; if (openConfig.context === OpenContext.CLI && waitMarkerFileURI && usedWindows.length === 1 && usedWindows[0]) { - usedWindows[0].whenClosedOrLoaded.then(() => fs.unlink(waitMarkerFileURI.fsPath, _error => undefined)); + usedWindows[0].whenClosedOrLoaded.then(() => unlink(waitMarkerFileURI.fsPath, () => undefined)); } return usedWindows; @@ -535,10 +544,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic foldersToOpen: IFolderPathToOpen[], emptyToRestore: IEmptyWindowBackupInfo[], emptyToOpen: number, - fileInputs: IFileInputs | undefined, + filesToOpen: IFilesToOpen | undefined, foldersToAdd: IFolderPathToOpen[] - ) { + ): { windows: ICodeWindow[], filesOpenedInWindow: ICodeWindow | undefined } { const usedWindows: ICodeWindow[] = []; + let filesOpenedInWindow: ICodeWindow | undefined = undefined; // Settings can decide if files/folders open in new window or not let { openFolderInNewWindow, openFilesInNewWindow } = this.shouldOpenNewWindow(openConfig); @@ -554,13 +564,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit const potentialWindowsCount = foldersToOpen.length + workspacesToOpen.length + emptyToRestore.length; - if (potentialWindowsCount === 0 && fileInputs) { + if (potentialWindowsCount === 0 && filesToOpen) { // Find suitable window or folder path to open files in - const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0]; + const fileToCheck = filesToOpen.filesToOpenOrCreate[0] || filesToOpen.filesToDiff[0]; // only look at the windows with correct authority - const windows = WindowsMainService.WINDOWS.filter(window => fileInputs && window.remoteAuthority === fileInputs.remoteAuthority); + const windows = WindowsMainService.WINDOWS.filter(window => filesToOpen && window.remoteAuthority === filesToOpen.remoteAuthority); const bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, @@ -587,46 +597,52 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic else { // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, fileInputs)); + const window = this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, filesToOpen); + usedWindows.push(window); - // Reset these because we handled them - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + filesToOpen = undefined; + filesOpenedInWindow = window; } } // Finally, if no window or folder is found, just open the files in an empty window else { - usedWindows.push(this.openInBrowserWindow({ + const window = this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, - fileInputs, + filesToOpen, forceNewWindow: true, - remoteAuthority: fileInputs.remoteAuthority, + remoteAuthority: filesToOpen.remoteAuthority, forceNewTabbedWindow: openConfig.forceNewTabbedWindow - })); + }); + usedWindows.push(window); - // Reset these because we handled them - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + filesToOpen = undefined; + filesOpenedInWindow = window; } } // Handle workspaces to open (instructed and to restore) - const allWorkspacesToOpen = arrays.distinct(workspacesToOpen, workspace => workspace.workspace.id); // prevent duplicates + const allWorkspacesToOpen = distinct(workspacesToOpen, workspace => workspace.workspace.id); // prevent duplicates if (allWorkspacesToOpen.length > 0) { // Check for existing instances - const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace))); + const windowsOnWorkspace = coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace))); if (windowsOnWorkspace.length > 0) { const windowOnWorkspace = windowsOnWorkspace[0]; - const fileInputsForWindow = (fileInputs?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? filesToOpen : undefined; // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow)); + const window = this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -639,14 +655,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } const remoteAuthority = workspaceToOpen.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, fileInputsForWindow)); + const window = this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -654,21 +672,23 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle folders to open (instructed and to restore) - const allFoldersToOpen = arrays.distinct(foldersToOpen, folder => extUriBiasedIgnorePathCase.getComparisonKey(folder.folderUri)); // prevent duplicates + const allFoldersToOpen = distinct(foldersToOpen, folder => extUriBiasedIgnorePathCase.getComparisonKey(folder.folderUri)); // prevent duplicates if (allFoldersToOpen.length > 0) { // Check for existing instances - const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri))); + const windowsOnFolderPath = coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri))); if (windowsOnFolderPath.length > 0) { const windowOnFolderPath = windowsOnFolderPath[0]; - const fileInputsForWindow = fileInputs?.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : undefined; + const filesToOpenInWindow = filesToOpen?.remoteAuthority === windowOnFolderPath.remoteAuthority ? filesToOpen : undefined; // Do open files - usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow)); + const window = this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -682,14 +702,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } const remoteAuthority = folderToOpen.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, fileInputsForWindow)); + const window = this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, filesToOpenInWindow); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -697,26 +719,28 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle empty to restore - const allEmptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates + const allEmptyToRestore = distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates if (allEmptyToRestore.length > 0) { allEmptyToRestore.forEach(emptyWindowBackupInfo => { const remoteAuthority = emptyWindowBackupInfo.remoteAuthority; - const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined; + const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined; - usedWindows.push(this.openInBrowserWindow({ + const window = this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, - fileInputs: fileInputsForWindow, + filesToOpen: filesToOpenInWindow, remoteAuthority, forceNewWindow: true, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, emptyWindowBackupInfo - })); + }); + usedWindows.push(window); - // Reset these because we handled them - if (fileInputsForWindow) { - fileInputs = undefined; + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpenInWindow) { + filesToOpen = undefined; + filesOpenedInWindow = window; } openFolderInNewWindow = true; // any other folders to open must open in new window then @@ -724,42 +748,48 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle empty to open (only if no other window opened) - if (usedWindows.length === 0 || fileInputs) { - if (fileInputs && !emptyToOpen) { + if (usedWindows.length === 0 || filesToOpen) { + if (filesToOpen && !emptyToOpen) { emptyToOpen++; } - const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined); + const remoteAuthority = filesToOpen ? filesToOpen.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined); for (let i = 0; i < emptyToOpen; i++) { - usedWindows.push(this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, fileInputs)); + const window = this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, filesToOpen); + usedWindows.push(window); - // Reset these because we handled them - fileInputs = undefined; - openFolderInNewWindow = true; // any other window to open must open in new window then + // Reset `filesToOpen` because we handled them and also remember window we used + if (filesToOpen) { + filesToOpen = undefined; + filesOpenedInWindow = window; + } + + // any other window to open must open in new window then + openFolderInNewWindow = true; } } - return arrays.distinct(usedWindows); + return { windows: distinct(usedWindows), filesOpenedInWindow }; } - private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, fileInputs?: IFileInputs): ICodeWindow { + private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen?: IFilesToOpen): ICodeWindow { this.logService.trace('windowsManager#doOpenFilesInExistingWindow'); window.focus(); // make sure window has focus const params: { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {}; - if (fileInputs) { - params.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate; - params.filesToDiff = fileInputs.filesToDiff; - params.filesToWait = fileInputs.filesToWait; + if (filesToOpen) { + params.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate; + params.filesToDiff = filesToOpen.filesToDiff; + params.filesToWait = filesToOpen.filesToWait; } if (configuration.userEnv) { params.termProgram = configuration.userEnv['TERM_PROGRAM']; } - window.sendWhenReady('vscode:openFiles', params); + window.sendWhenReady('vscode:openFiles', CancellationToken.None, params); return window; } @@ -768,12 +798,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic window.focus(); // make sure window has focus const request: IAddFoldersRequest = { foldersToAdd }; - window.sendWhenReady('vscode:addFolders', request); + window.sendWhenReady('vscode:addFolders', CancellationToken.None, request); return window; } - private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow { + private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/97172 } @@ -785,12 +815,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, - fileInputs, + filesToOpen, windowToUse }); } - private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow { + private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/49587 } @@ -801,7 +831,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic initialStartup: openConfig.initialStartup, workspace: folderOrWorkspace.workspace, folderUri: folderOrWorkspace.folderUri, - fileInputs, + filesToOpen, remoteAuthority: folderOrWorkspace.remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, @@ -812,6 +842,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private getPathsToOpen(openConfig: IOpenConfiguration): IPathToOpen[] { let windowsToOpen: IPathToOpen[]; let isCommandLineOrAPICall = false; + let restoredWindows = false; // Extract paths: from API if (openConfig.urisToOpen && openConfig.urisToOpen.length > 0) { @@ -833,11 +864,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Extract windows: from previous session else { windowsToOpen = this.doGetWindowsFromLastSession(); + restoredWindows = true; } // Convert multiple folders into workspace (if opened via API or CLI) // This will ensure to open these folders in one window instead of multiple - // If we are in addMode, we should not do this because in that case all + // If we are in `addMode`, we should not do this because in that case all // folders should be added to the existing window. if (!openConfig.addMode && isCommandLineOrAPICall) { const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri); @@ -853,6 +885,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } } + // Check for `window.startup` setting to include all windows + // from the previous session if this is the initial startup and we have + // not restored windows already otherwise. + // Use `unshift` to ensure any new window to open comes last + // for proper focus treatment. + if (openConfig.initialStartup && !restoredWindows && this.configurationService.getValue('window')?.restoreWindows === 'preserve') { + windowsToOpen.unshift(...this.doGetWindowsFromLastSession().filter(window => window.workspace || window.folderUri || window.backupPath)); + } + return windowsToOpen; } @@ -914,7 +955,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } } - // file uris const fileUris = cli['file-uri']; if (fileUris) { @@ -947,9 +987,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } private doGetWindowsFromLastSession(): IPathToOpen[] { - const restoreWindows = this.getRestoreWindowsSetting(); + const restoreWindowsSetting = this.getRestoreWindowsSetting(); - switch (restoreWindows) { + switch (restoreWindowsSetting) { // none: we always open an empty window case 'none': @@ -960,9 +1000,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // folders: restore last opened folders only case 'one': case 'all': + case 'preserve': case 'folders': + + // Collect previously opened windows const openedWindows: IWindowState[] = []; - if (restoreWindows !== 'one') { + if (restoreWindowsSetting !== 'one') { openedWindows.push(...this.windowsState.openedWindows); } if (this.windowsState.lastActiveWindow) { @@ -971,17 +1014,25 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const windowsToOpen: IPathToOpen[] = []; for (const openedWindow of openedWindows) { - if (openedWindow.workspace) { // Workspaces + + // Workspaces + if (openedWindow.workspace) { const pathToOpen = this.parseUri({ workspaceUri: openedWindow.workspace.configPath }, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen?.workspace) { windowsToOpen.push(pathToOpen); } - } else if (openedWindow.folderUri) { // Folders + } + + // Folders + else if (openedWindow.folderUri) { const pathToOpen = this.parseUri({ folderUri: openedWindow.folderUri }, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen?.folderUri) { windowsToOpen.push(pathToOpen); } - } else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Empty window, potentially editors open to be restored + } + + // Empty window, potentially editors open to be restored + else if (restoreWindowsSetting !== 'folders' && openedWindow.backupPath) { windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority }); } } @@ -1002,10 +1053,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (this.lifecycleMainService.wasRestarted) { restoreWindows = 'all'; // always reopen all windows when an update was applied } else { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); restoreWindows = windowConfig?.restoreWindows || 'all'; // by default restore all windows - if (!['all', 'folders', 'one', 'none'].includes(restoreWindows)) { + if (!['preserve', 'all', 'folders', 'one', 'none'].includes(restoreWindows)) { restoreWindows = 'all'; // by default restore all windows } } @@ -1142,7 +1193,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic let candidate = normalize(anyPath); try { - const candidateStat = fs.statSync(candidate); + const candidateStat = statSync(candidate); if (candidateStat.isFile()) { // Workspace (unless disabled via flag) @@ -1197,7 +1248,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private shouldOpenNewWindow(openConfig: IOpenConfiguration): { openFolderInNewWindow: boolean; openFilesInNewWindow: boolean; } { // let the user settings override how folders are open in a new window or same window unless we are forced - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const openFolderInNewWindowConfig = windowConfig?.openFoldersInNewWindow || 'default' /* default */; const openFilesInNewWindowConfig = windowConfig?.openFilesInNewWindow || 'off' /* default */; @@ -1348,7 +1399,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow { // Build INativeWindowConfiguration from config and options - const configuration: INativeWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI + const configuration = { ...options.cli } as INativeWindowConfiguration; configuration.appRoot = this.environmentService.appRoot; configuration.machineId = this.machineId; configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; @@ -1360,11 +1411,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic configuration.folderUri = options.folderUri; configuration.remoteAuthority = options.remoteAuthority; - const fileInputs = options.fileInputs; - if (fileInputs) { - configuration.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate; - configuration.filesToDiff = fileInputs.filesToDiff; - configuration.filesToWait = fileInputs.filesToWait; + const filesToOpen = options.filesToOpen; + if (filesToOpen) { + configuration.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate; + configuration.filesToDiff = filesToOpen.filesToDiff; + configuration.filesToWait = filesToOpen.filesToWait; } // if we know the backup folder upfront (for empty windows to restore), we can set it @@ -1385,18 +1436,18 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // New window if (!window) { - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); const state = this.getNewWindowState(configuration); // Window state is not from a previous session: only allow fullscreen if we inherit it or user wants fullscreen let allowFullscreen: boolean; if (state.hasDefaultState) { - allowFullscreen = (windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0); + allowFullscreen = !!(windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0); } // Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore else { - allowFullscreen = this.lifecycleMainService.wasRestarted || windowConfig?.restoreFullscreen; + allowFullscreen = !!(this.lifecycleMainService.wasRestarted || windowConfig?.restoreFullscreen); if (allowFullscreen && isMacintosh && WindowsMainService.WINDOWS.some(win => win.isFullScreen)) { // macOS: Electron does not allow to restore multiple windows in @@ -1581,7 +1632,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic state.y = Math.round(displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height! / 2)); // Check for newWindowDimensions setting and adjust accordingly - const windowConfig = this.configurationService.getValue('window'); + const windowConfig = this.configurationService.getValue('window'); let ensureNoOverlap = true; if (windowConfig?.newWindowDimensions) { if (windowConfig.newWindowDimensions === 'maximized') { @@ -1659,7 +1710,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const focusedWindow = this.getFocusedWindow() || this.getLastActiveWindow(); if (focusedWindow) { - focusedWindow.sendWhenReady(channel, ...args); + focusedWindow.sendWhenReady(channel, CancellationToken.None, ...args); } } @@ -1669,14 +1720,23 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic continue; // do not send if we are instructed to ignore it } - window.sendWhenReady(channel, payload); + window.sendWhenReady(channel, CancellationToken.None, payload); } } getWindowById(windowId: number): ICodeWindow | undefined { const res = WindowsMainService.WINDOWS.filter(window => window.id === windowId); - return arrays.firstOrDefault(res); + return firstOrDefault(res); + } + + getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined { + const browserWindow = BrowserWindow.fromWebContents(webContents); + if (!browserWindow) { + return undefined; + } + + return this.getWindowById(browserWindow.id); } getWindows(): ICodeWindow[] { diff --git a/src/vs/code/test/electron-main/windowsStateStorage.test.ts b/src/vs/platform/windows/test/electron-main/windowsStateStorage.test.ts similarity index 100% rename from src/vs/code/test/electron-main/windowsStateStorage.test.ts rename to src/vs/platform/windows/test/electron-main/windowsStateStorage.test.ts diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index c0c34d74..72ca615f 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -144,13 +144,14 @@ export interface IWorkspaceFolder extends IWorkspaceFolderData { export class Workspace implements IWorkspace { - private _foldersMap: TernarySearchTree = TernarySearchTree.forUris(); + private _foldersMap: TernarySearchTree = TernarySearchTree.forUris(this._ignorePathCasing); private _folders!: WorkspaceFolder[]; constructor( private _id: string, - folders: WorkspaceFolder[] = [], - private _configuration: URI | null = null + folders: WorkspaceFolder[], + private _configuration: URI | null, + private _ignorePathCasing: (key: URI) => boolean, ) { this.folders = folders; } @@ -158,6 +159,7 @@ export class Workspace implements IWorkspace { update(workspace: Workspace) { this._id = workspace.id; this._configuration = workspace.configuration; + this._ignorePathCasing = workspace._ignorePathCasing; this.folders = workspace.folders; } @@ -195,7 +197,7 @@ export class Workspace implements IWorkspace { } private updateFoldersMap(): void { - this._foldersMap = TernarySearchTree.forUris(); + this._foldersMap = TernarySearchTree.forUris(this._ignorePathCasing); for (const folder of this.folders) { this._foldersMap.set(folder.uri, folder); } diff --git a/src/vs/platform/workspace/test/common/testWorkspace.ts b/src/vs/platform/workspace/test/common/testWorkspace.ts index 75762fed..0cdb093a 100644 --- a/src/vs/platform/workspace/test/common/testWorkspace.ts +++ b/src/vs/platform/workspace/test/common/testWorkspace.ts @@ -4,8 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { Workspace, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { isWindows } from 'vs/base/common/platform'; +import { Workspace as BaseWorkspace, toWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { isLinux, isWindows } from 'vs/base/common/platform'; + +export class Workspace extends BaseWorkspace { + constructor( + id: string, + folders: WorkspaceFolder[] = [], + configuration: URI | null = null, + ignorePathCasing: (key: URI) => boolean = () => !isLinux + ) { + super(id, folders, configuration, ignorePathCasing); + } +} const wsUri = URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace'); export const TestWorkspace = testWorkspace(wsUri); diff --git a/src/vs/platform/workspace/test/common/workspace.test.ts b/src/vs/platform/workspace/test/common/workspace.test.ts index 10e25727..1d3f78f7 100644 --- a/src/vs/platform/workspace/test/common/workspace.test.ts +++ b/src/vs/platform/workspace/test/common/workspace.test.ts @@ -8,7 +8,7 @@ import * as path from 'vs/base/common/path'; import { Workspace, toWorkspaceFolders, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { IRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; -import { isWindows } from 'vs/base/common/platform'; +import { isLinux, isWindows } from 'vs/base/common/platform'; suite('Workspace', () => { @@ -27,7 +27,7 @@ suite('Workspace', () => { test('getFolder returns the folder with given uri', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 }); - let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })], null, () => !isLinux); const actual = testObject.getFolder(expected.uri); @@ -36,7 +36,7 @@ suite('Workspace', () => { test('getFolder returns the folder if the uri is sub', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 }); - let testObject = new Workspace('', [expected, new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); + let testObject = new Workspace('', [expected, new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a'))); @@ -45,7 +45,7 @@ suite('Workspace', () => { test('getFolder returns the closest folder if the uri is sub', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 }); - let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a'))); @@ -54,7 +54,7 @@ suite('Workspace', () => { test('getFolder returns the folder even if the uri has query path', () => { const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 }); - let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a')).with({ query: 'somequery' })); @@ -62,7 +62,7 @@ suite('Workspace', () => { }); test('getFolder returns null if the uri is not sub', () => { - let testObject = new Workspace('', [new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })], null, () => !isLinux); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'main/a'))); diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index b6321d73..7a5baa22 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -330,53 +330,57 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa }); // Recent Workspaces - if (this.getRecentlyOpened().workspaces.length > 0) { + try { + if (this.getRecentlyOpened().workspaces.length > 0) { - // The user might have meanwhile removed items from the jump list and we have to respect that - // so we need to update our list of recent paths with the choice of the user to not add them again - // Also: Windows will not show our custom category at all if there is any entry which was removed - // by the user! See https://github.com/microsoft/vscode/issues/15052 - let toRemove: URI[] = []; - for (let item of app.getJumpListSettings().removedItems) { - const args = item.args; - if (args) { - const match = /^--(folder|file)-uri\s+"([^"]+)"$/.exec(args); - if (match) { - toRemove.push(URI.parse(match[2])); + // The user might have meanwhile removed items from the jump list and we have to respect that + // so we need to update our list of recent paths with the choice of the user to not add them again + // Also: Windows will not show our custom category at all if there is any entry which was removed + // by the user! See https://github.com/microsoft/vscode/issues/15052 + let toRemove: URI[] = []; + for (let item of app.getJumpListSettings().removedItems) { + const args = item.args; + if (args) { + const match = /^--(folder|file)-uri\s+"([^"]+)"$/.exec(args); + if (match) { + toRemove.push(URI.parse(match[2])); + } } } + this.removeRecentlyOpened(toRemove); + + // Add entries + jumpList.push({ + type: 'custom', + name: nls.localize('recentFolders', "Recent Workspaces"), + items: arrays.coalesce(this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(recent => { + const workspace = isRecentWorkspace(recent) ? recent.workspace : recent.folderUri; + const title = recent.label ? splitName(recent.label).name : this.getSimpleWorkspaceLabel(workspace, this.environmentService.untitledWorkspacesHome); + + let description; + let args; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(dirname(workspace), this.environmentService)); + args = `--folder-uri "${workspace.toString()}"`; + } else { + description = nls.localize('workspaceDesc', "{0} {1}", getBaseLabel(workspace.configPath), getPathLabel(dirname(workspace.configPath), this.environmentService)); + args = `--file-uri "${workspace.configPath.toString()}"`; + } + + return { + type: 'task', + title, + description, + program: process.execPath, + args, + iconPath: 'explorer.exe', // simulate folder icon + iconIndex: 0 + }; + })) + }); } - this.removeRecentlyOpened(toRemove); - - // Add entries - jumpList.push({ - type: 'custom', - name: nls.localize('recentFolders', "Recent Workspaces"), - items: arrays.coalesce(this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(recent => { - const workspace = isRecentWorkspace(recent) ? recent.workspace : recent.folderUri; - const title = recent.label ? splitName(recent.label).name : this.getSimpleWorkspaceLabel(workspace, this.environmentService.untitledWorkspacesHome); - - let description; - let args; - if (isSingleFolderWorkspaceIdentifier(workspace)) { - description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(dirname(workspace), this.environmentService)); - args = `--folder-uri "${workspace.toString()}"`; - } else { - description = nls.localize('workspaceDesc', "{0} {1}", getBaseLabel(workspace.configPath), getPathLabel(dirname(workspace.configPath), this.environmentService)); - args = `--file-uri "${workspace.configPath.toString()}"`; - } - - return { - type: 'task', - title, - description, - program: process.execPath, - args, - iconPath: 'explorer.exe', // simulate folder icon - iconIndex: 0 - }; - })) - }); + } catch (error) { + this.logService.warn('updateWindowsJumpList#recentWorkspaces', error); // https://github.com/microsoft/vscode/issues/111177 } // Recent @@ -387,7 +391,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa try { app.setJumpList(jumpList); } catch (error) { - this.logService.warn('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors + this.logService.warn('updateWindowsJumpList#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors } } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 99ba257d..e1c2dd30 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -791,8 +791,8 @@ declare module 'vscode' { /** * A reference to a named icon. Currently, [File](#ThemeIcon.File), [Folder](#ThemeIcon.Folder), - * and [codicons](https://microsoft.github.io/vscode-codicons/dist/codicon.html) are supported. - * Using a theme icon is preferred over a custom icon as it gives theme authors the possibility to change the icons. + * and [ThemeIcon ids](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing) are supported. + * Using a theme icon is preferred over a custom icon as it gives product theme authors the possibility to change the icons. * * *Note* that theme icons can also be rendered inside labels and descriptions. Places that support theme icons spell this out * and they use the `$()`-syntax, for instance `quickPick.label = "Hello World $(globe)"`. @@ -809,7 +809,7 @@ declare module 'vscode' { static readonly Folder: ThemeIcon; /** - * The id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. + * The id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. */ readonly id: string; @@ -820,7 +820,7 @@ declare module 'vscode' { /** * Creates a reference to a theme icon. - * @param id id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. + * @param id id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. * @param color optional `ThemeColor` for the icon. The color is currently only used in [TreeItem](#TreeItem). */ constructor(id: string, color?: ThemeColor); @@ -1878,8 +1878,9 @@ declare module 'vscode' { /** * A relative pattern is a helper to construct glob patterns that are matched - * relatively to a base path. The base path can either be an absolute file path - * or a [workspace folder](#WorkspaceFolder). + * relatively to a base file path. The base path can either be an absolute file + * path as string or uri or a [workspace folder](#WorkspaceFolder), which is the + * preferred way of creating the relative pattern. */ export class RelativePattern { @@ -1898,14 +1899,28 @@ declare module 'vscode' { pattern: string; /** - * Creates a new relative pattern object with a base path and pattern to match. This pattern - * will be matched on file paths relative to the base path. + * Creates a new relative pattern object with a base file path and pattern to match. This pattern + * will be matched on file paths relative to the base. * - * @param base A base file path to which this pattern will be matched against relatively. - * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on file paths - * relative to the base path. + * Example: + * ```ts + * const folder = vscode.workspace.workspaceFolders?.[0]; + * if (folder) { + * + * // Match any TypeScript file in the root of this workspace folder + * const pattern1 = new vscode.RelativePattern(folder, '*.ts'); + * + * // Match any TypeScript file in `someFolder` inside this workspace folder + * const pattern2 = new vscode.RelativePattern(folder, 'someFolder/*.ts'); + * } + * ``` + * + * @param base A base to which this pattern will be matched against relatively. It is recommended + * to pass in a [workspace folder](#WorkspaceFolder) if the pattern should match inside the workspace. + * Otherwise, a uri or string should only be used if the pattern is for a file path outside the workspace. + * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on paths relative to the base. */ - constructor(base: WorkspaceFolder | string, pattern: string) + constructor(base: WorkspaceFolder | Uri | string, pattern: string) } /** @@ -4325,6 +4340,12 @@ declare module 'vscode' { * [Folding](https://code.visualstudio.com/docs/editor/codebasics#_folding) in the editor. */ export interface FoldingRangeProvider { + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChangeFoldingRanges?: Event; + /** * Returns a list of folding ranges or null and undefined if the provider * does not want to participate or was cancelled. @@ -4520,6 +4541,50 @@ declare module 'vscode' { provideCallHierarchyOutgoingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; } + /** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid range contents. + */ + export class LinkedEditingRanges { + /** + * Create a new linked editing ranges object. + * + * @param ranges A list of ranges that can be edited together + * @param wordPattern An optional word pattern that describes valid contents for the given ranges + */ + constructor(ranges: Range[], wordPattern?: RegExp); + + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap. + */ + readonly ranges: Range[]; + + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + readonly wordPattern?: RegExp; + } + + /** + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. + */ + export interface LinkedEditingRangeProvider { + /** + * For a given position in a document, returns the range of the symbol at the position and all ranges + * that have the same content. A change to one of the ranges can be applied to all other ranges if the new content + * is valid. An optional word pattern can be returned with the result to describe valid contents. + * If no result-specific word pattern is provided, the word pattern from the language configuration is used. + * + * @param document The document in which the provider was invoked. + * @param position The position at which the provider was invoked. + * @param token A cancellation token. + * @return A list of ranges that can be edited together + */ + provideLinkedEditingRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + /** * A tuple of two characters, like a pair of * opening and closing brackets. @@ -5339,7 +5404,7 @@ declare module 'vscode' { * * `My text $(icon-name) contains icons like $(icon-name) this one.` * - * Where the icon-name is taken from the [codicon](https://microsoft.github.io/vscode-codicons/dist/codicon.html) icon set, e.g. + * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. * `light-bulb`, `thumbsup`, `zap` etc. */ text: string; @@ -5525,6 +5590,72 @@ declare module 'vscode' { tooltip?: string; } + /** + * A file decoration represents metadata that can be rendered with a file. + */ + export class FileDecoration { + + /** + * A very short string that represents this decoration. + */ + badge?: string; + + /** + * A human-readable tooltip for this decoration. + */ + tooltip?: string; + + /** + * The color of this decoration. + */ + color?: ThemeColor; + + /** + * A flag expressing that this decoration should be + * propagated to its parents. + */ + propagate?: boolean; + + /** + * Creates a new decoration. + * + * @param badge A letter that represents the decoration. + * @param tooltip The tooltip of the decoration. + * @param color The color of the decoration. + */ + constructor(badge?: string, tooltip?: string, color?: ThemeColor); + } + + /** + * The decoration provider interfaces defines the contract between extensions and + * file decorations. + */ + export interface FileDecorationProvider { + + /** + * An optional event to signal that decorations for one or many files have changed. + * + * *Note* that this event should be used to propagate information about children. + * + * @see [EventEmitter](#EventEmitter) + */ + onDidChangeFileDecorations?: Event; + + /** + * Provide decorations for a given uri. + * + * *Note* that this function is only called when a file gets rendered in the UI. + * This means a decoration from a descendent that propagates upwards must be signaled + * to the editor via the [onDidChangeFileDecorations](#FileDecorationProvider.onDidChangeFileDecorations)-event. + * + * @param uri The uri of the file to provide a decoration for. + * @param token A cancellation token. + * @returns A decoration or a thenable that resolves to such. + */ + provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; + } + + /** * In a remote window the extension kind describes if an extension * runs where the UI (window) runs or if an extension runs remotely. @@ -6180,14 +6311,13 @@ declare module 'vscode' { */ export class CustomExecution { /** - * Constructs a CustomExecution task object. The callback will be executed the task is run, at which point the + * Constructs a CustomExecution task object. The callback will be executed when the task is run, at which point the * extension should return the Pseudoterminal it will "run in". The task should wait to do further execution until * [Pseudoterminal.open](#Pseudoterminal.open) is called. Task cancellation should be handled using * [Pseudoterminal.close](#Pseudoterminal.close). When the task is complete fire * [Pseudoterminal.onDidClose](#Pseudoterminal.onDidClose). - * @param process The [Pseudoterminal](#Pseudoterminal) to be used by the task to display output. * @param callback The callback that will be called when the task is started by a user. Any ${} style variables that - * were in the task definition will be resolved and passed into the callback. + * were in the task definition will be resolved and passed into the callback as `resolvedDefinition`. */ constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); } @@ -6413,9 +6543,9 @@ declare module 'vscode' { readonly execution: TaskExecution; /** - * The process's exit code. + * The process's exit code. Will be `undefined` when the task is terminated. */ - readonly exitCode: number; + readonly exitCode: number | undefined; } export interface TaskFilter { @@ -6860,6 +6990,21 @@ declare module 'vscode' { * @param options Defines if existing files should be overwritten. */ copy(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; + + /** + * Check if a given file system supports writing files. + * + * Keep in mind that just because a file system supports writing, that does + * not mean that writes will always succeed. There may be permissions issues + * or other errors that prevent writing a file. + * + * @param scheme The scheme of the filesystem, for example `file` or `git`. + * + * @return `true` if the file system supports writing, `false` if it does not + * support writing (i.e. it is readonly), and `undefined` if VS Code does not + * know about the filesystem. + */ + isWritableFileSystem(scheme: string): boolean | undefined; } /** @@ -8524,6 +8669,14 @@ declare module 'vscode' { */ export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; + /** + * Register a file decoration provider. + * + * @param provider A [FileDecorationProvider](#FileDecorationProvider). + * @return A [disposable](#Disposable) that unregisters the provider. + */ + export function registerFileDecorationProvider(provider: FileDecorationProvider): Disposable; + /** * The currently active color theme as configured in the settings. The active * theme can be changed via the `workbench.colorTheme` setting. @@ -8700,13 +8853,34 @@ declare module 'vscode' { * @return Parent of `element`. */ getParent?(element: T): ProviderResult; + + /** + * Called only on hover to resolve the [TreeItem](#TreeItem.tooltip) property if it is undefined. + * Only properties that were undefined can be resolved in `resolveTreeItem`. + * Functionality may be expanded later to include being called to resolve other missing + * properties on selection and/or on open. + * + * Will only ever be called once per TreeItem. + * + * onDidChangeTreeData should not be triggered from within resolveTreeItem. + * + * *Note* that this function is called when tree items are already showing in the UI. + * Because of that, no property that changes the presentation (label, description, command, etc.) + * can be changed. + * + * @param element The object associated with the TreeItem + * @param item Undefined properties of `item` should be set then `item` should be returned. + * @return The resolved tree item or a thenable that resolves to such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveTreeItem?(item: TreeItem, element: T): ProviderResult; } export class TreeItem { /** * A human-readable string describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). */ - label?: string; + label?: string | TreeItemLabel; /** * Optional id for the tree item that has to be unique across tree. The id is used to preserve the selection and expansion state of the tree item. @@ -8739,10 +8913,14 @@ declare module 'vscode' { /** * The tooltip text when you hover over this item. */ - tooltip?: string | undefined; + tooltip?: string | MarkdownString | undefined; /** * The [command](#Command) that should be executed when the tree item is selected. + * + * Please use `vscode.open` or `vscode.diff` as command IDs when the tree item is opening + * something in the editor. Using these commands ensures that the resulting editor will + * appear consistent with how other built-in trees open editors. */ command?: Command; @@ -8782,7 +8960,7 @@ declare module 'vscode' { * @param label A human-readable string describing this item * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) */ - constructor(label: string, collapsibleState?: TreeItemCollapsibleState); + constructor(label: string | TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); /** * @param resourceUri The [uri](#Uri) of the resource representing this item. @@ -8809,6 +8987,23 @@ declare module 'vscode' { Expanded = 2 } + /** + * Label describing the [Tree item](#TreeItem) + */ + export interface TreeItemLabel { + + /** + * A human-readable string describing the [Tree item](#TreeItem). + */ + label: string; + + /** + * Ranges in the label to highlight. A range is defined as a tuple of two number where the + * first is the inclusive start index and the second the exclusive end index + */ + highlights?: [number, number][]; + } + /** * Value-object describing what options a terminal should use. */ @@ -9924,6 +10119,8 @@ declare module 'vscode' { * flags to ignore certain kinds of events can be provided. To stop listening to events the watcher must be disposed. * * *Note* that only files within the current [workspace folders](#workspace.workspaceFolders) can be watched. + * *Note* that when watching for file changes such as '**​/*.js', notifications will not be sent when a parent folder is + * moved or deleted (this is a known limitation of the current implementation and may change in the future). * * @param globPattern A [glob pattern](#GlobPattern) that is applied to the absolute paths of created, changed, * and deleted files. Use a [relative pattern](#RelativePattern) to limit events to a certain [workspace folder](#WorkspaceFolder). @@ -10691,6 +10888,19 @@ declare module 'vscode' { */ export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyProvider): Disposable; + /** + * Register a linked editing range provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their [score](#languages.match) and the best-matching provider that has a result is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A linked editing range provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerLinkedEditingRangeProvider(selector: DocumentSelector, provider: LinkedEditingRangeProvider): Disposable; + /** * Set a [language configuration](#LanguageConfiguration) for a language. * @@ -10699,6 +10909,7 @@ declare module 'vscode' { * @return A [disposable](#Disposable) that unsets this configuration. */ export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; + } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 4076358f..19e8d8f1 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -223,6 +223,13 @@ declare module 'vscode' { } + export interface TunnelCreationOptions { + /** + * True when the local operating system will require elevation to use the requested local port. + */ + elevationRequired?: boolean; + } + export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; export class RemoteAuthorityResolverError extends Error { @@ -238,8 +245,11 @@ declare module 'vscode' { * Can be optionally implemented if the extension can forward ports better than the core. * When not implemented, the core will use its default forwarding logic. * When implemented, the core will use this to forward ports. + * + * To enable the "Change Local Port" action on forwarded ports, make sure to set the `localAddress` of + * the returned `Tunnel` to a `{ port: number, host: string; }` and not a string. */ - tunnelFactory?: (tunnelOptions: TunnelOptions) => Thenable | undefined; + tunnelFactory?: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined; /** * Provides filtering for candidate ports. @@ -741,71 +751,6 @@ declare module 'vscode' { //#endregion - //#region file-decorations: https://github.com/microsoft/vscode/issues/54938 - - - export class FileDecoration { - - /** - * A very short string that represents this decoration. - */ - badge?: string; - - /** - * A human-readable tooltip for this decoration. - */ - tooltip?: string; - - /** - * The color of this decoration. - */ - color?: ThemeColor; - - /** - * A flag expressing that this decoration should be - * propagated to its parents. - */ - propagate?: boolean; - - /** - * Creates a new decoration. - * - * @param badge A letter that represents the decoration. - * @param tooltip The tooltip of the decoration. - * @param color The color of the decoration. - */ - constructor(badge?: string, tooltip?: string, color?: ThemeColor); - } - - /** - * The decoration provider interfaces defines the contract between extensions and - * file decorations. - */ - export interface FileDecorationProvider { - - /** - * An event to signal decorations for one or many files have changed. - * - * @see [EventEmitter](#EventEmitter - */ - onDidChange: Event; - - /** - * Provide decorations for a given uri. - * - * @param uri The uri of the file to provide a decoration for. - * @param token A cancellation token. - * @returns A decoration or a thenable that resolves to such. - */ - provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; - } - - export namespace window { - export function registerDecorationProvider(provider: FileDecorationProvider): Disposable; - } - - //#endregion - //#region debug /** @@ -824,46 +769,9 @@ declare module 'vscode' { // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_Variable). } - // deprecated debug API - - export interface DebugConfigurationProvider { - /** - * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. - * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead - */ - debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; - } - //#endregion - //#region LogLevel: https://github.com/microsoft/vscode/issues/85992 - /** - * @deprecated DO NOT USE, will be removed - */ - export enum LogLevel { - Trace = 1, - Debug = 2, - Info = 3, - Warning = 4, - Error = 5, - Critical = 6, - Off = 7 - } - - export namespace env { - /** - * @deprecated DO NOT USE, will be removed - */ - export const logLevel: LogLevel; - - /** - * @deprecated DO NOT USE, will be removed - */ - export const onDidChangeLogLevel: Event; - } - - //#endregion //#region @joaomoreno: SCM validation @@ -1008,45 +916,8 @@ declare module 'vscode' { //#endregion //#region Tree View: https://github.com/microsoft/vscode/issues/61313 - /** - * Label describing the [Tree item](#TreeItem) - */ - export interface TreeItemLabel { - - /** - * A human-readable string describing the [Tree item](#TreeItem). - */ - label: string; - - /** - * Ranges in the label to highlight. A range is defined as a tuple of two number where the - * first is the inclusive start index and the second the exclusive end index - */ - highlights?: [number, number][]; - - } - - // https://github.com/microsoft/vscode/issues/100741 - export interface TreeDataProvider { - resolveTreeItem?(element: T, item: TreeItem2): TreeItem2 | Thenable; - } - - export class TreeItem2 extends TreeItem { - /** - * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). - */ - label?: string | TreeItemLabel | /* for compilation */ any; - - /** - * Content to be shown when you hover over the tree item. - */ - tooltip?: string | MarkdownString | /* for compilation */ any; - - /** - * @param label Label describing this item - * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) - */ - constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); + export interface TreeView extends Disposable { + reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number }): Thenable; } //#endregion @@ -1112,45 +983,6 @@ declare module 'vscode' { //#endregion - //#region OnTypeRename: https://github.com/microsoft/vscode/issues/88424 - - /** - * The rename provider interface defines the contract between extensions and - * the live-rename feature. - */ - export interface OnTypeRenameProvider { - /** - * Provide a list of ranges that can be live renamed together. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @return A list of ranges that can be live-renamed togehter. The ranges must have - * identical length and contain identical text content. The ranges cannot overlap. Optional a word pattern - * that overrides the word pattern defined when registering the provider. Live rename stops as soon as the renamed content - * no longer matches the word pattern. - */ - provideOnTypeRenameRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<{ ranges: Range[]; wordPattern?: RegExp; }>; - } - - namespace languages { - /** - * Register a rename provider that works on type. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their [score](#languages.match) and the best-matching provider is used. Failure - * of the selected provider will cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An on type rename provider. - * @param wordPattern Word pattern for this provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerOnTypeRenameProvider(selector: DocumentSelector, provider: OnTypeRenameProvider, wordPattern?: RegExp): Disposable; - } - - //#endregion - //#region Custom editor move https://github.com/microsoft/vscode/issues/86146 // TODO: Also for custom editor @@ -1403,6 +1235,12 @@ declare module 'vscode' { * The document's current run state */ runState?: NotebookRunState; + + /** + * Whether the document is trusted, default to true + * When false, insecure outputs like HTML, JavaScript, SVG will not be rendered. + */ + trusted?: boolean; } export interface NotebookDocumentContentOptions { @@ -1824,6 +1662,13 @@ declare module 'vscode' { dispose(): void; } + export interface NotebookDocumentShowOptions { + viewColumn?: ViewColumn; + preserveFocus?: boolean; + preview?: boolean; + selection?: NotebookCellRange; + } + export namespace notebook { export function registerNotebookContentProvider( @@ -1891,6 +1736,7 @@ declare module 'vscode' { export const onDidChangeActiveNotebookEditor: Event; export const onDidChangeNotebookEditorSelection: Event; export const onDidChangeNotebookEditorVisibleRanges: Event; + export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Promise; } //#endregion @@ -2138,36 +1984,338 @@ declare module 'vscode' { } //#endregion - //#region https://github.com/microsoft/vscode/issues/91697 - - export interface FileSystem { + //#region https://github.com/microsoft/vscode/issues/107467 + /* + General activation events: + - `onLanguage:*` most test extensions will want to activate when their + language is opened to provide code lenses. + - `onTests:*` new activation event very simiular to `workspaceContains`, + but only fired when the user wants to run tests or opens the test explorer. + */ + export namespace test { /** - * Check if a given file system supports writing files. - * - * Keep in mind that just because a file system supports writing, that does - * not mean that writes will always succeed. There may be permissions issues - * or other errors that prevent writing a file. - * - * @param scheme The scheme of the filesystem, for example `file` or `git`. - * - * @return `true` if the file system supports writing, `false` if it does not - * support writing (i.e. it is readonly), and `undefined` if VS Code does not - * know about the filesystem. + * Registers a provider that discovers tests for the given document + * selectors. It is activated when either tests need to be enumerated, or + * a document matching the selector is opened. */ - isWritableFileSystem(scheme: string): boolean | undefined; + export function registerTestProvider(testProvider: TestProvider): Disposable; + + /** + * Runs tests with the given options. If no options are given, then + * all tests are run. Returns the resulting test run. + */ + export function runTests(options: TestRunOptions): Thenable; + + /** + * Returns an observer that retrieves tests in the given workspace folder. + */ + export function createWorkspaceTestObserver(workspaceFolder: WorkspaceFolder): TestObserver; + + /** + * Returns an observer that retrieves tests in the given text document. + */ + export function createDocumentTestObserver(document: TextDocument): TestObserver; } + export interface TestObserver { + /** + * List of tests returned by test provider for files in the workspace. + */ + readonly tests: ReadonlyArray; + /** + * An event that fires when an existing test in the collection changes, or + * null if a top-level test was added or removed. When fired, the consumer + * should check the test item and all its children for changes. + */ + readonly onDidChangeTest: Event; + + /** + * An event the fires when all test providers have signalled that the tests + * the observer references have been discovered. Providers may continue to + * watch for changes and cause {@link onDidChangeTest} to fire as files + * change, until the observer is disposed. + * + * @todo as below + */ + readonly onDidDiscoverInitialTests: Event; + + /** + * Dispose of the observer, allowing VS Code to eventually tell test + * providers that they no longer need to update tests. + */ + dispose(): void; + } + + export interface TestChangeEvent { + /** + * List of all tests that are newly added. + */ + readonly added: ReadonlyArray; + + /** + * List of existing tests that have updated. + */ + readonly updated: ReadonlyArray; + + /** + * List of existing tests that have been removed. + */ + readonly removed: ReadonlyArray; + + /** + * Highest node in the test tree under which changes were made. This can + * be easily plugged into events like the TreeDataProvider update event. + */ + readonly commonChangeAncestor: TestItem | null; + } + + /** + * Tree of tests returned from the provide methods in the {@link TestProvider}. + */ + export interface TestHierarchy { + /** + * Root node for tests. The `testRoot` instance must not be replaced over + * the lifespan of the TestHierarchy, since you will need to reference it + * in `onDidChangeTest` when a test is added or removed. + */ + readonly root: T; + + /** + * An event that fires when an existing test under the `root` changes. + * This can be a result of a state change in a test run, a property update, + * or an update to its children. Changes made to tests will not be visible + * to {@link TestObserver} instances until this event is fired. + * + * This will signal a change recursively to all children of the given node. + * For example, firing the event with the {@link testRoot} will refresh + * all tests. + */ + readonly onDidChangeTest: Event; + + /** + * An event that should be fired when all tests that are currently defined + * have been discovered. The provider should continue to watch for changes + * and fire `onDidChangeTest` until the hierarchy is disposed. + * + * @todo can this be covered by existing progress apis? Or return a promise + */ + readonly onDidDiscoverInitialTests: Event; + + /** + * Dispose will be called when there are no longer observers interested + * in the hierarchy. + */ + dispose(): void; + } + + /** + * Discovers and provides tests. It's expected that the TestProvider will + * ambiently listen to {@link vscode.window.onDidChangeVisibleTextEditors} to + * provide test information about the open files for use in code lenses and + * other file-specific UI. + * + * Additionally, the UI may request it to discover tests for the workspace + * via `addWorkspaceTests`. + * + * @todo rename from provider + */ + export interface TestProvider { + /** + * Requests that tests be provided for the given workspace. This will + * generally be called when tests need to be enumerated for the + * workspace. + * + * It's guaranteed that this method will not be called again while + * there is a previous undisposed watcher for the given workspace folder. + */ + createWorkspaceTestHierarchy?(workspace: WorkspaceFolder): TestHierarchy; + + /** + * Requests that tests be provided for the given document. This will + * be called when tests need to be enumerated for a single open file, + * for instance by code lens UI. + */ + createDocumentTestHierarchy?(document: TextDocument): TestHierarchy; + + /** + * Starts a test run. This should cause {@link onDidChangeTest} to + * fire with update test states during the run. + * @todo this will eventually need to be able to return a summary report, coverage for example. + */ + runTests?(options: TestRunOptions, cancellationToken: CancellationToken): ProviderResult; + } + + /** + * Options given to `TestProvider.runTests` + */ + export interface TestRunOptions { + /** + * Array of specific tests to run. The {@link TestProvider.testRoot} may + * be provided as an indication to run all tests. + */ + tests: T[]; + + /** + * Whether or not tests in this run should be debugged. + */ + debug: boolean; + } + + /** + * A test item is an item shown in the "test explorer" view. It encompasses + * both a suite and a test, since they have almost or identical capabilities. + */ + export interface TestItem { + /** + * Display name describing the test case. + */ + label: string; + + /** + * Optional description that appears next to the label. + */ + description?: string; + + /** + * Whether this test item can be run individually, defaults to `true` + * if not provided. + * + * In some cases, like Go's tests, test can have children but these + * children cannot be run independently. + */ + runnable?: boolean; + + /** + * Whether this test item can be debugged. Defaults to `false` if not provided. + */ + debuggable?: boolean; + + /** + * VS Code location. + */ + location?: Location; + + /** + * Optional list of nested tests for this item. + */ + children?: TestItem[]; + + /** + * Test run state. Will generally be {@link TestRunState.Unset} by + * default. + */ + state: TestState; + } + + export enum TestRunState { + // Initial state + Unset = 0, + // Test is currently running + Running = 1, + // Test run has passed + Passed = 2, + // Test run has failed (on an assertion) + Failed = 3, + // Test run has been skipped + Skipped = 4, + // Test run failed for some other reason (compilation error, timeout, etc) + Errored = 5 + } + + /** + * TestState includes a test and its run state. This is included in the + * {@link TestItem} and is immutable; it should be replaced in th TestItem + * in order to update it. This allows consumers to quickly and easily check + * for changes via object identity. + */ + export class TestState { + /** + * Current state of the test. + */ + readonly runState: TestRunState; + + /** + * Optional duration of the test run, in milliseconds. + */ + readonly duration?: number; + + /** + * Associated test run message. Can, for example, contain assertion + * failure information if the test fails. + */ + readonly messages: ReadonlyArray>; + + /** + * @param state Run state to hold in the test state + * @param messages List of associated messages for the test + * @param duration Length of time the test run took, if appropriate. + */ + constructor(runState: TestRunState, messages?: TestMessage[], duration?: number); + } + + /** + * Represents the severity of test messages. + */ + export enum TestMessageSeverity { + Error = 0, + Warning = 1, + Information = 2, + Hint = 3 + } + + /** + * Message associated with the test state. Can be linked to a specific + * source range -- useful for assertion failures, for example. + */ + export interface TestMessage { + /** + * Human-readable message text to display. + */ + message: string | MarkdownString; + + /** + * Message severity. Defaults to "Error", if not provided. + */ + severity?: TestMessageSeverity; + + /** + * Expected test output. If given with `actual`, a diff view will be shown. + */ + expectedOutput?: string; + + /** + * Actual test output. If given with `actual`, a diff view will be shown. + */ + actualOutput?: string; + + /** + * Associated file location. + */ + location?: Location; + } //#endregion - //#region https://github.com/microsoft/vscode/issues/108929 FoldingRangeProvider.onDidChangeFoldingRanges @aeschli - export interface FoldingRangeProvider2 extends FoldingRangeProvider { + //#region Statusbar Item Background Color (https://github.com/microsoft/vscode/issues/110214) + + /** + * A status bar item is a status bar contribution that can + * show text and icons and run a command on click. + */ + export interface StatusBarItem { /** - * An optional event to signal that the folding ranges from this provider have changed. + * The background color for this entry. + * + * Note: only `new ThemeColor('statusBarItem.errorBackground')` is + * supported for now. More background colors may be supported in the + * future. + * + * Note: when a background color is set, the statusbar may override + * the `color` choice to ensure the entry is readable in all themes. */ - onDidChangeFoldingRanges?: Event; - + backgroundColor: ThemeColor | undefined; } + //#endregion } diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index a4df8523..3b4c8a66 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -64,6 +64,7 @@ import './mainThreadLabelService'; import './mainThreadTunnelService'; import './mainThreadAuthentication'; import './mainThreadTimeline'; +import './mainThreadTesting'; import 'vs/workbench/api/common/apiCommands'; export class ExtensionPoints implements IWorkbenchContribution { diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 02240e28..c3965040 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -10,11 +10,10 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService'; import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import Severity from 'vs/base/common/severity'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { fromNow } from 'vs/base/common/date'; import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -23,7 +22,7 @@ import { IEncryptionService } from 'vs/workbench/services/encryption/common/encr import { IProductService } from 'vs/platform/product/common/productService'; import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; -const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser']; +const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser', 'github.codespaces']; interface IAccountUsage { extensionId: string; @@ -70,7 +69,7 @@ function addAccountUsage(storageService: IStorageService, providerId: string, ac }); } - storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL); + storageService.store(accountKey, JSON.stringify(usages), StorageScope.GLOBAL, StorageTarget.MACHINE); } export class MainThreadAuthenticationProvider extends Disposable { @@ -83,7 +82,6 @@ export class MainThreadAuthenticationProvider extends Disposable { public readonly label: string, public readonly supportsMultipleAccounts: boolean, private readonly notificationService: INotificationService, - private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, private readonly storageService: IStorageService, private readonly quickInputService: IQuickInputService, private readonly dialogService: IDialogService @@ -100,9 +98,15 @@ export class MainThreadAuthenticationProvider extends Disposable { } public manageTrustedExtensions(accountName: string) { + const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName); + + if (!allowedExtensions.length) { + this.dialogService.show(Severity.Info, nls.localize('noTrustedExtensions', "This account has not been used by any extensions."), []); + return; + } + const quickPick = this.quickInputService.createQuickPick<{ label: string, description: string, extension: AllowedExtension }>(); quickPick.canSelectMany = true; - const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName); const usages = readAccountUsages(this.storageService, this.id, accountName); const items = allowedExtensions.map(extension => { const usage = usages.find(usage => extension.id === usage.extensionId); @@ -122,7 +126,7 @@ export class MainThreadAuthenticationProvider extends Disposable { quickPick.onDidAccept(() => { const updatedAllowedList = quickPick.selectedItems.map(item => item.extension); - this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL); + this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL, StorageTarget.USER); quickPick.dispose(); }); @@ -153,8 +157,6 @@ export class MainThreadAuthenticationProvider extends Disposable { } else { this._accounts.set(session.account.label, [session.id]); } - - this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.label}`, version: 1 }); } async signOut(accountName: string): Promise { @@ -171,6 +173,7 @@ export class MainThreadAuthenticationProvider extends Disposable { if (result.confirmed) { sessionsForAccount?.forEach(sessionId => this.logout(sessionId)); removeAccountUsage(this.storageService, this.id, accountName); + this.storageService.remove(`${this.id}-${accountName}`, StorageScope.GLOBAL); } } @@ -220,7 +223,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu @IDialogService private readonly dialogService: IDialogService, @IStorageService private readonly storageService: IStorageService, @INotificationService private readonly notificationService: INotificationService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IExtensionService private readonly extensionService: IExtensionService, @@ -259,7 +261,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu } async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise { - const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService); + const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageService, this.quickInputService, this.dialogService); await provider.initialize(); this.authenticationService.registerAuthenticationProvider(id, provider); } @@ -383,10 +385,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu const allowList = readAllowedExtensions(this.storageService, providerId, accountName); if (!allowList.find(allowed => allowed.id === extensionId)) { allowList.push({ id: extensionId, name: extensionName }); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); + this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } - this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL); + this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL, StorageTarget.MACHINE); quickPick.dispose(); resolve(session); @@ -435,7 +437,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu if (allow) { addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName); allowList.push({ id: extensionId, name: extensionName }); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); + this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } return allow; @@ -458,10 +460,11 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu const allowList = readAllowedExtensions(this.storageService, providerId, accountName); if (!allowList.find(allowed => allowed.id === extensionId)) { allowList.push({ id: extensionId, name: extensionName }); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); + this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } - this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL); + this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); + addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName); } private getFullKey(extensionId: string): string { diff --git a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts index b6ee8cac..70fb045d 100644 --- a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts +++ b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts @@ -37,8 +37,14 @@ export class MainThreadBulkEdits implements MainThreadBulkEditsShape { dispose(): void { } - $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { + $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise { const edits = reviveWorkspaceEditDto2(dto); - return this._bulkEditService.apply(edits).then(() => true, _err => false); + return this._bulkEditService.apply(edits, { + // having a undoRedoGroupId means that this is a nested workspace edit, + // e.g one from a onWill-handler and for now we need to forcefully suppress + // refactor previewing, see: https://github.com/microsoft/vscode/issues/111873#issuecomment-738739852 + undoRedoGroupId, + suppressPreview: typeof undoRedoGroupId === 'number' ? true : undefined + }).then(() => true, _err => false); } } diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 8ea57336..1256682d 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -14,7 +14,7 @@ import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { isEqual } from 'vs/base/common/resources'; -// todo@joh move these things back into something like contrib/insets +// todo@jrieken move these things back into something like contrib/insets class EditorWebviewZone implements IViewZone { readonly domNode: HTMLElement; @@ -73,7 +73,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise { let editor: IActiveCodeEditor | undefined; - id = id.substr(0, id.indexOf(',')); //todo@joh HACK + id = id.substr(0, id.indexOf(',')); //todo@jrieken HACK for (const candidate of this._editorService.listCodeEditors()) { if (candidate.getId() === id && candidate.hasModel() && isEqual(candidate.getModel().uri, URI.revive(uri))) { diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts index ff817619..38f6ac66 100644 --- a/src/vs/workbench/api/browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCommands.ts @@ -34,23 +34,23 @@ export class MainThreadCommands implements MainThreadCommandsShape { this._generateCommandsDocumentationRegistration.dispose(); } - private _generateCommandsDocumentation(): Promise { - return this._proxy.$getContributedCommandHandlerDescriptions().then(result => { - // add local commands - const commands = CommandsRegistry.getCommands(); - for (const [id, command] of commands) { - if (command.description) { - result[id] = command.description; - } - } + private async _generateCommandsDocumentation(): Promise { + const result = await this._proxy.$getContributedCommandHandlerDescriptions(); - // print all as markdown - const all: string[] = []; - for (let id in result) { - all.push('`' + id + '` - ' + _generateMarkdown(result[id])); + // add local commands + const commands = CommandsRegistry.getCommands(); + for (const [id, command] of commands) { + if (command.description) { + result[id] = command.description; } - console.log(all.join('\n')); - }); + } + + // print all as markdown + const all: string[] = []; + for (let id in result) { + all.push('`' + id + '` - ' + _generateMarkdown(result[id])); + } + console.log(all.join('\n')); } $registerCommand(id: string): void { diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index b6b32841..8dba884c 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -21,6 +21,8 @@ import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, V import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { localize } from 'vs/nls'; export class MainThreadCommentThread implements modes.CommentThread { @@ -351,6 +353,9 @@ export class MainThreadCommentController { } } + +const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscussion, localize('commentsViewIcon', 'View icon of the comments view.')); + @extHostNamedCustomer(MainContext.MainThreadComments) export class MainThreadComments extends Disposable implements MainThreadCommentsShape { private readonly _proxy: ExtHostCommentsShape; @@ -472,7 +477,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: COMMENTS_VIEW_TITLE, hideIfEmpty: true, - icon: Codicon.commentDiscussion.classNames, + icon: commentsViewIcon, order: 10, }, ViewContainerLocation.Panel); @@ -482,7 +487,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments canToggleVisibility: false, ctorDescriptor: new SyncDescriptor(CommentsPanel), canMoveView: true, - containerIcon: Codicon.commentDiscussion.classNames, + containerIcon: commentsViewIcon, focusCommand: { id: 'workbench.action.focusCommentsPanel' } diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 410b6b06..bcf31a1a 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -23,8 +23,7 @@ import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/comm import { MainThreadWebviewPanels } from 'vs/workbench/api/browser/mainThreadWebviewPanels'; import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; +import { editorGroupToViewColumn, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; @@ -604,6 +603,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod this._proxy.$backup(this._editorResource.toJSON(), this.viewType, token))); this._hotExitState = pendingState; + token.onCancellationRequested(() => { + pendingState.operation.cancel(); + }); + try { const backupId = await pendingState.operation; // Make sure state has not changed in the meantime diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 54fd5da6..f1906f16 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -82,7 +82,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb // RPC methods (MainThreadDebugServiceShape) public $registerDebugTypes(debugTypes: string[]) { - this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterFactory(debugTypes, this)); + this._toDispose.add(this.debugService.getAdapterManager().registerDebugAdapterFactory(debugTypes, this)); } public $startBreakpointEvents(): void { @@ -155,7 +155,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return Promise.resolve(); } - public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise { + public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, handle: number): Promise { const provider = { type: debugType, @@ -176,12 +176,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return this._proxy.$resolveDebugConfigurationWithSubstitutedVariables(handle, folder, config, token); }; } - if (hasProvideDebugAdapter) { - console.info('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); - provider.debugAdapterExecutable = (folder) => { - return this._proxy.$legacyDebugAdapterExecutable(handle, folder); - }; - } this._debugConfigurationProviders.set(handle, provider); this._toDispose.add(this.debugService.getConfigurationManager().registerDebugConfigurationProvider(provider)); @@ -205,7 +199,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } }; this._debugAdapterDescriptorFactories.set(handle, provider); - this._toDispose.add(this.debugService.getConfigurationManager().registerDebugAdapterDescriptorFactory(provider)); + this._toDispose.add(this.debugService.getAdapterManager().registerDebugAdapterDescriptorFactory(provider)); return Promise.resolve(undefined); } @@ -214,7 +208,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb const provider = this._debugAdapterDescriptorFactories.get(handle); if (provider) { this._debugAdapterDescriptorFactories.delete(handle); - this.debugService.getConfigurationManager().unregisterDebugAdapterDescriptorFactory(provider); + this.debugService.getAdapterManager().unregisterDebugAdapterDescriptorFactory(provider); } } diff --git a/src/vs/workbench/api/browser/mainThreadDialogs.ts b/src/vs/workbench/api/browser/mainThreadDialogs.ts index ff3b66e9..afa4240f 100644 --- a/src/vs/workbench/api/browser/mainThreadDialogs.ts +++ b/src/vs/workbench/api/browser/mainThreadDialogs.ts @@ -23,12 +23,20 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { // } - $showOpenDialog(options?: MainThreadDialogOpenOptions): Promise { - return Promise.resolve(this._fileDialogService.showOpenDialog(MainThreadDialogs._convertOpenOptions(options))); + async $showOpenDialog(options?: MainThreadDialogOpenOptions): Promise { + const convertedOptions = MainThreadDialogs._convertOpenOptions(options); + if (!convertedOptions.defaultUri) { + convertedOptions.defaultUri = await this._fileDialogService.defaultFilePath(); + } + return Promise.resolve(this._fileDialogService.showOpenDialog(convertedOptions)); } - $showSaveDialog(options?: MainThreadDialogSaveOptions): Promise { - return Promise.resolve(this._fileDialogService.showSaveDialog(MainThreadDialogs._convertSaveOptions(options))); + async $showSaveDialog(options?: MainThreadDialogSaveOptions): Promise { + const convertedOptions = MainThreadDialogs._convertSaveOptions(options); + if (!convertedOptions.defaultUri) { + convertedOptions.defaultUri = await this._fileDialogService.defaultFilePath(); + } + return Promise.resolve(this._fileDialogService.showSaveDialog(convertedOptions)); } private static _convertOpenOptions(options?: MainThreadDialogOpenOptions): IOpenDialogOptions { @@ -38,7 +46,8 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { canSelectFolders: options?.canSelectFolders, canSelectMany: options?.canSelectMany, defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined, - title: options?.title || undefined + title: options?.title || undefined, + availableFileSystems: [] }; if (options?.filters) { result.filters = []; diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index c92a58c2..24252121 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -19,9 +19,8 @@ import { MainThreadDocuments } from 'vs/workbench/api/browser/mainThreadDocument import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor'; import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditors'; import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IExtHostContext, IModelAddedData, ITextEditorAddData, MainContext } from 'vs/workbench/api/common/extHost.protocol'; -import { EditorViewColumn, editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; -import { IEditorPane } from 'vs/workbench/common/editor'; +import { editorGroupToViewColumn, EditorGroupColumn, IEditorPane } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -31,41 +30,8 @@ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/commo import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { diffSets, diffMaps } from 'vs/base/common/collections'; -namespace delta { - - export function ofSets(before: Set, after: Set): { removed: T[], added: T[] } { - const removed: T[] = []; - const added: T[] = []; - for (let element of before) { - if (!after.has(element)) { - removed.push(element); - } - } - for (let element of after) { - if (!before.has(element)) { - added.push(element); - } - } - return { removed, added }; - } - - export function ofMaps(before: Map, after: Map): { removed: V[], added: V[] } { - const removed: V[] = []; - const added: V[] = []; - for (let [index, value] of before) { - if (!after.has(index)) { - removed.push(value); - } - } - for (let [index, value] of after) { - if (!before.has(index)) { - added.push(value); - } - } - return { removed, added }; - } -} class TextEditorSnapshot { @@ -118,8 +84,8 @@ class DocumentAndEditorState { undefined, after.activeEditor ); } - const documentDelta = delta.ofSets(before.documents, after.documents); - const editorDelta = delta.ofMaps(before.textEditors, after.textEditors); + const documentDelta = diffSets(before.documents, after.documents); + const editorDelta = diffMaps(before.textEditors, after.textEditors); const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined; const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined; @@ -443,7 +409,7 @@ export class MainThreadDocumentsAndEditors { }; } - private _findEditorPosition(editor: MainThreadTextEditor): EditorViewColumn | undefined { + private _findEditorPosition(editor: MainThreadTextEditor): EditorGroupColumn | undefined { for (const editorPane of this._editorService.visibleEditorPanes) { if (editor.matches(editorPane)) { return editorGroupToViewColumn(this._editorGroupService, editorPane.group); diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 738493d3..1723f632 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -14,18 +14,14 @@ import { ISelection } from 'vs/editor/common/core/selection'; import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor'; +import { ITextEditorOptions, IResourceEditorInput, EditorActivation } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors'; import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor'; import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; -import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; +import { editorGroupToViewColumn, EditorGroupColumn, viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { revive } from 'vs/base/common/marshalling'; @@ -161,7 +157,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { return this._documentsAndEditors.findTextEditorIdFor(editor); } - async $tryShowEditor(id: string, position?: EditorViewColumn): Promise { + async $tryShowEditor(id: string, position?: EditorGroupColumn): Promise { const mainThreadEditor = this._documentsAndEditors.getEditor(id); if (mainThreadEditor) { const model = mainThreadEditor.getModel(); @@ -297,59 +293,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // --- commands -CommandsRegistry.registerCommand('_workbench.open', async function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorViewColumn, string?]) { - const editorService = accessor.get(IEditorService); - const editorGroupService = accessor.get(IEditorGroupsService); - const openerService = accessor.get(IOpenerService); - - const [resource, options, position, label] = args; - - if (options || typeof position === 'number') { - // use editor options or editor view column as a hint to use the editor service for opening - await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, position)); - return; - } - - if (resource && resource.scheme === 'command') { - // do not allow to execute commands from here - return; - - } - // finally, delegate to opener service - await openerService.open(resource); -}); - -CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => { - const editorService = accessor.get(IEditorService); - const editorGroupsService = accessor.get(IEditorGroupsService); - const configurationService = accessor.get(IConfigurationService); - const quickInputService = accessor.get(IQuickInputService); - - const [resource, id, options, position] = args; - - const group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, position)) ?? editorGroupsService.activeGroup; - const textOptions: ITextEditorOptions = options ? { ...options, override: false } : { override: false }; - - const input = editorService.createEditorInput({ resource }); - return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService); -}); - - -CommandsRegistry.registerCommand('_workbench.diff', async function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorViewColumn]) { - const editorService = accessor.get(IEditorService); - const editorGroupService = accessor.get(IEditorGroupsService); - - let [leftResource, rightResource, label, description, options, position] = args; - - if (!options || typeof options !== 'object') { - options = { - preserveFocus: false - }; - } - - await editorService.openEditor({ leftResource, rightResource, label, description, options }, viewColumnToEditorGroup(editorGroupService, position)); -}); - CommandsRegistry.registerCommand('_workbench.revertAllDirty', async function (accessor: ServicesAccessor) { const environmentService = accessor.get(IEnvironmentService); if (!environmentService.extensionTestsLocationURI) { diff --git a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts index ca2c751e..81905c6b 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts @@ -54,11 +54,13 @@ export class MainThreadFileSystemEventService { // BEFORE file operation - workingCopyFileService.addFileOperationParticipant({ - participate: (files, operation, progress, timeout, token) => { - return proxy.$onWillRunFileOperation(operation, files, timeout, token); + this._listener.add(workingCopyFileService.addFileOperationParticipant({ + participate: async (files, operation, undoRedoGroupId, isUndoing, _progress, timeout, token) => { + if (!isUndoing) { + return proxy.$onWillRunFileOperation(operation, files, undoRedoGroupId, timeout, token); + } } - }); + })); // AFTER file operation this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => proxy.$onDidRunFileOperation(e.operation, e.files))); @@ -75,7 +77,7 @@ Registry.as(Extensions.Configuration).registerConfigurat properties: { 'files.participants.timeout': { type: 'number', - default: 5000, + default: 60000, markdownDescription: localize('files.participants.timeout', "Timeout in milliseconds after which file participants for create, rename, and delete are cancelled. Use `0` to disable participants."), } } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 64a3f19a..ddea8e0e 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -165,16 +165,15 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { const provider = { - provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise => { - return this._proxy.$provideCodeLenses(handle, model.uri, token).then(listDto => { - if (!listDto) { - return undefined; - } - return { - lenses: listDto.lenses, - dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId) - }; - }); + provideCodeLenses: async (model: ITextModel, token: CancellationToken): Promise => { + const listDto = await this._proxy.$provideCodeLenses(handle, model.uri, token); + if (!listDto) { + return undefined; + } + return { + lenses: listDto.lenses, + dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId) + }; }, resolveCodeLens: (_model: ITextModel, codeLens: modes.CodeLens, token: CancellationToken): Promise => { return this._proxy.$resolveCodeLens(handle, codeLens, token); @@ -261,14 +260,12 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } - // --- on type rename + // --- linked editing - $registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], wordPattern?: IRegExpDto): void { - const revivedWordPattern = wordPattern ? MainThreadLanguageFeatures._reviveRegExp(wordPattern) : undefined; - this._registrations.set(handle, modes.OnTypeRenameProviderRegistry.register(selector, { - wordPattern: revivedWordPattern, - provideOnTypeRenameRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> => { - const res = await this._proxy.$provideOnTypeRenameRanges(handle, model.uri, position, token); + $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void { + this._registrations.set(handle, modes.LinkedEditingRangeProviderRegistry.register(selector, { + provideLinkedEditingRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { + const res = await this._proxy.$provideLinkedEditingRanges(handle, model.uri, position, token); if (res) { return { ranges: res.ranges, diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 93215c29..cbf34422 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -5,6 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { diffMaps, diffSets } from 'vs/base/common/collections'; import { Emitter } from 'vs/base/common/event'; import { IRelativePattern } from 'vs/base/common/glob'; import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; @@ -14,8 +15,11 @@ import { IExtUri } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { ILogService } from 'vs/platform/log/common/log'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; @@ -23,44 +27,14 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookEditorModel, INotebookExclusiveDocumentFilter, NotebookCellOutputsSplice, NotebookCellsChangeType, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; +import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { - static ofSets(before: Set, after: Set): { removed: T[], added: T[] } { - const removed: T[] = []; - const added: T[] = []; - before.forEach(element => { - if (!after.has(element)) { - removed.push(element); - } - }); - after.forEach(element => { - if (!before.has(element)) { - added.push(element); - } - }); - return { removed, added }; - } - - static ofMaps(before: Map, after: Map): { removed: V[], added: V[] } { - const removed: V[] = []; - const added: V[] = []; - before.forEach((value, index) => { - if (!after.has(index)) { - removed.push(value); - } - }); - after.forEach((value, index) => { - if (!before.has(index)) { - added.push(value); - } - }); - return { removed, added }; - } - static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta { if (!before) { const apiEditors = []; @@ -75,8 +49,8 @@ class DocumentAndEditorState { visibleEditors: [...after.visibleEditors].map(editor => editor[0]) }; } - const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents); - const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors); + const documentDelta = diffSets(before.documents, after.documents); + const editorDelta = diffMaps(before.textEditors, after.textEditors); const addedAPIEditors = editorDelta.added.map(add => ({ id: add.getId(), documentUri: add.uri!, @@ -89,7 +63,7 @@ class DocumentAndEditorState { // const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined; const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined; - const visibleEditorDelta = DocumentAndEditorState.ofMaps(before.visibleEditors, after.visibleEditors); + const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors); return { addedDocuments: documentDelta.added.map((e: NotebookTextModel): INotebookModelAddedData => { @@ -152,7 +126,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo @INotebookService private _notebookService: INotebookService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorService private readonly editorService: IEditorService, + @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService, + @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, + @IQuickInputService private readonly quickInputService: IQuickInputService, @ILogService private readonly logService: ILogService, @INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService, @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @@ -171,7 +148,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (!textModel) { return false; } - this._notebookService.transformEditsOutputs(textModel, cellEdits); return textModel.applyEdits(modelVersionId, cellEdits, true, undefined, () => undefined, undefined); } @@ -413,9 +389,9 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const editors = new Map(); this._notebookService.listNotebookEditors().forEach(editor => { - if (editor.hasModel()) { + if (editor.textModel) { editors.set(editor.getId(), editor); - documentEditorsMap.set(editor.textModel!.uri.toString(), editor); + documentEditorsMap.set(editor.textModel.uri.toString(), editor); } }); @@ -434,7 +410,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo documents.add(document); }); - if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.hasModel()) { + if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.textModel) { activeEditor = focusedNotebookEditor.getId(); } @@ -479,8 +455,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const edits: ICellEditOperation[] = [ { editType: CellEditType.Replace, index: 0, count: mainthreadTextModel.cells.length, cells: data.cells } ]; - - this._notebookService.transformEditsOutputs(mainthreadTextModel, edits); await new Promise(resolve => { DOM.scheduleAtNextAnimationFrame(() => { const ret = mainthreadTextModel!.applyEdits(mainthreadTextModel!.versionId, edits, true, undefined, () => undefined, undefined); @@ -606,7 +580,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return; } - this._notebookService.transformSpliceOutputs(textModel, splices); const cell = textModel.cells.find(cell => cell.handle === cellHandle); if (!cell) { @@ -662,8 +635,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id); if (editor && editor.isNotebookEditor) { const notebookEditor = editor as INotebookEditor; + if (!notebookEditor.hasModel()) { + return; + } const viewModel = notebookEditor.viewModel; - const cell = viewModel?.viewCells[range.start]; + const cell = viewModel.viewCells[range.start]; if (!cell) { return; } @@ -726,6 +702,51 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return uri; } + + async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise { + const editorOptions: ITextEditorOptions = { + preserveFocus: options.preserveFocus, + pinned: options.pinned, + // selection: options.selection, + // preserve pre 1.38 behaviour to not make group active when preserveFocus: true + // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 + activation: options.preserveFocus ? EditorActivation.RESTORE : undefined, + override: false, + }; + + const columnArg = viewColumnToEditorGroup(this._editorGroupService, options.position); + + let group: IEditorGroup | undefined = undefined; + + if (columnArg === SIDE_GROUP) { + const direction = preferredSideBySideGroupDirection(this.configurationService); + + let neighbourGroup = this.editorGroupsService.findGroup({ direction }); + if (!neighbourGroup) { + neighbourGroup = this.editorGroupsService.addGroup(this.editorGroupsService.activeGroup, direction); + } + group = neighbourGroup; + } else { + group = this.editorGroupsService.getGroup(viewColumnToEditorGroup(this.editorGroupsService, columnArg)) ?? this.editorGroupsService.activeGroup; + } + + const input = this.editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions }); + + // TODO: handle options.selection + const editorPane = await openEditorWith(input, viewType, options, group, this.editorService, this.configurationService, this.quickInputService); + const notebookEditor = (editorPane as unknown as { isNotebookEditor?: boolean })?.isNotebookEditor ? (editorPane!.getControl() as INotebookEditor) : undefined; + + if (notebookEditor) { + if (notebookEditor.viewModel && options.selection && notebookEditor.viewModel.viewCells[options.selection.start]) { + const focusedCell = notebookEditor.viewModel.viewCells[options.selection.start]; + notebookEditor.revealInCenterIfOutsideViewport(focusedCell); + notebookEditor.selectElement(focusedCell); + } + return notebookEditor.getId(); + } else { + throw new Error(`Notebook Editor creation failure for documenet ${resource}`); + } + } } diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 32dbe804..8f1e25eb 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -68,7 +68,8 @@ class MainThreadSCMResource implements ISCMResource { readonly sourceUri: URI, readonly resourceGroup: ISCMResourceGroup, readonly decorations: ISCMResourceDecorations, - readonly contextValue: string | undefined + readonly contextValue: string | undefined, + readonly command: Command | undefined ) { } open(preserveFocus: boolean): Promise { @@ -201,7 +202,7 @@ class MainThreadSCMProvider implements ISCMProvider { for (const [start, deleteCount, rawResources] of groupSlices) { const resources = rawResources.map(rawResource => { - const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] = rawResource; + const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command] = rawResource; const icon = icons[0]; const iconDark = icons[1] || icon; const decorations = { @@ -220,7 +221,8 @@ class MainThreadSCMProvider implements ISCMProvider { URI.revive(sourceUri), group, decorations, - contextValue || undefined + contextValue || undefined, + command ); }); diff --git a/src/vs/workbench/api/browser/mainThreadStatusBar.ts b/src/vs/workbench/api/browser/mainThreadStatusBar.ts index d50bc8f0..a847df3b 100644 --- a/src/vs/workbench/api/browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/browser/mainThreadStatusBar.ts @@ -27,7 +27,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { this.entries.clear(); } - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void { + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void { // if there are icons in the text use the tooltip for the aria label let ariaLabel: string; let role: string | undefined = undefined; @@ -37,7 +37,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { } else { ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : ''; } - const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel, role }; + const entry: IStatusbarEntry = { text, tooltip, command, color, backgroundColor, ariaLabel, role }; if (typeof priority === 'undefined') { priority = 0; diff --git a/src/vs/workbench/api/browser/mainThreadStorage.ts b/src/vs/workbench/api/browser/mainThreadStorage.ts index 57abf0e8..5cb658e9 100644 --- a/src/vs/workbench/api/browser/mainThreadStorage.ts +++ b/src/vs/workbench/api/browser/mainThreadStorage.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { MainThreadStorageShape, MainContext, IExtHostContext, ExtHostStorageShape, ExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IExtensionIdWithVersion, IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionIdWithVersion, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; @extHostNamedCustomer(MainContext.MainThreadStorage) export class MainThreadStorage implements MainThreadStorageShape { private readonly _storageService: IStorageService; - private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService; + private readonly _extensionsStorageSyncService: IExtensionsStorageSyncService; private readonly _proxy: ExtHostStorageShape; private readonly _storageListener: IDisposable; private readonly _sharedStorageKeysToWatch: Map = new Map(); @@ -21,13 +21,13 @@ export class MainThreadStorage implements MainThreadStorageShape { constructor( extHostContext: IExtHostContext, @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + @IExtensionsStorageSyncService extensionsStorageSyncService: IExtensionsStorageSyncService, ) { this._storageService = storageService; - this._storageKeysSyncRegistryService = storageKeysSyncRegistryService; + this._extensionsStorageSyncService = extensionsStorageSyncService; this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStorage); - this._storageListener = this._storageService.onDidChangeStorage(e => { + this._storageListener = this._storageService.onDidChangeValue(e => { const shared = e.scope === StorageScope.GLOBAL; if (shared && this._sharedStorageKeysToWatch.has(e.key)) { try { @@ -66,7 +66,8 @@ export class MainThreadStorage implements MainThreadStorageShape { let jsonValue: string; try { jsonValue = JSON.stringify(value); - this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE); + // Extension state is synced separately through extensions + this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE, StorageTarget.MACHINE); } catch (err) { return Promise.reject(err); } @@ -74,6 +75,6 @@ export class MainThreadStorage implements MainThreadStorageShape { } $registerExtensionStorageKeysToSync(extension: IExtensionIdWithVersion, keys: string[]): void { - this._storageKeysSyncRegistryService.registerExtensionStorageKeys(extension, keys); + this._extensionsStorageSyncService.setKeysForSync(extension, keys); } } diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 391cd0ce..2f4cb073 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -53,7 +53,7 @@ namespace TaskProcessStartedDTO { } namespace TaskProcessEndedDTO { - export function from(value: TaskExecution, exitCode: number): TaskProcessEndedDTO { + export function from(value: TaskExecution, exitCode: number | undefined): TaskProcessEndedDTO { return { id: value.id, exitCode @@ -429,7 +429,7 @@ export class MainThreadTask implements MainThreadTaskShape { } else if (event.kind === TaskEventKind.ProcessStarted) { this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId!)); } else if (event.kind === TaskEventKind.ProcessEnded) { - this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode!)); + this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode)); } else if (event.kind === TaskEventKind.End) { this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution())); } diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts new file mode 100644 index 00000000..52185f8d --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { getTestSubscriptionKey, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol'; +import { URI, UriComponents } from 'vs/base/common/uri'; + +@extHostNamedCustomer(MainContext.MainThreadTesting) +export class MainThreadTesting extends Disposable implements MainThreadTestingShape { + private readonly proxy: ExtHostTestingShape; + private readonly testSubscriptions = new Map(); + + constructor( + extHostContext: IExtHostContext, + @ITestService private readonly testService: ITestService, + ) { + super(); + this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostTesting); + this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri))); + this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri))); + } + + /** + * @inheritdoc + */ + public $registerTestProvider(id: string) { + this.testService.registerTestController(id, { + runTests: req => this.proxy.$runTestsForProvider(req), + }); + } + + /** + * @inheritdoc + */ + public $unregisterTestProvider(id: string) { + this.testService.unregisterTestController(id); + } + + /** + * @inheritdoc + */ + $subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void { + const uri = URI.revive(uriComponents); + const disposable = this.testService.subscribeToDiffs(resource, uri, + diff => this.proxy.$acceptDiff(resource, uriComponents, diff)); + this.testSubscriptions.set(getTestSubscriptionKey(resource, uri), disposable); + } + + /** + * @inheritdoc + */ + public $unsubscribeFromDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void { + const key = getTestSubscriptionKey(resource, URI.revive(uriComponents)); + this.testSubscriptions.get(key)?.dispose(); + this.testSubscriptions.delete(key); + } + + /** + * @inheritdoc + */ + public $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void { + this.testService.publishDiff(resource, URI.revive(uri), diff); + } + + public $runTests(req: RunTestsRequest): Promise { + return this.testService.runTests(req); + } + + public dispose() { + // no-op + } +} diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 7d0d71cc..8e32acd7 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -52,14 +52,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie }); } - $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise { - this.logService.trace('MainThreadTreeViews#$reveal', treeViewId, item, parentChain, options); + $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise { + this.logService.trace('MainThreadTreeViews#$reveal', treeViewId, itemInfo?.item, itemInfo?.parentChain, options); return this.viewsService.openView(treeViewId, options.focus) .then(() => { const viewer = this.getTreeView(treeViewId); - if (viewer) { - return this.reveal(viewer, this._dataProviders.get(treeViewId)!, item, parentChain, options); + if (viewer && itemInfo) { + return this.reveal(viewer, this._dataProviders.get(treeViewId)!, itemInfo.item, itemInfo.parentChain, options); } return undefined; }); @@ -235,10 +235,14 @@ class TreeViewDataProvider implements ITreeViewDataProvider { private updateTreeItem(current: ITreeItem, treeItem: ITreeItem): void { treeItem.children = treeItem.children ? treeItem.children : undefined; if (current) { - const properties = distinct([...Object.keys(current), ...Object.keys(treeItem)]); + const properties = distinct([...Object.keys(current instanceof ResolvableTreeItem ? current.asTreeItem() : current), + ...Object.keys(treeItem)]); for (const property of properties) { (current)[property] = (treeItem)[property]; } + if (current instanceof ResolvableTreeItem) { + current.resetResolve(); + } } } } diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index 2030fe21..ac9b8356 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -6,8 +6,8 @@ import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { IRemoteExplorerService, MakeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { ITunnelProvider, ITunnelService, TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { IRemoteExplorerService, makeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -26,8 +26,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun this._register(tunnelService.onTunnelClosed(() => this._proxy.$onDidTunnelsChange())); } - async $openTunnel(tunnelOptions: TunnelOptions): Promise { - const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label); + async $openTunnel(tunnelOptions: TunnelOptions, source: string): Promise { + const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label, source); if (tunnel) { return TunnelDto.fromServiceTunnel(tunnel); } @@ -47,8 +47,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun }); } - async $registerCandidateFinder(): Promise { - this.remoteExplorerService.registerCandidateFinder(() => this._proxy.$findCandidatePorts()); + async $onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise { + this.remoteExplorerService.onFoundNewCandidates(candidates); } async $tunnelServiceReady(): Promise { @@ -57,19 +57,17 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun async $setTunnelProvider(): Promise { const tunnelProvider: ITunnelProvider = { - forwardPort: (tunnelOptions: TunnelOptions) => { - const forward = this._proxy.$forwardPort(tunnelOptions); + forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => { + const forward = this._proxy.$forwardPort(tunnelOptions, tunnelCreationOptions); if (forward) { return forward.then(tunnel => { return { tunnelRemotePort: tunnel.remoteAddress.port, tunnelRemoteHost: tunnel.remoteAddress.host, - localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : MakeAddress(tunnel.localAddress.host, tunnel.localAddress.port), + localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : makeAddress(tunnel.localAddress.host, tunnel.localAddress.port), tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined, - dispose: (silent: boolean) => { - if (!silent) { - this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }); - } + dispose: (silent?: boolean) => { + this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }, silent); } }; }); @@ -80,22 +78,6 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun this.tunnelService.setTunnelProvider(tunnelProvider); } - async $setCandidateFilter(): Promise { - this._register(this.remoteExplorerService.setCandidateFilter(async (candidates: { host: string, port: number, detail: string }[]): Promise<{ host: string, port: number, detail: string }[]> => { - const filters: boolean[] = await this._proxy.$filterCandidates(candidates); - const filteredCandidates: { host: string, port: number, detail: string }[] = []; - if (filters.length !== candidates.length) { - return candidates; - } - for (let i = 0; i < candidates.length; i++) { - if (filters[i]) { - filteredCandidates.push(candidates[i]); - } - } - return filteredCandidates; - })); - } - dispose(): void { } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 561e3c21..6428e6f4 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -9,8 +9,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; -import { IEditorInput } from 'vs/workbench/common/editor'; +import { editorGroupToViewColumn, EditorGroupColumn, IEditorInput, viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; @@ -150,7 +149,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc handle: extHostProtocol.WebviewHandle, viewType: string, title: string, - showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean; }, + showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; }, options: WebviewInputOptions ): void { const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index d6a1d810..6be78f4e 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -8,8 +8,8 @@ import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as resources from 'vs/base/common/resources'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation } from 'vs/workbench/common/views'; -import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; +import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor, ViewContainerLocation, testViewIcon } from 'vs/workbench/common/views'; +import { CustomTreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -30,9 +30,8 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { Codicon } from 'vs/base/common/codicons'; -import { CustomTreeView } from 'vs/workbench/contrib/views/browser/treeView'; import { WebviewViewPane } from 'vs/workbench/contrib/webviewView/browser/webviewViewPane'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -321,7 +320,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private registerTestViewContainer(): void { const title = localize('test', "Test"); - const icon = Codicon.beaker.classNames; + const icon = testViewIcon; this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined, ViewContainerLocation.Sidebar); } @@ -356,7 +355,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number { containers.forEach(descriptor => { - const icon = resources.joinPath(extension.extensionLocation, descriptor.icon); + const themeIcon = ThemeIcon.fromString(descriptor.icon); + + const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); const id = `workbench.view.extension.${descriptor.id}`; const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier, location); @@ -376,7 +377,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return order; } - private registerCustomViewContainer(id: string, title: string, icon: URI | string, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer { + private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer { let viewContainer = this.viewContainersRegistry.get(id); if (!viewContainer) { @@ -470,7 +471,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution { ? container.viewOrderDelegate.getOrder(item.group) : undefined; - const icon = item.icon ? resources.joinPath(extension.description.extensionLocation, item.icon) : undefined; + let icon: ThemeIcon | URI | undefined; + if (typeof item.icon === 'string') { + icon = ThemeIcon.fromString(item.icon) || resources.joinPath(extension.description.extensionLocation, item.icon); + } + const initialVisibility = this.convertInitialVisibility(item.visibility); const type = this.getViewType(item.type); diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index eaf1c90e..7670549c 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -3,20 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type * as vscode from 'vscode'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; -import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; -import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces'; -import { Schemas } from 'vs/base/common/network'; +import { IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWorkspacesService, IRecent } from 'vs/platform/workspaces/common/workspaces'; import { ILogService } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IViewDescriptorService, IViewsService, ViewVisibilityState } from 'vs/workbench/common/views'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -35,41 +31,6 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => }; } -interface IOpenFolderAPICommandOptions { - forceNewWindow?: boolean; - forceReuseWindow?: boolean; - noRecentEntry?: boolean; -} - -export class OpenFolderAPICommand { - public static readonly ID = 'vscode.openFolder'; - public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise; - public static execute(executor: ICommandsExecutor, uri?: URI, options?: IOpenFolderAPICommandOptions): Promise; - public static execute(executor: ICommandsExecutor, uri?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}): Promise { - if (typeof arg === 'boolean') { - arg = { forceNewWindow: arg }; - } - if (!uri) { - return executor.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg.forceNewWindow }); - } - const options: IOpenWindowOptions = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry }; - uri = URI.revive(uri); - const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri }; - return executor.executeCommand('_files.windowOpen', [uriToOpen], options); - } -} -CommandsRegistry.registerCommand({ - id: OpenFolderAPICommand.ID, - handler: adjustHandler(OpenFolderAPICommand.execute), - description: { - description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', - args: [ - { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI }, - { name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' } - ] - } -}); - interface INewWindowAPICommandOptions { reuseWindow?: boolean; remoteAuthority?: string; @@ -96,67 +57,6 @@ CommandsRegistry.registerCommand({ } }); -export class DiffAPICommand { - public static readonly ID = 'vscode.diff'; - public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise { - return executor.executeCommand('_workbench.diff', [ - left, right, - label, - undefined, - typeConverters.TextEditorOpenOptions.from(options), - options ? typeConverters.ViewColumn.from(options.viewColumn) : undefined - ]); - } -} -CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute)); - -export class OpenAPICommand { - public static readonly ID = 'vscode.open'; - public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions, label?: string): Promise { - let options: ITextEditorOptions | undefined; - let position: EditorViewColumn | undefined; - - if (columnOrOptions) { - if (typeof columnOrOptions === 'number') { - position = typeConverters.ViewColumn.from(columnOrOptions); - } else { - options = typeConverters.TextEditorOpenOptions.from(columnOrOptions); - position = typeConverters.ViewColumn.from(columnOrOptions.viewColumn); - } - } - - return executor.executeCommand('_workbench.open', [ - resource, - options, - position, - label - ]); - } -} -CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute)); - -export class OpenWithAPICommand { - public static readonly ID = 'vscode.openWith'; - public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions): Promise { - let options: ITextEditorOptions | undefined; - let position: EditorViewColumn | undefined; - - if (typeof columnOrOptions === 'number') { - position = typeConverters.ViewColumn.from(columnOrOptions); - } else if (typeof columnOrOptions !== 'undefined') { - options = typeConverters.TextEditorOpenOptions.from(columnOrOptions); - } - - return executor.executeCommand('_workbench.openWith', [ - resource, - viewType, - options, - position - ]); - } -} -CommandsRegistry.registerCommand(OpenWithAPICommand.ID, adjustHandler(OpenWithAPICommand.execute)); - CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) { const workspacesService = accessor.get(IWorkspacesService); return workspacesService.removeRecentlyOpened([uri]); @@ -219,38 +119,6 @@ CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function return workspacesService.getRecentlyOpened(); }); -export class SetEditorLayoutAPICommand { - public static readonly ID = 'vscode.setEditorLayout'; - public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise { - return executor.executeCommand('layoutEditorGroups', layout); - } -} -CommandsRegistry.registerCommand({ - id: SetEditorLayoutAPICommand.ID, - handler: adjustHandler(SetEditorLayoutAPICommand.execute), - description: { - description: 'Set Editor Layout', - args: [{ - name: 'args', - schema: { - 'type': 'object', - 'required': ['groups'], - 'properties': { - 'orientation': { - 'type': 'number', - 'default': 0, - 'enum': [0, 1] - }, - 'groups': { - '$ref': '#/definitions/editorGroupsSchema', // defined in keybindingService.ts ... - 'default': [{}, {}], - } - } - } - }] - } -}); - CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (accessor: ServicesAccessor, level: number) { const logService = accessor.get(ILogService); const environmentService = accessor.get(IEnvironmentService); @@ -260,6 +128,13 @@ CommandsRegistry.registerCommand('_extensionTests.setLogLevel', function (access } }); +CommandsRegistry.registerCommand('_workbench.openExternal', function (accessor: ServicesAccessor, uri: UriComponents, options: { allowTunneling?: boolean }) { + // TODO: discuss martin, ben where to put this + const openerService = accessor.get(IOpenerService); + openerService.open(URI.revive(uri), options); +}); + + CommandsRegistry.registerCommand('_extensionTests.getLogLevel', function (accessor: ServicesAccessor) { const logService = accessor.get(ILogService); @@ -304,3 +179,31 @@ CommandsRegistry.registerCommand({ args: [] } }); + + +// ----------------------------------------------------------------- +// The following commands are registered on the renderer but as API +// command. DO NOT USE this unless you have understood what this +// means +// ----------------------------------------------------------------- + + +class OpenAPICommand { + public static readonly ID = 'vscode.open'; + public static execute(executor: ICommandsExecutor, resource: URI): Promise { + + return executor.executeCommand('_workbench.open', resource); + } +} +CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute)); + +class DiffAPICommand { + public static readonly ID = 'vscode.diff'; + public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise { + return executor.executeCommand('_workbench.diff', [ + left, right, + label, + ]); + } +} +CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute)); diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 83c0ab29..aab5275f 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -28,6 +28,10 @@ const configurationEntrySchema: IJSONSchema = { properties: { description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'), type: 'object', + propertyNames: { + pattern: '\\S+', + patternErrorMessage: nls.localize('vscode.extension.contributes.configuration.property.empty', 'Property should not be empty.'), + }, additionalProperties: { anyOf: [ { $ref: 'http://json-schema.org/draft-07/schema#' }, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 284c6aff..fa880fcf 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -51,7 +51,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import * as vscode from 'vscode'; +import type * as vscode from 'vscode'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { originalFSPath } from 'vs/base/common/resources'; import { values } from 'vs/base/common/collections'; @@ -81,6 +81,7 @@ import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEdito import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; +import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -152,6 +153,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostWebviewPanels = rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, new ExtHostWebviewPanels(rpcProtocol, extHostWebviews, extHostWorkspace)); const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews, extHostWebviewPanels)); const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews)); + const extHostTesting = rpcProtocol.set(ExtHostContext.ExtHostTesting, new ExtHostTesting(rpcProtocol, extHostDocumentsAndEditors, extHostWorkspace)); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); @@ -201,40 +203,50 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I })(); const authentication: typeof vscode.authentication = { - registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable { - return extHostAuthentication.registerAuthenticationProvider(provider); - }, - get onDidChangeAuthenticationProviders(): Event { - return extHostAuthentication.onDidChangeAuthenticationProviders; - }, - getProviderIds(): Thenable> { - return extHostAuthentication.getProviderIds(); - }, - get providerIds(): string[] { - return extHostAuthentication.providerIds; - }, - get providers(): ReadonlyArray { - return extHostAuthentication.providers; - }, getSession(providerId: string, scopes: string[], options?: vscode.AuthenticationGetSessionOptions) { return extHostAuthentication.getSession(extension, providerId, scopes, options as any); }, - logout(providerId: string, sessionId: string): Thenable { - return extHostAuthentication.logout(providerId, sessionId); - }, get onDidChangeSessions(): Event { return extHostAuthentication.onDidChangeSessions; }, + registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostAuthentication.registerAuthenticationProvider(provider); + }, + get onDidChangeAuthenticationProviders(): Event { + checkProposedApiEnabled(extension); + return extHostAuthentication.onDidChangeAuthenticationProviders; + }, + getProviderIds(): Thenable> { + checkProposedApiEnabled(extension); + return extHostAuthentication.getProviderIds(); + }, + get providerIds(): string[] { + checkProposedApiEnabled(extension); + return extHostAuthentication.providerIds; + }, + get providers(): ReadonlyArray { + checkProposedApiEnabled(extension); + return extHostAuthentication.providers; + }, + logout(providerId: string, sessionId: string): Thenable { + checkProposedApiEnabled(extension); + return extHostAuthentication.logout(providerId, sessionId); + }, getPassword(key: string): Thenable { + checkProposedApiEnabled(extension); return extHostAuthentication.getPassword(extension, key); }, setPassword(key: string, value: string): Thenable { + checkProposedApiEnabled(extension); return extHostAuthentication.setPassword(extension, key, value); }, deletePassword(key: string): Thenable { + checkProposedApiEnabled(extension); return extHostAuthentication.deletePassword(extension, key); }, get onDidChangePassword(): Event { + checkProposedApiEnabled(extension); return extHostAuthentication.onDidChangePassword; } }; @@ -293,14 +305,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; }, get uriScheme() { return initData.environment.appUriScheme; }, - get logLevel() { - checkProposedApiEnabled(extension); - return typeConverters.LogLevel.to(extHostLogService.getLevel()); - }, - get onDidChangeLogLevel() { - checkProposedApiEnabled(extension); - return Event.map(extHostLogService.onDidChangeLogLevel, l => typeConverters.LogLevel.to(l)); - }, get clipboard(): vscode.Clipboard { return extHostClipboard; }, @@ -333,6 +337,25 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ? extHostTypes.ExtensionKind.Workspace : extHostTypes.ExtensionKind.UI; + const test: typeof vscode.test = { + registerTestProvider(provider) { + checkProposedApiEnabled(extension); + return extHostTesting.registerTestProvider(provider); + }, + createDocumentTestObserver(document) { + checkProposedApiEnabled(extension); + return extHostTesting.createTextDocumentTestObserver(document); + }, + createWorkspaceTestObserver(workspaceFolder) { + checkProposedApiEnabled(extension); + return extHostTesting.createWorkspaceTestObserver(workspaceFolder); + }, + runTests(provider) { + checkProposedApiEnabled(extension); + return extHostTesting.runTests(provider); + }, + }; + // namespace: extensions const extensions: typeof vscode.extensions = { getExtension(extensionId: string): Extension | undefined { @@ -397,9 +420,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, - registerOnTypeRenameProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, stopPattern?: RegExp): vscode.Disposable { - checkProposedApiEnabled(extension); - return extHostLanguageFeatures.registerOnTypeRenameProvider(extension, checkSelector(selector), provider, stopPattern); + registerLinkedEditingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable { + return extHostLanguageFeatures.registerLinkedEditingRangeProvider(extension, checkSelector(selector), provider); }, registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider); @@ -569,7 +591,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I priority = priority; } - return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation); + return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation, extension); }, setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): vscode.Disposable { return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); @@ -617,9 +639,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options); }, - registerDecorationProvider(provider: vscode.FileDecorationProvider) { - checkProposedApiEnabled(extension); - return extHostDecorations.registerDecorationProvider(provider, extension.identifier); + registerFileDecorationProvider(provider: vscode.FileDecorationProvider) { + return extHostDecorations.registerFileDecorationProvider(provider, extension.identifier); }, registerUriHandler(handler: vscode.UriHandler) { return extHostUrls.registerUriHandler(extension.identifier, handler); @@ -667,6 +688,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables); }, + showNotebookDocument(document, options?) { + checkProposedApiEnabled(extension); + return extHostNotebook.showNotebookDocument(document, options); + } }; // namespace: workspace @@ -835,7 +860,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, openTunnel: (forward: vscode.TunnelOptions) => { checkProposedApiEnabled(extension); - return extHostTunnelService.openTunnel(forward).then(value => { + return extHostTunnelService.openTunnel(extension, forward).then(value => { if (!value) { throw new Error('cannot open tunnel'); } @@ -870,14 +895,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } }; - const comment: typeof vscode.comments = { + // namespace: comments + const comments: typeof vscode.comments = { createCommentController(id: string, label: string) { return extHostComment.createCommentController(extension, id, label); } }; - const comments = comment; - // namespace: debug const debug: typeof vscode.debug = { get activeDebugSession() { @@ -1014,6 +1038,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider); }, createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType { + checkProposedApiEnabled(extension); return extHostNotebook.createNotebookEditorDecorationType(options); }, get activeNotebookEditor(): vscode.NotebookEditor | undefined { @@ -1067,40 +1092,46 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespaces authentication, commands, + comments, debug, env, extensions, languages, - scm, - comment, - comments, - tasks, notebook, + scm, + tasks, + test, window, workspace, // types Breakpoint: extHostTypes.Breakpoint, + CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, + CallHierarchyItem: extHostTypes.CallHierarchyItem, + CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, CancellationTokenSource: CancellationTokenSource, CodeAction: extHostTypes.CodeAction, CodeActionKind: extHostTypes.CodeActionKind, CodeActionTrigger: extHostTypes.CodeActionTrigger, CodeLens: extHostTypes.CodeLens, - CodeInset: extHostTypes.CodeInset, Color: extHostTypes.Color, ColorInformation: extHostTypes.ColorInformation, ColorPresentation: extHostTypes.ColorPresentation, - CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, + ColorThemeKind: extHostTypes.ColorThemeKind, CommentMode: extHostTypes.CommentMode, + CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, CompletionItemTag: extHostTypes.CompletionItemTag, CompletionList: extHostTypes.CompletionList, CompletionTriggerKind: extHostTypes.CompletionTriggerKind, ConfigurationTarget: extHostTypes.ConfigurationTarget, + CustomExecution: extHostTypes.CustomExecution, DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable, - DebugAdapterServer: extHostTypes.DebugAdapterServer, - DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer, DebugAdapterInlineImplementation: extHostTypes.DebugAdapterInlineImplementation, + DebugAdapterNamedPipeServer: extHostTypes.DebugAdapterNamedPipeServer, + DebugAdapterServer: extHostTypes.DebugAdapterServer, + DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind, + DebugConsoleMode: extHostTypes.DebugConsoleMode, DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, Diagnostic: extHostTypes.Diagnostic, DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation, @@ -1117,9 +1148,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, ExtensionMode: extHostTypes.ExtensionMode, - ExtensionRuntime: extHostTypes.ExtensionRuntime, - CustomExecution: extHostTypes.CustomExecution, FileChangeType: extHostTypes.FileChangeType, + FileDecoration: extHostTypes.FileDecoration, FileSystemError: extHostTypes.FileSystemError, FileType: files.FileType, FoldingRange: extHostTypes.FoldingRange, @@ -1128,7 +1158,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, Location: extHostTypes.Location, - LogLevel: extHostTypes.LogLevel, MarkdownString: extHostTypes.MarkdownString, OverviewRulerLane: OverviewRulerLane, ParameterInformation: extHostTypes.ParameterInformation, @@ -1138,23 +1167,20 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I QuickInputButtons: extHostTypes.QuickInputButtons, Range: extHostTypes.Range, RelativePattern: extHostTypes.RelativePattern, - ResolvedAuthority: extHostTypes.ResolvedAuthority, - RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError, - SemanticTokensLegend: extHostTypes.SemanticTokensLegend, - SemanticTokensBuilder: extHostTypes.SemanticTokensBuilder, - SemanticTokens: extHostTypes.SemanticTokens, - SemanticTokensEdits: extHostTypes.SemanticTokensEdits, - SemanticTokensEdit: extHostTypes.SemanticTokensEdit, Selection: extHostTypes.Selection, SelectionRange: extHostTypes.SelectionRange, + SemanticTokens: extHostTypes.SemanticTokens, + SemanticTokensBuilder: extHostTypes.SemanticTokensBuilder, + SemanticTokensEdit: extHostTypes.SemanticTokensEdit, + SemanticTokensEdits: extHostTypes.SemanticTokensEdits, + SemanticTokensLegend: extHostTypes.SemanticTokensLegend, ShellExecution: extHostTypes.ShellExecution, ShellQuoting: extHostTypes.ShellQuoting, - SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, SignatureHelp: extHostTypes.SignatureHelp, + SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, SignatureInformation: extHostTypes.SignatureInformation, SnippetString: extHostTypes.SnippetString, SourceBreakpoint: extHostTypes.SourceBreakpoint, - SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType, StandardTokenType: extHostTypes.StandardTokenType, StatusBarAlignment: extHostTypes.StatusBarAlignment, SymbolInformation: extHostTypes.SymbolInformation, @@ -1174,29 +1200,80 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ThemeColor: extHostTypes.ThemeColor, ThemeIcon: extHostTypes.ThemeIcon, TreeItem: extHostTypes.TreeItem, - TreeItem2: extHostTypes.TreeItem, TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, + UIKind: UIKind, Uri: URI, ViewColumn: extHostTypes.ViewColumn, WorkspaceEdit: extHostTypes.WorkspaceEdit, - // proposed - CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, - CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, - CallHierarchyItem: extHostTypes.CallHierarchyItem, - DebugConsoleMode: extHostTypes.DebugConsoleMode, - DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind, - FileDecoration: extHostTypes.FileDecoration, - UIKind: UIKind, - ColorThemeKind: extHostTypes.ColorThemeKind, - TimelineItem: extHostTypes.TimelineItem, - CellKind: extHostTypes.CellKind, - CellOutputKind: extHostTypes.CellOutputKind, - NotebookCellRunState: extHostTypes.NotebookCellRunState, - NotebookRunState: extHostTypes.NotebookRunState, - NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment, - NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType, - NotebookCellOutput: extHostTypes.NotebookCellOutput, - NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem, + // proposed api types + get RemoteAuthorityResolverError() { + // checkProposedApiEnabled(extension); + return extHostTypes.RemoteAuthorityResolverError; + }, + get ResolvedAuthority() { + // checkProposedApiEnabled(extension); + return extHostTypes.ResolvedAuthority; + }, + get SourceControlInputBoxValidationType() { + // checkProposedApiEnabled(extension); + return extHostTypes.SourceControlInputBoxValidationType; + }, + get ExtensionRuntime() { + // checkProposedApiEnabled(extension); + return extHostTypes.ExtensionRuntime; + }, + get TimelineItem() { + // checkProposedApiEnabled(extension); + return extHostTypes.TimelineItem; + }, + get CellKind() { + // checkProposedApiEnabled(extension); + return extHostTypes.CellKind; + }, + get CellOutputKind() { + // checkProposedApiEnabled(extension); + return extHostTypes.CellOutputKind; + }, + get NotebookCellRunState() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellRunState; + }, + get NotebookRunState() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookRunState; + }, + get NotebookCellStatusBarAlignment() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellStatusBarAlignment; + }, + get NotebookEditorRevealType() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookEditorRevealType; + }, + get NotebookCellOutput() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellOutput; + }, + get NotebookCellOutputItem() { + // checkProposedApiEnabled(extension); + return extHostTypes.NotebookCellOutputItem; + }, + get LinkedEditingRanges() { + // checkProposedApiEnabled(extension); + return extHostTypes.LinkedEditingRanges; + }, + get TestRunState() { + // checkProposedApiEnabled(extension); + return extHostTypes.TestRunState; + }, + get TestMessageSeverity() { + // checkProposedApiEnabled(extension); + return extHostTypes.TestMessageSeverity; + }, + get TestState() { + // checkProposedApiEnabled(extension); + return extHostTypes.TestState; + }, }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 77ef6577..b97a962f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -36,7 +36,6 @@ import * as statusbar from 'vs/workbench/services/statusbar/common/statusbar'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import * as tasks from 'vs/workbench/api/common/shared/tasks'; import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views'; import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; @@ -45,10 +44,10 @@ import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'v import { ActivationKind, ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; -import { SaveReason } from 'vs/workbench/common/editor'; +import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; -import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; import { IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, INotebookKernelInfoDto2, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -57,7 +56,8 @@ import { Dto } from 'vs/base/common/types'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; -import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync'; +import { RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -263,21 +263,21 @@ export interface IApplyEditsOptions extends IUndoStopOptions { } export interface ITextDocumentShowOptions { - position?: EditorViewColumn; + position?: EditorGroupColumn; preserveFocus?: boolean; pinned?: boolean; selection?: IRange; } export interface MainThreadBulkEditsShape extends IDisposable { - $tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto): Promise; + $tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise; } export interface MainThreadTextEditorsShape extends IDisposable { $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise; $registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void; $removeTextEditorDecorationType(key: string): void; - $tryShowEditor(id: string, position: EditorViewColumn): Promise; + $tryShowEditor(id: string, position: EditorGroupColumn): Promise; $tryHideEditor(id: string): Promise; $trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise; $trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): Promise; @@ -292,7 +292,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { export interface MainThreadTreeViewsShape extends IDisposable { $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): void; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise; - $reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise; + $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; $setTitle(treeViewId: string, title: string, description: string | undefined): void; } @@ -384,7 +384,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; - $registerOnTypeRenameProvider(handle: number, selector: IDocumentFilterDto[], stopPattern: IRegExpDto | undefined): void; + $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string, supportsResolve: boolean): void; $registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; @@ -565,7 +565,7 @@ export interface MainThreadQuickOpenShape extends IDisposable { } export interface MainThreadStatusBarShape extends IDisposable { - $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void; + $setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: statusbar.StatusbarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void; $dispose(id: number): void; } @@ -597,7 +597,7 @@ export interface ExtHostEditorInsetsShape { export type WebviewHandle = string; export interface WebviewPanelShowOptions { - readonly viewColumn?: EditorViewColumn; + readonly viewColumn?: EditorGroupColumn; readonly preserveFocus?: boolean; } @@ -661,7 +661,7 @@ export interface WebviewPanelViewStateData { [handle: string]: { readonly active: boolean; readonly visible: boolean; - readonly position: EditorViewColumn; + readonly position: EditorGroupColumn; }; } @@ -673,11 +673,11 @@ export interface ExtHostWebviewsShape { export interface ExtHostWebviewPanelsShape { $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; $onDidDisposeWebviewPanel(handle: WebviewHandle): Promise; - $deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } export interface ExtHostCustomEditorsShape { - $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise; + $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise; $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>; $disposeCustomDocument(resource: UriComponents, viewType: string): Promise; @@ -741,6 +741,13 @@ export enum NotebookEditorRevealType { InCenterIfOutsideViewport = 2, } +export interface INotebookDocumentShowOptions { + position?: EditorGroupColumn; + preserveFocus?: boolean; + pinned?: boolean; + selection?: ICellRange; +} + export type INotebookCellStatusBarEntryDto = Dto; export interface MainThreadNotebookShape extends IDisposable { @@ -760,6 +767,7 @@ export interface MainThreadNotebookShape extends IDisposable { $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; $tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise; + $tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise; $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise; $registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void; $removeNotebookEditorDecorationType(key: string): void; @@ -866,7 +874,8 @@ export type SCMRawResource = [ string /*tooltip*/, boolean /*strike through*/, boolean /*faded*/, - string /*context value*/ + string /*context value*/, + ICommandDto | undefined /*command*/ ]; export type SCMRawResourceSplice = [ @@ -920,7 +929,7 @@ export interface MainThreadDebugServiceShape extends IDisposable { $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void; $acceptDAError(handle: number, name: string, message: string, stack: string | undefined): void; $acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void; - $registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise; + $registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, handle: number): Promise; $registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise; $unregisterDebugConfigurationProvider(handle: number): void; $unregisterDebugAdapterDescriptorFactory(handle: number): void; @@ -946,13 +955,12 @@ export interface MainThreadWindowShape extends IDisposable { } export interface MainThreadTunnelServiceShape extends IDisposable { - $openTunnel(tunnelOptions: TunnelOptions): Promise; + $openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise; $closeTunnel(remote: { host: string, port: number }): Promise; $getTunnels(): Promise; - $registerCandidateFinder(): Promise; $setTunnelProvider(): Promise; - $setCandidateFilter(): Promise; $tunnelServiceReady(): Promise; + $onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise; } export interface MainThreadTimelineShape extends IDisposable { @@ -1006,10 +1014,10 @@ export interface ITextEditorAddData { options: IResolvedTextEditorConfiguration; selections: ISelection[]; visibleRanges: IRange[]; - editorPosition: EditorViewColumn | undefined; + editorPosition: EditorGroupColumn | undefined; } export interface ITextEditorPositionData { - [id: string]: EditorViewColumn; + [id: string]: EditorGroupColumn; } export interface IEditorPropertiesChangeData { options: IResolvedTextEditorConfiguration | null; @@ -1139,7 +1147,7 @@ export interface SourceTargetPair { export interface ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents): void; - $onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise; + $onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise; $onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void; } @@ -1307,7 +1315,7 @@ export interface IWorkspaceCellEditDto { export interface IWorkspaceEditDto { edits: Array; - // todo@joh reject should go into rename + // todo@jrieken reject should go into rename rejectReason?: string; } @@ -1395,6 +1403,11 @@ export interface ILanguageWordDefinitionDto { regexFlags: string } +export interface ILinkedEditingRangesDto { + ranges: IRange[]; + wordPattern?: IRegExpDto; +} + export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; @@ -1407,7 +1420,7 @@ export interface ExtHostLanguageFeaturesShape { $provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; - $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: IRegExpDto; } | undefined>; + $provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise; $provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise; $resolveCodeAction(handle: number, id: ChainedCacheId, token: CancellationToken): Promise; @@ -1602,7 +1615,6 @@ export interface ExtHostDebugServiceShape { $resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; $resolveDebugConfigurationWithSubstitutedVariables(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; $provideDebugConfigurations(handle: number, folder: UriComponents | undefined, token: CancellationToken): Promise; - $legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise; // TODO@AW legacy $provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise; $acceptDebugSessionStarted(session: IDebugSessionDto): void; $acceptDebugSessionTerminated(session: IDebugSessionDto): void; @@ -1709,7 +1721,6 @@ export interface ExtHostNotebookShape { $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise; - $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise; $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise; $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise; $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise; @@ -1738,10 +1749,8 @@ export interface MainThreadThemingShape extends IDisposable { } export interface ExtHostTunnelServiceShape { - $findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]>; - $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise; - $forwardPort(tunnelOptions: TunnelOptions): Promise | undefined; - $closeTunnel(remote: { host: string, port: number }): Promise; + $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined; + $closeTunnel(remote: { host: string, port: number }, silent?: boolean): Promise; $onDidTunnelsChange(): Promise; } @@ -1749,6 +1758,28 @@ export interface ExtHostTimelineShape { $getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions): Promise; } +export const enum ExtHostTestingResource { + Workspace, + TextDocument +} + +export interface ExtHostTestingShape { + $runTestsForProvider(req: RunTestForProviderRequest): Promise; + $subscribeToTests(resource: ExtHostTestingResource, uri: UriComponents): void; + $unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void; + + $acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void; +} + +export interface MainThreadTestingShape { + $registerTestProvider(id: string): void; + $unregisterTestProvider(id: string): void; + $subscribeToDiffs(resource: ExtHostTestingResource, uri: UriComponents): void; + $unsubscribeFromDiffs(resource: ExtHostTestingResource, uri: UriComponents): void; + $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void; + $runTests(req: RunTestsRequest): Promise; +} + // --- proxy identifiers export const MainContext = { @@ -1798,7 +1829,8 @@ export const MainContext = { MainThreadNotebook: createMainId('MainThreadNotebook'), MainThreadTheming: createMainId('MainThreadTheming'), MainThreadTunnelService: createMainId('MainThreadTunnelService'), - MainThreadTimeline: createMainId('MainThreadTimeline') + MainThreadTimeline: createMainId('MainThreadTimeline'), + MainThreadTesting: createMainId('MainThreadTesting'), }; export const ExtHostContext = { @@ -1841,5 +1873,6 @@ export const ExtHostContext = { ExtHostTheming: createMainId('ExtHostTheming'), ExtHostTunnelService: createMainId('ExtHostTunnelService'), ExtHostAuthentication: createMainId('ExtHostAuthentication'), - ExtHostTimeline: createMainId('ExtHostTimeline') + ExtHostTimeline: createMainId('ExtHostTimeline'), + ExtHostTesting: createMainId('ExtHostTesting'), }; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 26300e19..08b2543a 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import type * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as types from 'vs/workbench/api/common/extHostTypes'; @@ -12,77 +12,17 @@ import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallD import * as modes from 'vs/editor/common/modes'; import * as search from 'vs/workbench/contrib/search/common/search'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures'; -import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands'; -import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { IRange } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; //#region --- NEW world -export class ApiCommandArgument { - - static readonly Uri = new ApiCommandArgument('uri', 'Uri of a text document', v => URI.isUri(v), v => v); - static readonly Position = new ApiCommandArgument('position', 'A position in a text document', v => types.Position.isPosition(v), typeConverters.Position.from); - static readonly Range = new ApiCommandArgument('range', 'A range in a text document', v => types.Range.isRange(v), typeConverters.Range.from); - - static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof types.CallHierarchyItem, typeConverters.CallHierarchyItem.to); - - constructor( - readonly name: string, - readonly description: string, - readonly validate: (v: V) => boolean, - readonly convert: (v: V) => O - ) { } -} - -export class ApiCommandResult { - - constructor( - readonly description: string, - readonly convert: (v: V, apiArgs: any[]) => O - ) { } -} - -export class ApiCommand { - - constructor( - readonly id: string, - readonly internalId: string, - readonly description: string, - readonly args: ApiCommandArgument[], - readonly result: ApiCommandResult - ) { } - - register(commands: ExtHostCommands): IDisposable { - - return commands.registerCommand(false, this.id, async (...apiArgs) => { - - const internalArgs = this.args.map((arg, i) => { - if (!arg.validate(apiArgs[i])) { - throw new Error(`Invalid argument '${arg.name}' when running '${this.id}', receieved: ${apiArgs[i]}`); - } - return arg.convert(apiArgs[i]); - }); - - const internalResult = await commands.executeCommand(this.internalId, ...internalArgs); - return this.result.convert(internalResult, apiArgs); - }, undefined, this._getCommandHandlerDesc()); - } - - private _getCommandHandlerDesc(): ICommandHandlerDescription { - return { - description: this.description, - args: this.args, - returns: this.result.description - }; - } -} - - const newCommands: ApiCommand[] = [ // -- document highlights new ApiCommand( @@ -189,7 +129,7 @@ const newCommands: ApiCommand[] = [ // -- symbol search new ApiCommand( 'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol providers.', - [new ApiCommandArgument('query', 'Search string', v => typeof v === 'string', v => v)], + [ApiCommandArgument.String.with('query', 'Search string')], new ApiCommandResult<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][], types.SymbolInformation[]>('A promise that resolves to an array of SymbolInformation-instances.', value => { const result: types.SymbolInformation[] = []; if (Array.isArray(value)) { @@ -219,7 +159,7 @@ const newCommands: ApiCommand[] = [ // --- rename new ApiCommand( 'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.', - [ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('newName', 'The new symbol name', v => typeof v === 'string', v => v)], + [ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('newName', 'The new symbol name')], new ApiCommandResult('A promise that resolves to a WorkspaceEdit.', value => { if (!value) { return undefined; @@ -233,9 +173,169 @@ const newCommands: ApiCommand[] = [ // --- links new ApiCommand( 'vscode.executeLinkProvider', '_executeLinkProvider', 'Execute document link provider.', - [ApiCommandArgument.Uri, new ApiCommandArgument('linkResolveCount', '(optional) Number of links that should be resolved, only when links are unresolved.', v => typeof v === 'number' || typeof v === 'undefined', v => v)], + [ApiCommandArgument.Uri, ApiCommandArgument.Number.with('linkResolveCount', 'Number of links that should be resolved, only when links are unresolved.').optional()], new ApiCommandResult('A promise that resolves to an array of DocumentLink-instances.', value => value.map(typeConverters.DocumentLink.to)) - ) + ), + // --- completions + new ApiCommand( + 'vscode.executeCompletionItemProvider', '_executeCompletionItemProvider', 'Execute completion item provider.', + [ + ApiCommandArgument.Uri, + ApiCommandArgument.Position, + ApiCommandArgument.String.with('triggerCharacter', 'Trigger completion when the user types the character, like `,` or `(`').optional(), + ApiCommandArgument.Number.with('itemResolveCount', 'Number of completions to resolve (too large numbers slow down completions)').optional() + ], + new ApiCommandResult('A promise that resolves to a CompletionList-instance.', (value, _args, converter) => { + if (!value) { + return new types.CompletionList([]); + } + const items = value.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, converter)); + return new types.CompletionList(items, value.incomplete); + }) + ), + // --- signature help + new ApiCommand( + 'vscode.executeSignatureHelpProvider', '_executeSignatureHelpProvider', 'Execute signature help provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('triggerCharacter', 'Trigger signature help when the user types the character, like `,` or `(`').optional()], + new ApiCommandResult('A promise that resolves to SignatureHelp.', value => { + if (value) { + return typeConverters.SignatureHelp.to(value); + } + return undefined; + }) + ), + // --- code lens + new ApiCommand( + 'vscode.executeCodeLensProvider', '_executeCodeLensProvider', 'Execute code lens provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Number.with('itemResolveCount', 'Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)').optional()], + new ApiCommandResult('A promise that resolves to an array of CodeLens-instances.', (value, _args, converter) => { + return tryMapWith(item => { + return new types.CodeLens(typeConverters.Range.to(item.range), item.command && converter.fromInternal(item.command)); + })(value); + }) + ), + // --- code actions + new ApiCommand( + 'vscode.executeCodeActionProvider', '_executeCodeActionProvider', 'Execute code action provider.', + [ + ApiCommandArgument.Uri, + new ApiCommandArgument('rangeOrSelection', 'Range in a text document. Some refactoring provider requires Selection object.', v => types.Range.isRange(v), v => types.Selection.isSelection(v) ? typeConverters.Selection.from(v) : typeConverters.Range.from(v)), + ApiCommandArgument.String.with('kind', 'Code action kind to return code actions for').optional(), + ApiCommandArgument.Number.with('itemResolveCount', 'Number of code actions to resolve (too large numbers slow down code actions)').optional() + ], + new ApiCommandResult('A promise that resolves to an array of Command-instances.', (value, _args, converter) => { + return tryMapWith((codeAction) => { + if (codeAction._isSynthetic) { + if (!codeAction.command) { + throw new Error('Synthetic code actions must have a command'); + } + return converter.fromInternal(codeAction.command); + } else { + const ret = new types.CodeAction( + codeAction.title, + codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined + ); + if (codeAction.edit) { + ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit); + } + if (codeAction.command) { + ret.command = converter.fromInternal(codeAction.command); + } + ret.isPreferred = codeAction.isPreferred; + return ret; + } + })(value); + }) + ), + // --- colors + new ApiCommand( + 'vscode.executeDocumentColorProvider', '_executeDocumentColorProvider', 'Execute document color provider.', + [ApiCommandArgument.Uri], + new ApiCommandResult('A promise that resolves to an array of ColorInformation objects.', result => { + if (result) { + return result.map(ci => new types.ColorInformation(typeConverters.Range.to(ci.range), typeConverters.Color.to(ci.color))); + } + return []; + }) + ), + new ApiCommand( + 'vscode.executeColorPresentationProvider', '_executeColorPresentationProvider', 'Execute color presentation provider.', + [ + new ApiCommandArgument('color', 'The color to show and insert', v => v instanceof types.Color, typeConverters.Color.from), + new ApiCommandArgument<{ uri: URI, range: types.Range; }, { uri: URI, range: IRange; }>('context', 'Context object with uri and range', _v => true, v => ({ uri: v.uri, range: typeConverters.Range.from(v.range) })), + ], + new ApiCommandResult('A promise that resolves to an array of ColorPresentation objects.', result => { + if (result) { + return result.map(typeConverters.ColorPresentation.to); + } + return []; + }) + ), + // --- notebooks + new ApiCommand( + 'vscode.resolveNotebookContentProviders', '_resolveNotebookContentProvider', 'Resolve Notebook Content Providers', + [ + new ApiCommandArgument('viewType', '', v => typeof v === 'string', v => v), + new ApiCommandArgument('displayName', '', v => typeof v === 'string', v => v), + new ApiCommandArgument('options', '', v => typeof v === 'object', v => v), + ], + new ApiCommandResult<{ + viewType: string; + displayName: string; + options: { transientOutputs: boolean; transientMetadata: TransientMetadata }; + filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[] + }[], { + viewType: string; + displayName: string; + filenamePattern: vscode.NotebookFilenamePattern[]; + options: vscode.NotebookDocumentContentOptions; + }[] | undefined>('A promise that resolves to an array of NotebookContentProvider static info objects.', tryMapWith(item => { + return { + viewType: item.viewType, + displayName: item.displayName, + options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata }, + filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern)) + }; + })) + ), + // --- open'ish commands + new ApiCommand( + 'vscode.open', '_workbench.open', 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.', + [ + ApiCommandArgument.Uri, + new ApiCommandArgument('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', + v => v === undefined || typeof v === 'number' || typeof v === 'object', + v => !v ? v : typeof v === 'number' ? [v, undefined] : [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)] + ).optional(), + ApiCommandArgument.String.with('label', '').optional() + ], + ApiCommandResult.Void + ), + new ApiCommand( + 'vscode.openWith', '_workbench.openWith', 'Opens the provided resource with a specific editor.', + [ + ApiCommandArgument.Uri.with('resource', 'Resource to open'), + ApiCommandArgument.String.with('viewId', 'Custom editor view id or \'default\' to use VS Code\'s default editor'), + new ApiCommandArgument('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', + v => v === undefined || typeof v === 'number' || typeof v === 'object', + v => !v ? v : typeof v === 'number' ? [v, undefined] : [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)], + ).optional() + ], + ApiCommandResult.Void + ), + new ApiCommand( + 'vscode.diff', '_workbench.diff', 'Opens the provided resources in the diff editor to compare their contents.', + [ + ApiCommandArgument.Uri.with('left', 'Left-hand side resource of the diff editor'), + ApiCommandArgument.Uri.with('right', 'Rigth-hand side resource of the diff editor'), + ApiCommandArgument.String.with('title', 'Human readable title for the diff editor').optional(), + new ApiCommandArgument('columnOrOptions', 'Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', + v => v === undefined || typeof v === 'object', + v => v && [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)] + ).optional(), + ], + ApiCommandResult.Void + ), ]; //#endregion @@ -246,7 +346,7 @@ const newCommands: ApiCommand[] = [ export class ExtHostApiCommands { static register(commands: ExtHostCommands) { - newCommands.forEach(command => command.register(commands)); + newCommands.forEach(commands.registerApiCommand, commands); return new ExtHostApiCommands(commands).registerCommands(); } @@ -258,66 +358,10 @@ export class ExtHostApiCommands { } registerCommands() { - this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, { - description: 'Execute signature help provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position }, - { name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' } - ], - returns: 'A promise that resolves to SignatureHelp.' - }); - this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, { - description: 'Execute completion item provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position }, - { name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' }, - { name: 'itemResolveCount', description: '(optional) Number of completions to resolve (too large numbers slow down completions)', constraint: (value: any) => value === undefined || typeof value === 'number' } - ], - returns: 'A promise that resolves to a CompletionList-instance.' - }); - this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, { - description: 'Execute code action provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range }, - { name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' }, - { name: 'itemResolveCount', description: '(optional) Number of code actions to resolve (too large numbers slow down code actions)', constraint: (value: any) => value === undefined || typeof value === 'number' } - ], - returns: 'A promise that resolves to an array of Command-instances.' - }); - this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, { - description: 'Execute CodeLens provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)', constraint: (value: any) => value === undefined || typeof value === 'number' } - ], - returns: 'A promise that resolves to an array of CodeLens-instances.' - }); - this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, { - description: 'Execute document color provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - ], - returns: 'A promise that resolves to an array of ColorInformation objects.' - }); - this._register('vscode.executeColorPresentationProvider', this._executeColorPresentationProvider, { - description: 'Execute color presentation provider.', - args: [ - { name: 'color', description: 'The color to show and insert', constraint: types.Color }, - { name: 'context', description: 'Context object with uri and range' } - ], - returns: 'A promise that resolves to an array of ColorPresentation objects.' - }); - this._register('vscode.resolveNotebookContentProviders', this._resolveNotebookContentProviders, { - description: 'Resolve Notebook Content Providers', - args: [], - returns: 'A promise that resolves to an array of NotebookContentProvider static info objects.' - }); + // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -333,32 +377,6 @@ export class ExtHostApiCommands { }; }; - this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), { - description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', - args: [ - { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || URI.isUri(value) }, - { name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Whether the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' } - ] - }); - - this._register(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute), { - description: 'Opens the provided resources in the diff editor to compare their contents.', - args: [ - { name: 'left', description: 'Left-hand side resource of the diff editor', constraint: URI }, - { name: 'right', description: 'Right-hand side resource of the diff editor', constraint: URI }, - { name: 'title', description: '(optional) Human readable title for the diff editor', constraint: (v: any) => v === undefined || typeof v === 'string' }, - { name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' } - ] - }); - - this._register(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute), { - description: 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.', - args: [ - { name: 'resource', description: 'Resource to open', constraint: URI }, - { name: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: (v: any) => v === undefined || typeof v === 'number' || typeof v === 'object' } - ] - }); - this._register(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute), { description: 'Removes an entry with the given path from the recently opened list.', args: [ @@ -366,13 +384,6 @@ export class ExtHostApiCommands { ] }); - this._register(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute), { - description: 'Sets the editor layout. The layout is described as object with an initial (optional) orientation (0 = horizontal, 1 = vertical) and an array of editor groups within. Each editor group can have a size and another array of editor groups that will be laid out orthogonal to the orientation. If editor group sizes are provided, their sum must be 1 to be applied per row or column. Example for a 2x2 grid: `{ orientation: 0, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}, {}], size: 0.5 }] }`', - args: [ - { name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) } - ] - }); - this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), { description: 'Opens the issue reporter with the provided extension id as the selected source', args: [ @@ -383,132 +394,16 @@ export class ExtHostApiCommands { // --- command impl + /** + * @deprecated use the ApiCommand instead + */ private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void { const disposable = this._commands.registerCommand(false, id, handler, this, description); this._disposables.add(disposable); } - private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position), - triggerCharacter - }; - return this._commands.executeCommand('_executeSignatureHelpProvider', args).then(value => { - if (value) { - return typeConverters.SignatureHelp.to(value); - } - return undefined; - }); - } - - private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position), - triggerCharacter, - maxItemsToResolve - }; - return this._commands.executeCommand('_executeCompletionItemProvider', args).then(result => { - if (result) { - const items = result.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, this._commands.converter)); - return new types.CompletionList(items, result.incomplete); - } - return undefined; - }); - } - - private _executeDocumentColorProvider(resource: URI): Promise { - const args = { - resource - }; - return this._commands.executeCommand('_executeDocumentColorProvider', args).then(result => { - if (result) { - return result.map(ci => ({ range: typeConverters.Range.to(ci.range), color: typeConverters.Color.to(ci.color) })); - } - return []; - }); - } - - private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range; }): Promise { - const args = { - resource: context.uri, - color: typeConverters.Color.from(color), - range: typeConverters.Range.from(context.range), - }; - return this._commands.executeCommand('_executeColorPresentationProvider', args).then(result => { - if (result) { - return result.map(typeConverters.ColorPresentation.to); - } - return []; - }); - } - private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string, itemResolveCount?: number): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> { - const args = { - resource, - rangeOrSelection: types.Selection.isSelection(rangeOrSelection) - ? typeConverters.Selection.from(rangeOrSelection) - : typeConverters.Range.from(rangeOrSelection), - kind, - itemResolveCount, - }; - return this._commands.executeCommand('_executeCodeActionProvider', args) - .then(tryMapWith(codeAction => { - if (codeAction._isSynthetic) { - if (!codeAction.command) { - throw new Error('Synthetic code actions must have a command'); - } - return this._commands.converter.fromInternal(codeAction.command); - } else { - const ret = new types.CodeAction( - codeAction.title, - codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined - ); - if (codeAction.edit) { - ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit); - } - if (codeAction.command) { - ret.command = this._commands.converter.fromInternal(codeAction.command); - } - ret.isPreferred = codeAction.isPreferred; - return ret; - } - })); - } - - private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise { - const args = { resource, itemResolveCount }; - return this._commands.executeCommand('_executeCodeLensProvider', args) - .then(tryMapWith(item => { - return new types.CodeLens( - typeConverters.Range.to(item.range), - item.command ? this._commands.converter.fromInternal(item.command) : undefined); - })); - } - - private _resolveNotebookContentProviders(): Promise<{ - viewType: string; - displayName: string; - filenamePattern: vscode.NotebookFilenamePattern[]; - options: vscode.NotebookDocumentContentOptions; - }[] | undefined> { - return this._commands.executeCommand<{ - viewType: string; - displayName: string; - options: { transientOutputs: boolean; transientMetadata: TransientMetadata }; - filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[] - }[]>('_resolveNotebookContentProvider') - .then(tryMapWith(item => { - return { - viewType: item.viewType, - displayName: item.displayName, - options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata }, - filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern)) - }; - })); - } } function tryMapWith(f: (x: T) => R) { diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index c20a7a42..649612e3 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -14,12 +14,13 @@ import * as modes from 'vs/editor/common/modes'; import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; import { revive } from 'vs/base/common/marshalling'; -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { URI } from 'vs/base/common/uri'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { ISelection } from 'vs/editor/common/core/selection'; interface CommandHandler { callback: Function; @@ -36,18 +37,32 @@ export class ExtHostCommands implements ExtHostCommandsShape { readonly _serviceBrand: undefined; private readonly _commands = new Map(); + private readonly _apiCommands = new Map(); + private readonly _proxy: MainThreadCommandsShape; - private readonly _converter: CommandsConverter; private readonly _logService: ILogService; private readonly _argumentProcessors: ArgumentProcessor[]; + readonly converter: CommandsConverter; + constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @ILogService logService: ILogService ) { this._proxy = extHostRpc.getProxy(MainContext.MainThreadCommands); this._logService = logService; - this._converter = new CommandsConverter(this, logService); + this.converter = new CommandsConverter( + this, + id => { + // API commands that have no return type (void) can be + // converted to their internal command and don't need + // any indirection commands + const candidate = this._apiCommands.get(id); + return candidate?.result === ApiCommandResult.Void + ? candidate : undefined; + }, + logService + ); this._argumentProcessors = [ { processArgument(a) { @@ -77,14 +92,38 @@ export class ExtHostCommands implements ExtHostCommandsShape { ]; } - get converter(): CommandsConverter { - return this._converter; - } - registerArgumentProcessor(processor: ArgumentProcessor): void { this._argumentProcessors.push(processor); } + registerApiCommand(apiCommand: ApiCommand): extHostTypes.Disposable { + + + const registration = this.registerCommand(false, apiCommand.id, async (...apiArgs) => { + + const internalArgs = apiCommand.args.map((arg, i) => { + if (!arg.validate(apiArgs[i])) { + throw new Error(`Invalid argument '${arg.name}' when running '${apiCommand.id}', receieved: ${apiArgs[i]}`); + } + return arg.convert(apiArgs[i]); + }); + + const internalResult = await this.executeCommand(apiCommand.internalId, ...internalArgs); + return apiCommand.result.convert(internalResult, apiArgs, this.converter); + }, undefined, { + description: apiCommand.description, + args: apiCommand.args, + returns: apiCommand.result.description + }); + + this._apiCommands.set(apiCommand.id, apiCommand); + + return new extHostTypes.Disposable(() => { + registration.dispose(); + this._apiCommands.delete(apiCommand.id); + }); + } + registerCommand(global: boolean, id: string, callback: (...args: any[]) => T | Thenable, thisArg?: any, description?: ICommandHandlerDescription): extHostTypes.Disposable { this._logService.trace('ExtHostCommands#registerCommand', id); @@ -214,6 +253,8 @@ export class ExtHostCommands implements ExtHostCommandsShape { } } +export interface IExtHostCommands extends ExtHostCommands { } +export const IExtHostCommands = createDecorator('IExtHostCommands'); export class CommandsConverter { @@ -224,6 +265,7 @@ export class CommandsConverter { // --- conversion between internal and api commands constructor( private readonly _commands: ExtHostCommands, + private readonly _lookupApiCommand: (id: string) => ApiCommand | undefined, private readonly _logService: ILogService ) { this._delegatingCommandId = `_vscode_delegate_cmd_${Date.now().toString(36)}`; @@ -245,7 +287,20 @@ export class CommandsConverter { tooltip: command.tooltip }; - if (command.command && isNonEmptyArray(command.arguments)) { + if (!command.command) { + // falsy command id -> return converted command but don't attempt any + // argument or API-command dance since this command won't run anyways + return result; + } + + const apiCommand = this._lookupApiCommand(command.command); + if (apiCommand) { + // API command with return-value can be converted inplace + result.id = apiCommand.internalId; + result.arguments = apiCommand.args.map((arg, i) => arg.convert(command.arguments && command.arguments[i])); + + + } else if (isNonEmptyArray(command.arguments)) { // we have a contributed command with arguments. that // means we don't want to send the arguments around @@ -293,5 +348,55 @@ export class CommandsConverter { } -export interface IExtHostCommands extends ExtHostCommands { } -export const IExtHostCommands = createDecorator('IExtHostCommands'); + +export class ApiCommandArgument { + + static readonly Uri = new ApiCommandArgument('uri', 'Uri of a text document', v => URI.isUri(v), v => v); + static readonly Position = new ApiCommandArgument('position', 'A position in a text document', v => extHostTypes.Position.isPosition(v), extHostTypeConverter.Position.from); + static readonly Range = new ApiCommandArgument('range', 'A range in a text document', v => extHostTypes.Range.isRange(v), extHostTypeConverter.Range.from); + static readonly Selection = new ApiCommandArgument('selection', 'A selection in a text document', v => extHostTypes.Selection.isSelection(v), extHostTypeConverter.Selection.from); + static readonly Number = new ApiCommandArgument('number', '', v => typeof v === 'number', v => v); + static readonly String = new ApiCommandArgument('string', '', v => typeof v === 'string', v => v); + + static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof extHostTypes.CallHierarchyItem, extHostTypeConverter.CallHierarchyItem.to); + + constructor( + readonly name: string, + readonly description: string, + readonly validate: (v: V) => boolean, + readonly convert: (v: V) => O + ) { } + + optional(): ApiCommandArgument { + return new ApiCommandArgument( + this.name, `(optional) ${this.description}`, + value => value === undefined || value === null || this.validate(value), + value => value === undefined ? undefined : value === null ? null : this.convert(value) + ); + } + + with(name: string | undefined, description: string | undefined): ApiCommandArgument { + return new ApiCommandArgument(name ?? this.name, description ?? this.description, this.validate, this.convert); + } +} + +export class ApiCommandResult { + + static readonly Void = new ApiCommandResult('no result', v => v); + + constructor( + readonly description: string, + readonly convert: (v: V, apiArgs: any[], cmdConverter: CommandsConverter) => O + ) { } +} + +export class ApiCommand { + + constructor( + readonly id: string, + readonly internalId: string, + readonly description: string, + readonly args: ApiCommandArgument[], + readonly result: ApiCommandResult + ) { } +} diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts index f3b3cbd8..becd0887 100644 --- a/src/vs/workbench/api/common/extHostCustomEditors.ts +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { EditorGroupColumn } from 'vs/workbench/common/editor'; import type * as vscode from 'vscode'; import { Cache } from './cache'; import * as extHostProtocol from './extHost.protocol'; @@ -252,7 +252,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor handle: extHostProtocol.WebviewHandle, viewType: string, title: string, - position: EditorViewColumn, + position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken, ): Promise { diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index b724e831..4026a1d2 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -23,7 +23,6 @@ import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostC import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ISignService } from 'vs/platform/sign/common/sign'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; @@ -97,7 +96,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private readonly _onDidChangeBreakpoints: Emitter; - private _aexCommands: Map; private _debugAdapters: Map; private _debugAdaptersTrackers: Map; @@ -105,14 +103,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private _signService: ISignService | undefined; - constructor( @IExtHostRpcService extHostRpcService: IExtHostRpcService, - @IExtHostWorkspace private _workspaceService: IExtHostWorkspace, + @IExtHostWorkspace protected _workspaceService: IExtHostWorkspace, @IExtHostExtensionService private _extensionService: IExtHostExtensionService, @IExtHostDocumentsAndEditors private _editorsService: IExtHostDocumentsAndEditors, @IExtHostConfiguration protected _configurationService: IExtHostConfiguration, - @IExtHostCommands private _commandService: IExtHostCommands ) { this._configProviderHandleCounter = 0; this._configProviders = []; @@ -123,7 +119,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E this._trackerFactoryHandleCounter = 0; this._trackerFactories = []; - this._aexCommands = new Map(); this._debugAdapters = new Map(); this._debugAdaptersTrackers = new Map(); @@ -182,7 +177,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) { const debugTypes: string[] = []; - this._aexCommands.clear(); for (const ed of extensionRegistry.getAllExtensionDescriptions()) { if (ed.contributes) { @@ -191,9 +185,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E for (const dbg of debuggers) { if (isDebuggerMainContribution(dbg)) { debugTypes.push(dbg.type); - if (dbg.adapterExecutableCommand) { - this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand); - } } } } @@ -312,10 +303,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E return new Disposable(() => { }); } - if (provider.debugAdapterExecutable) { - console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.'); - } - const handle = this._configProviderHandleCounter++; this._configProviders.push({ type, handle, provider }); @@ -323,7 +310,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E !!provider.provideDebugConfigurations, !!provider.resolveDebugConfiguration, !!provider.resolveDebugConfigurationWithSubstitutedVariables, - !!provider.debugAdapterExecutable, // TODO@AW: deprecated handle); return new Disposable(() => { @@ -651,26 +637,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E }); } - // TODO@AW deprecated and legacy - public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { - return asPromise(async () => { - const provider = this.getConfigProviderByHandle(configProviderHandle); - if (!provider) { - throw new Error('no DebugConfigurationProvider found'); - } - if (!provider.debugAdapterExecutable) { - throw new Error('DebugConfigurationProvider has no method debugAdapterExecutable'); - } - const folder = await this.getFolder(folderUri); - return provider.debugAdapterExecutable(folder, CancellationToken.None); - }).then(executable => { - if (!executable) { - throw new Error('nothing returned from DebugConfigurationProvider.debugAdapterExecutable'); - } - return this.convertToDto(executable); - }); - } - public async $provideDebugAdapter(adapterFactoryHandle: number, sessionDto: IDebugSessionDto): Promise { const adapterDescriptorFactory = this.getAdapterDescriptorFactoryByHandle(adapterFactoryHandle); if (!adapterDescriptorFactory) { @@ -830,18 +796,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E return Promise.resolve(new DebugAdapterServer(serverPort)); } - // TODO@AW legacy - const pair = this._configProviders.filter(p => p.type === session.type).pop(); - if (pair && pair.provider.debugAdapterExecutable) { - const func = pair.provider.debugAdapterExecutable; - return asPromise(() => func(session.workspaceFolder, CancellationToken.None)).then(executable => { - if (executable) { - return executable; - } - return undefined; - }); - } - if (adapterDescriptorFactory) { const extensionRegistry = await this._extensionService.getExtensionRegistry(); return asPromise(() => adapterDescriptorFactory.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => { @@ -852,17 +806,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E }); } - // try deprecated command based extension API "adapterExecutableCommand" to determine the executable - // TODO@AW legacy - const aex = this._aexCommands.get(session.type); - if (aex) { - const folder = session.workspaceFolder; - const rootFolder = folder ? folder.uri.toString() : undefined; - return this._commandService.executeCommand(aex, rootFolder).then((ae: any) => { - return new DebugAdapterExecutable(ae.command, ae.args || []); - }); - } - // fallback: use executable information from package.json const extensionRegistry = await this._extensionService.getExtensionRegistry(); return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry)); @@ -987,7 +930,7 @@ export class ExtHostDebugConsole implements vscode.DebugConsole { export class ExtHostVariableResolverService extends AbstractVariableResolverService { - constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) { + constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment, workspaceService?: IExtHostWorkspace) { super({ getFolderUri: (folderName: string): URI | undefined => { const found = folders.filter(f => f.name === folderName); @@ -999,7 +942,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ getWorkspaceFolderCount: (): number => { return folders.length; }, - getConfigurationValue: (folderUri: URI, section: string): string | undefined => { + getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => { return configurationService.getConfiguration(undefined, folderUri).get(section); }, getExecPath: (): string | undefined => { @@ -1014,6 +957,18 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; }, + getWorkspaceFolderPathForFile: (): string | undefined => { + if (editorService && workspaceService) { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + const ws = workspaceService.getWorkspaceFolder(activeEditor.document.uri); + if (ws) { + return path.normalize(ws.uri.fsPath); + } + } + } + return undefined; + }, getSelectedText: (): string | undefined => { if (editorService) { const activeEditor = editorService.activeEditor(); @@ -1032,7 +987,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; } - }, undefined, env, !editorService); + }, undefined, env); } } @@ -1118,10 +1073,9 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase { @IExtHostWorkspace workspaceService: IExtHostWorkspace, @IExtHostExtensionService extensionService: IExtHostExtensionService, @IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors, - @IExtHostConfiguration configurationService: IExtHostConfiguration, - @IExtHostCommands commandService: IExtHostCommands + @IExtHostConfiguration configurationService: IExtHostConfiguration ) { - super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService); + super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService); } protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index 39ba9147..ea485837 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -37,12 +37,12 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { this._proxy = extHostRpc.getProxy(MainContext.MainThreadDecorations); } - registerDecorationProvider(provider: vscode.FileDecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable { + registerFileDecorationProvider(provider: vscode.FileDecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable { const handle = ExtHostDecorations._handlePool++; this._provider.set(handle, { provider, extensionId }); this._proxy.$registerDecorationProvider(handle, extensionId.value); - const listener = provider.onDidChange(e => { + const listener = provider.onDidChangeFileDecorations && provider.onDidChangeFileDecorations(e => { if (!e) { this._proxy.$onDidChange(handle, null); return; @@ -75,7 +75,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { }); return new Disposable(() => { - listener.dispose(); + listener?.dispose(); this._proxy.$unregisterDecorationProvider(handle); this._provider.delete(handle); }); diff --git a/src/vs/workbench/api/common/extHostDocumentContentProviders.ts b/src/vs/workbench/api/common/extHostDocumentContentProviders.ts index eba5e784..e4d2b49a 100644 --- a/src/vs/workbench/api/common/extHostDocumentContentProviders.ts +++ b/src/vs/workbench/api/common/extHostDocumentContentProviders.ts @@ -13,6 +13,7 @@ import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors'; import { Schemas } from 'vs/base/common/network'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { splitLines } from 'vs/base/common/strings'; export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape { @@ -61,7 +62,7 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro } // create lines and compare - const lines = value.split(/\r\n|\r|\n/); + const lines = splitLines(value); // broadcast event when content changed if (!document.equalLines(lines)) { diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index c576f8f5..0503a131 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -178,23 +178,23 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ }; } - async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise { + async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise { switch (operation) { case FileOperation.MOVE: - await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, timeout, token); + await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, undoRedoGroupId, timeout, token); break; case FileOperation.DELETE: - await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token); + await this._fireWillEvent(this._onWillDeleteFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token); break; case FileOperation.CREATE: - await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, timeout, token); + await this._fireWillEvent(this._onWillCreateFile, { files: files.map(f => URI.revive(f.target)) }, undoRedoGroupId, timeout, token); break; default: //ignore, dont send } } - private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, timeout: number, token: CancellationToken): Promise { + private async _fireWillEvent(emitter: AsyncEmitter, data: Omit, undoRedoGroupId: number | undefined, timeout: number, token: CancellationToken): Promise { const edits: WorkspaceEdit[] = []; @@ -222,7 +222,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); dto.edits = dto.edits.concat(edits); } - return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto); + return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto, undoRedoGroupId); } } } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index b26a4ef0..49cde88e 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -32,7 +32,6 @@ import { IdGenerator } from 'vs/base/common/idGenerator'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { Cache } from './cache'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; // --- adapter @@ -117,65 +116,57 @@ class CodeLensAdapter { private readonly _provider: vscode.CodeLensProvider ) { } - provideCodeLenses(resource: URI, token: CancellationToken): Promise { + async provideCodeLenses(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => { - - if (!lenses || token.isCancellationRequested) { - return undefined; - } - - const cacheId = this._cache.add(lenses); - const disposables = new DisposableStore(); - this._disposables.set(cacheId, disposables); - - const result: extHostProtocol.ICodeLensListDto = { - cacheId, - lenses: [], - }; - - for (let i = 0; i < lenses.length; i++) { - result.lenses.push({ - cacheId: [cacheId, i], - range: typeConvert.Range.from(lenses[i].range), - command: this._commands.toInternal(lenses[i].command, disposables) - }); - } - - return result; - }); + const lenses = await this._provider.provideCodeLenses(doc, token); + if (!lenses || token.isCancellationRequested) { + return undefined; + } + const cacheId = this._cache.add(lenses); + const disposables = new DisposableStore(); + this._disposables.set(cacheId, disposables); + const result: extHostProtocol.ICodeLensListDto = { + cacheId, + lenses: [], + }; + for (let i = 0; i < lenses.length; i++) { + result.lenses.push({ + cacheId: [cacheId, i], + range: typeConvert.Range.from(lenses[i].range), + command: this._commands.toInternal(lenses[i].command, disposables) + }); + } + return result; } - resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise { + async resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise { const lens = symbol.cacheId && this._cache.get(...symbol.cacheId); if (!lens) { - return Promise.resolve(undefined); + return undefined; } - let resolve: Promise; + let resolvedLens: vscode.CodeLens | undefined | null; if (typeof this._provider.resolveCodeLens !== 'function' || lens.isResolved) { - resolve = Promise.resolve(lens); + resolvedLens = lens; } else { - resolve = asPromise(() => this._provider.resolveCodeLens!(lens, token)); + resolvedLens = await this._provider.resolveCodeLens(lens, token); + } + if (!resolvedLens) { + resolvedLens = lens; } - return resolve.then(newLens => { - if (token.isCancellationRequested) { - return undefined; - } - - const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]); - if (!disposables) { - // We've already been disposed of - return undefined; - } - - newLens = newLens || lens; - symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd, disposables); - return symbol; - }); + if (token.isCancellationRequested) { + return undefined; + } + const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]); + if (!disposables) { + // disposed in the meantime + return undefined; + } + symbol.command = this._commands.toInternal(resolvedLens.command ?? CodeLensAdapter._badCmd, disposables); + return symbol; } releaseCodeLenses(cachedId: number): void { @@ -320,18 +311,18 @@ class DocumentHighlightAdapter { } } -class OnTypeRenameAdapter { +class LinkedEditingRangeAdapter { constructor( private readonly _documents: ExtHostDocuments, - private readonly _provider: vscode.OnTypeRenameProvider + private readonly _provider: vscode.LinkedEditingRangeProvider ) { } - provideOnTypeRenameRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: RegExp; } | undefined> { + provideLinkedEditingRanges(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); - return asPromise(() => this._provider.provideOnTypeRenameRanges(doc, pos, token)).then(value => { + return asPromise(() => this._provider.provideLinkedEditingRanges(doc, pos, token)).then(value => { if (value && Array.isArray(value.ranges)) { return { ranges: coalesce(value.ranges.map(typeConvert.Range.from)), @@ -1329,7 +1320,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter - | OnTypeRenameAdapter; + | LinkedEditingRangeAdapter; class AdapterData { constructor( @@ -1571,18 +1562,17 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token), undefined); } - // --- on type rename + // --- linked editing - registerOnTypeRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeRenameProvider, wordPattern?: RegExp): vscode.Disposable { - const handle = this._addNewAdapter(new OnTypeRenameAdapter(this._documents, provider), extension); - const serializedWordPattern = wordPattern ? ExtHostLanguageFeatures._serializeRegExp(wordPattern) : undefined; - this._proxy.$registerOnTypeRenameProvider(handle, this._transformDocumentSelector(selector), serializedWordPattern); + registerLinkedEditingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable { + const handle = this._addNewAdapter(new LinkedEditingRangeAdapter(this._documents, provider), extension); + this._proxy.$registerLinkedEditingRangeProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } - $provideOnTypeRenameRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<{ ranges: IRange[]; wordPattern?: extHostProtocol.IRegExpDto; } | undefined> { - return this._withAdapter(handle, OnTypeRenameAdapter, async adapter => { - const res = await adapter.provideOnTypeRenameRanges(URI.revive(resource), position, token); + $provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { + return this._withAdapter(handle, LinkedEditingRangeAdapter, async adapter => { + const res = await adapter.provideLinkedEditingRanges(URI.revive(resource), position, token); if (res) { return { ranges: res.ranges, @@ -1814,7 +1804,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), undefined); } - registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider2): vscode.Disposable { + registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { const handle = this._nextHandle(); const eventHandle = typeof provider.onDidChangeFoldingRanges === 'function' ? this._nextHandle() : undefined; @@ -1823,8 +1813,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF let result = this._createDisposable(handle); if (eventHandle !== undefined) { - checkProposedApiEnabled(extension); - const subscription = provider.onDidChangeFoldingRanges!(_ => this._proxy.$emitFoldingRangeEvent(eventHandle)); + const subscription = provider.onDidChangeFoldingRanges!(() => this._proxy.$emitFoldingRangeEvent(eventHandle)); result = Disposable.from(result, subscription); } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 0846a2d5..bb136d1c 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -214,7 +214,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _proxy: MainThreadNotebookShape; private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape; private readonly _notebookContentProviders = new Map(); - private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map(); private readonly _documents = new ResourceMap(); private readonly _editors = new Map(); @@ -414,6 +413,35 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return callback(provider, document); } + async showNotebookDocument(notebookDocument: vscode.NotebookDocument, options?: vscode.NotebookDocumentShowOptions): Promise { + let resolvedOptions: INotebookDocumentShowOptions; + if (typeof options === 'object') { + resolvedOptions = { + position: typeConverters.ViewColumn.from(options.viewColumn), + preserveFocus: options.preserveFocus, + selection: options.selection, + pinned: typeof options.preview === 'boolean' ? !options.preview : undefined + }; + } else { + resolvedOptions = { + preserveFocus: false + }; + } + + const editorId = await this._proxy.$tryShowNotebookDocument(notebookDocument.uri, notebookDocument.viewType, resolvedOptions); + const editor = editorId && this._editors.get(editorId)?.editor; + + if (editor) { + return editor; + } + + if (editorId) { + throw new Error(`Could NOT open editor for "${notebookDocument.toString()}" because another editor opened in the meantime.`); + } else { + throw new Error(`Could NOT open editor for "${notebookDocument.toString()}".`); + } + } + async $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise { return this._withAdapter(handle, uri, (adapter, document) => { return adapter.provideKernels(document, token); @@ -487,28 +515,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } - async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise { - const document = this._documents.get(URI.revive(uri)); - - if (!document || document.notebookDocument.viewType !== viewType) { - return; - } - - const kernelInfo = this._notebookKernels.get(kernelId); - - if (!kernelInfo) { - return; - } - - const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined; - - if (cell) { - return withToken(token => (kernelInfo!.kernel.executeCell as any)(document.notebookDocument, cell.cell, token)); - } else { - return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document.notebookDocument, token)); - } - } - async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise { const document = this._documents.get(URI.revive(uri)); if (!document) { diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 4adf04af..2f2fd4cd 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -448,7 +448,11 @@ function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light } const dark = getDarkIconUri(iconPath as URI | { light: URI; dark: URI; }); const light = getLightIconUri(iconPath as URI | { light: URI; dark: URI; }); - return { dark, light }; + // Tolerate strings: https://github.com/microsoft/vscode/issues/110432#issuecomment-726144556 + return { + dark: typeof dark === 'string' ? URI.file(dark) : dark, + light: typeof light === 'string' ? URI.file(light) : light + }; } function getLightIconUri(iconPath: URI | { light: URI; dark: URI; }) { diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 83739e74..05806163 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -17,6 +17,7 @@ import { ISplice } from 'vs/base/common/sequence'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type ProviderHandle = number; type GroupHandle = number; @@ -90,6 +91,49 @@ function compareResourceStatesDecorations(a: vscode.SourceControlResourceDecorat return result; } +function compareCommands(a: vscode.Command, b: vscode.Command): number { + if (a.command !== b.command) { + return a.command < b.command ? -1 : 1; + } + + if (a.title !== b.title) { + return a.title < b.title ? -1 : 1; + } + + if (a.tooltip !== b.tooltip) { + if (a.tooltip !== undefined && b.tooltip !== undefined) { + return a.tooltip < b.tooltip ? -1 : 1; + } else if (a.tooltip !== undefined) { + return 1; + } else if (b.tooltip !== undefined) { + return -1; + } + } + + if (a.arguments === b.arguments) { + return 0; + } else if (!a.arguments) { + return -1; + } else if (!b.arguments) { + return 1; + } else if (a.arguments.length !== b.arguments.length) { + return a.arguments.length - b.arguments.length; + } + + for (let i = 0; i < a.arguments.length; i++) { + const aArg = a.arguments[i]; + const bArg = b.arguments[i]; + + if (aArg === bArg) { + continue; + } + + return aArg < bArg ? -1 : 1; + } + + return 0; +} + function compareResourceStates(a: vscode.SourceControlResourceState, b: vscode.SourceControlResourceState): number { let result = comparePaths(a.resourceUri.fsPath, b.resourceUri.fsPath, true); @@ -97,6 +141,18 @@ function compareResourceStates(a: vscode.SourceControlResourceState, b: vscode.S return result; } + if (a.command && b.command) { + result = compareCommands(a.command, b.command); + } else if (a.command) { + return 1; + } else if (b.command) { + return -1; + } + + if (result !== 0) { + return result; + } + if (a.decorations && b.decorations) { result = compareResourceStatesDecorations(a.decorations, b.decorations); } else if (a.decorations) { @@ -166,17 +222,13 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { private _validateInput: IValidateInput | undefined; get validateInput(): IValidateInput | undefined { - if (!this._extension.enableProposedApi) { - throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); - } + checkProposedApiEnabled(this._extension); return this._validateInput; } set validateInput(fn: IValidateInput | undefined) { - if (!this._extension.enableProposedApi) { - throw new Error(`[${this._extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`); - } + checkProposedApiEnabled(this._extension); if (fn && typeof fn !== 'function') { throw new Error(`[${this._extension.identifier.value}]: Invalid SCM input box validation function`); @@ -223,8 +275,9 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG private _resourceHandlePool: number = 0; private _resourceStates: vscode.SourceControlResourceState[] = []; - private _resourceStatesMap: Map = new Map(); - private _resourceStatesCommandsMap: Map = new Map(); + private _resourceStatesMap = new Map(); + private _resourceStatesCommandsMap = new Map(); + private _resourceStatesDisposablesMap = new Map(); private readonly _onDidUpdateResourceStates = new Emitter(); readonly onDidUpdateResourceStates = this._onDidUpdateResourceStates.event; @@ -302,9 +355,16 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG const lightIconUri = r.decorations && getIconResource(r.decorations.light) || iconUri; const darkIconUri = r.decorations && getIconResource(r.decorations.dark) || iconUri; const icons: UriComponents[] = []; + let command: ICommandDto | undefined; if (r.command) { - this._resourceStatesCommandsMap.set(handle, r.command); + if (r.command.command === 'vscode.open' || r.command.command === 'vscode.diff') { + const disposables = new DisposableStore(); + command = this._commands.converter.toInternal(r.command, disposables); + this._resourceStatesDisposablesMap.set(handle, disposables); + } else { + this._resourceStatesCommandsMap.set(handle, r.command); + } } if (lightIconUri) { @@ -320,7 +380,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG const faded = r.decorations && !!r.decorations.faded; const contextValue = r.contextValue || ''; - const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] as SCMRawResource; + const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command] as SCMRawResource; return { rawResource, handle }; }); @@ -340,6 +400,8 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG for (const handle of handlesToDelete) { this._resourceStatesMap.delete(handle); this._resourceStatesCommandsMap.delete(handle); + this._resourceStatesDisposablesMap.get(handle)?.dispose(); + this._resourceStatesDisposablesMap.delete(handle); } } diff --git a/src/vs/workbench/api/common/extHostStatusBar.ts b/src/vs/workbench/api/common/extHostStatusBar.ts index edbaec0f..a89e9fe3 100644 --- a/src/vs/workbench/api/common/extHostStatusBar.ts +++ b/src/vs/workbench/api/common/extHostStatusBar.ts @@ -10,10 +10,21 @@ import { MainContext, MainThreadStatusBarShape, IMainContext, ICommandDto } from import { localize } from 'vs/nls'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export class ExtHostStatusBarEntry implements vscode.StatusBarItem { private static ID_GEN = 0; + private static ALLOWED_BACKGROUND_COLORS = (() => { + const map = new Map(); + + // https://github.com/microsoft/vscode/issues/110214 + map.set('statusBarItem.errorBackground', new ThemeColor('statusBarItem.errorForeground')); + + return map; + })(); + private _id: number; private _alignment: number; private _priority?: number; @@ -26,6 +37,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { private _text: string = ''; private _tooltip?: string; private _color?: string | ThemeColor; + private _backgroundColor?: ThemeColor; private readonly _internalCommandRegistration = new DisposableStore(); private _command?: { readonly fromApi: string | vscode.Command, @@ -36,8 +48,9 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { private _proxy: MainThreadStatusBarShape; private _commands: CommandsConverter; private _accessibilityInformation?: vscode.AccessibilityInformation; + private _extension?: IExtensionDescription; - constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation) { + constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation, extension?: IExtensionDescription) { this._id = ExtHostStatusBarEntry.ID_GEN++; this._proxy = proxy; this._commands = commands; @@ -46,6 +59,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this._alignment = alignment; this._priority = priority; this._accessibilityInformation = accessibilityInformation; + this._extension = extension; } public get id(): number { @@ -72,6 +86,14 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { return this._color; } + public get backgroundColor(): ThemeColor | undefined { + if (this._extension) { + checkProposedApiEnabled(this._extension); + } + + return this._backgroundColor; + } + public get command(): string | vscode.Command | undefined { return this._command?.fromApi; } @@ -95,6 +117,19 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this.update(); } + public set backgroundColor(color: ThemeColor | undefined) { + if (this._extension) { + checkProposedApiEnabled(this._extension); + } + + if (color && !ExtHostStatusBarEntry.ALLOWED_BACKGROUND_COLORS.has(color.id)) { + color = undefined; + } + + this._backgroundColor = color; + this.update(); + } + public set command(command: string | vscode.Command | undefined) { if (this._command?.fromApi === command) { return; @@ -144,9 +179,15 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this._timeoutHandle = setTimeout(() => { this._timeoutHandle = undefined; + // If a background color is set, the foreground is determined + let color = this._color; + if (this._backgroundColor) { + color = ExtHostStatusBarEntry.ALLOWED_BACKGROUND_COLORS.get(this._backgroundColor.id); + } + // Set to status bar - this._proxy.$setEntry(this.id, this._statusId, this._statusName, this.text, this.tooltip, this._command?.internal, this.color, - this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT, + this._proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color, + this._backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT, this._priority, this._accessibilityInformation); }, 0); } @@ -207,8 +248,8 @@ export class ExtHostStatusBar { this._statusMessage = new StatusBarMessage(this); } - createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation): vscode.StatusBarItem { - return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation); + createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation, extension?: IExtensionDescription): vscode.StatusBarItem { + return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation, extension); } setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): Disposable { diff --git a/src/vs/workbench/api/common/extHostStorage.ts b/src/vs/workbench/api/common/extHostStorage.ts index 0c38bb51..01712256 100644 --- a/src/vs/workbench/api/common/extHostStorage.ts +++ b/src/vs/workbench/api/common/extHostStorage.ts @@ -7,7 +7,7 @@ import { MainContext, MainThreadStorageShape, ExtHostStorageShape } from './extH import { Emitter } from 'vs/base/common/event'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync'; export interface IStorageChangeEvent { shared: boolean; diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index f0bb1dfa..9b0753a5 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -188,6 +188,10 @@ export namespace CustomExecutionDTO { customExecution: 'customExecution' }; } + + export function to(taskId: string, providedCustomExeutions: Map): types.CustomExecution | undefined { + return providedCustomExeutions.get(taskId); + } } @@ -274,15 +278,17 @@ export namespace TaskDTO { }; return result; } - export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise { + export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider, providedCustomExeutions: Map): Promise { if (value === undefined || value === null) { return undefined; } - let execution: types.ShellExecution | types.ProcessExecution | undefined; + let execution: types.ShellExecution | types.ProcessExecution | types.CustomExecution | undefined; if (ProcessExecutionDTO.is(value.execution)) { execution = ProcessExecutionDTO.to(value.execution); } else if (ShellExecutionDTO.is(value.execution)) { execution = ShellExecutionDTO.to(value.execution); + } else if (CustomExecutionDTO.is(value.execution)) { + execution = CustomExecutionDTO.to(value._id, providedCustomExeutions); } const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition); let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined; @@ -354,13 +360,6 @@ class TaskExecutionImpl implements vscode.TaskExecution { } export namespace TaskExecutionDTO { - export async function to(value: tasks.TaskExecutionDTO, tasks: ExtHostTaskBase, workspaceProvider: IExtHostWorkspaceProvider): Promise { - const task = await TaskDTO.to(value.task, workspaceProvider); - if (!task) { - throw new Error('Unexpected: Task cannot be created.'); - } - return new TaskExecutionImpl(tasks, value.id, task); - } export function from(value: vscode.TaskExecution): tasks.TaskExecutionDTO { return { id: (value as TaskExecutionImpl)._id, @@ -447,7 +446,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => { const result: vscode.Task[] = []; for (let value of values) { - const task = await TaskDTO.to(value, this._workspaceProvider); + const task = await TaskDTO.to(value, this._workspaceProvider, this._providedCustomExecutions2); if (task) { result.push(task); } @@ -573,7 +572,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`); } - const task = await TaskDTO.to(taskDTO, this._workspaceProvider); + const task = await TaskDTO.to(taskDTO, this._workspaceProvider, this._providedCustomExecutions2); if (!task) { throw new Error('Unexpected: Task cannot be resolved.'); } @@ -631,7 +630,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask return result; } const createdResult: Promise = new Promise(async (resolve, reject) => { - const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider); + const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider, this._providedCustomExecutions2); if (!taskToCreate) { reject('Unexpected: Task does not exist.'); } else { @@ -705,6 +704,10 @@ export class WorkerExtHostTask extends ExtHostTaskBase { } public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { + if (!task.execution) { + throw new Error('Tasks to execute must include an execution'); + } + const dto = TaskDTO.from(task, extension); if (dto === undefined) { throw new Error('Task is not valid'); diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts new file mode 100644 index 00000000..02fc32cb --- /dev/null +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -0,0 +1,794 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { mapFind } from 'vs/base/common/arrays'; +import { disposableTimeout } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { throttle } from 'vs/base/common/decorators'; +import { Emitter } from 'vs/base/common/event'; +import { once } from 'vs/base/common/functional'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { TestItem } from 'vs/workbench/api/common/extHostTypeConverters'; +import { Disposable, RequiredTestItem } from 'vs/workbench/api/common/extHostTypes'; +import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import type * as vscode from 'vscode'; + +const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`; + +export class ExtHostTesting implements ExtHostTestingShape { + private readonly providers = new Map(); + private readonly proxy: MainThreadTestingShape; + private readonly ownedTests = new OwnedTestCollection(); + private readonly testSubscriptions = new Map(); + + private workspaceObservers: WorkspaceFolderTestObserverFactory; + private textDocumentObservers: TextDocumentTestObserverFactory; + + constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) { + this.proxy = rpc.getProxy(MainContext.MainThreadTesting); + this.workspaceObservers = new WorkspaceFolderTestObserverFactory(this.proxy); + this.textDocumentObservers = new TextDocumentTestObserverFactory(this.proxy, documents); + } + + /** + * Implements vscode.test.registerTestProvider + */ + public registerTestProvider(provider: vscode.TestProvider): vscode.Disposable { + const providerId = generateUuid(); + this.providers.set(providerId, provider); + this.proxy.$registerTestProvider(providerId); + + return new Disposable(() => { + this.providers.delete(providerId); + this.proxy.$unregisterTestProvider(providerId); + }); + } + + /** + * Implements vscode.test.createTextDocumentTestObserver + */ + public createTextDocumentTestObserver(document: vscode.TextDocument) { + return this.textDocumentObservers.checkout(document.uri); + } + + /** + * Implements vscode.test.createWorkspaceTestObserver + */ + public createWorkspaceTestObserver(workspaceFolder: vscode.WorkspaceFolder) { + return this.workspaceObservers.checkout(workspaceFolder.uri); + } + + /** + * Implements vscode.test.runTests + */ + public async runTests(req: vscode.TestRunOptions) { + await this.proxy.$runTests({ + tests: req.tests + // Find workspace items first, then owned tests, then document tests. + // If a test instance exists in both the workspace and document, prefer + // the workspace because it's less ephemeral. + .map(test => this.workspaceObservers.getMirroredTestDataByReference(test) + ?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test)) + ?? this.textDocumentObservers.getMirroredTestDataByReference(test)) + .filter(isDefined) + .map(item => ({ providerId: item.providerId, testId: item.id })), + debug: req.debug + }); + } + + /** + * Handles a request to read tests for a file, or workspace. + * @override + */ + public $subscribeToTests(resource: ExtHostTestingResource, uriComponents: UriComponents) { + const uri = URI.revive(uriComponents); + const subscriptionKey = getTestSubscriptionKey(resource, uri); + if (this.testSubscriptions.has(subscriptionKey)) { + return; + } + + let method: undefined | ((p: vscode.TestProvider) => vscode.TestHierarchy | undefined); + if (resource === ExtHostTestingResource.TextDocument) { + const document = this.documents.getDocument(uri); + if (document) { + method = p => p.createDocumentTestHierarchy?.(document.document); + } + } else { + const folder = this.workspace.getWorkspaceFolder(uri, false); + if (folder) { + method = p => p.createWorkspaceTestHierarchy?.(folder); + } + } + + if (!method) { + return; + } + + const disposable = new DisposableStore(); + const collection = disposable.add(this.ownedTests.createForHierarchy(diff => this.proxy.$publishDiff(resource, uriComponents, diff))); + for (const [id, provider] of this.providers) { + try { + const hierarchy = method(provider); + if (!hierarchy) { + continue; + } + + disposable.add(hierarchy); + collection.addRoot(hierarchy.root, id); + hierarchy.onDidChangeTest(e => collection.onItemChange(e, id)); + } catch (e) { + console.error(e); + } + } + + this.testSubscriptions.set(subscriptionKey, { store: disposable, collection }); + } + + /** + * Disposes of a previous subscription to tests. + * @override + */ + public $unsubscribeFromTests(resource: ExtHostTestingResource, uriComponents: UriComponents) { + const uri = URI.revive(uriComponents); + const subscriptionKey = getTestSubscriptionKey(resource, uri); + this.testSubscriptions.get(subscriptionKey)?.store.dispose(); + this.testSubscriptions.delete(subscriptionKey); + } + + /** + * Receives a test update from the main thread. Called (eventually) whenever + * tests change. + * @override + */ + public $acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void { + if (resource === ExtHostTestingResource.TextDocument) { + this.textDocumentObservers.acceptDiff(URI.revive(uri), diff); + } else { + this.workspaceObservers.acceptDiff(URI.revive(uri), diff); + } + } + + /** + * Runs tests with the given set of IDs. Allows for test from multiple + * providers to be run. + * @override + */ + public async $runTestsForProvider(req: RunTestForProviderRequest): Promise { + const provider = this.providers.get(req.providerId); + if (!provider || !provider.runTests) { + return EMPTY_TEST_RESULT; + } + + const tests = req.ids.map(id => this.ownedTests.getTestById(id)?.actual).filter(isDefined); + if (!tests.length) { + return EMPTY_TEST_RESULT; + } + + await provider.runTests({ tests, debug: req.debug }, CancellationToken.None); + return EMPTY_TEST_RESULT; + } +} + +const keyMap: { [K in keyof Omit]: null } = { + label: null, + location: null, + state: null, + debuggable: null, + description: null, + runnable: null +}; + +const simpleProps = Object.keys(keyMap) as ReadonlyArray; + +const itemEqualityComparator = (a: vscode.TestItem) => { + const values: unknown[] = []; + for (const prop of simpleProps) { + values.push(a[prop]); + } + + return (b: vscode.TestItem) => { + for (let i = 0; i < simpleProps.length; i++) { + if (values[i] !== b[simpleProps[i]]) { + return false; + } + } + + return true; + }; +}; + +/** + * @private + */ +export interface OwnedCollectionTestItem extends InternalTestItem { + actual: vscode.TestItem; + previousChildren: Set; + previousEquals: (v: vscode.TestItem) => boolean; +} + +/** + * @private + */ +export class OwnedTestCollection { + protected readonly testIdToInternal = new Map(); + + /** + * Gets test information by ID, if it was defined and still exists in this + * extension host. + */ + public getTestById(id: string) { + return this.testIdToInternal.get(id); + } + + /** + * Creates a new test collection for a specific hierarchy for a workspace + * or document observation. + */ + public createForHierarchy(publishDiff: (diff: TestsDiff) => void = () => undefined) { + return new SingleUseTestCollection(this.testIdToInternal, publishDiff); + } +} + +/** + * Maintains tests created and registered for a single set of hierarchies + * for a workspace or document. + * @private + */ +export class SingleUseTestCollection implements IDisposable { + protected readonly testItemToInternal = new Map(); + protected diff: TestsDiff = []; + private disposed = false; + + constructor(private readonly testIdToInternal: Map, private readonly publishDiff: (diff: TestsDiff) => void) { } + + /** + * Adds a new root node to the collection. + */ + public addRoot(item: vscode.TestItem, providerId: string) { + this.addItem(item, providerId, null); + this.throttleSendDiff(); + } + + /** + * Gets test information by its reference, if it was defined and still exists + * in this extension host. + */ + public getTestByReference(item: vscode.TestItem) { + return this.testItemToInternal.get(item); + } + + /** + * Should be called when an item change is fired on the test provider. + */ + public onItemChange(item: vscode.TestItem, providerId: string) { + const existing = this.testItemToInternal.get(item); + if (!existing) { + if (!this.disposed) { + console.warn(`Received a TestProvider.onDidChangeTest for a test that wasn't seen before as a child.`); + } + return; + } + + this.addItem(item, providerId, existing.parent); + this.throttleSendDiff(); + } + + /** + * Gets a diff of all changes that have been made, and clears the diff queue. + */ + public collectDiff() { + const diff = this.diff; + this.diff = []; + return diff; + } + + public dispose() { + for (const item of this.testItemToInternal.values()) { + this.testIdToInternal.delete(item.id); + } + + this.testIdToInternal.clear(); + this.diff = []; + this.disposed = true; + } + + protected getId(): string { + return generateUuid(); + } + + private addItem(actual: vscode.TestItem, providerId: string, parent: string | null) { + let internal = this.testItemToInternal.get(actual); + if (!internal) { + internal = { + actual, + id: this.getId(), + parent, + item: TestItem.from(actual), + providerId, + previousChildren: new Set(), + previousEquals: itemEqualityComparator(actual), + }; + + this.testItemToInternal.set(actual, internal); + this.testIdToInternal.set(internal.id, internal); + this.diff.push([TestDiffOpType.Add, { id: internal.id, parent, providerId, item: internal.item }]); + } else if (!internal.previousEquals(actual)) { + internal.item = TestItem.from(actual); + internal.previousEquals = itemEqualityComparator(actual); + this.diff.push([TestDiffOpType.Update, { id: internal.id, parent, providerId, item: internal.item }]); + } + + // If there are children, track which ones are deleted + // and recursively and/update them. + if (actual.children) { + const deletedChildren = internal.previousChildren; + const currentChildren = new Set(); + for (const child of actual.children) { + const c = this.addItem(child, providerId, internal.id); + deletedChildren.delete(c.id); + currentChildren.add(c.id); + } + + for (const child of deletedChildren) { + this.removeItembyId(child); + } + + internal.previousChildren = currentChildren; + } + + + return internal; + } + + private removeItembyId(id: string) { + this.diff.push([TestDiffOpType.Remove, id]); + + const queue = [this.testIdToInternal.get(id)]; + while (queue.length) { + const item = queue.pop(); + if (!item) { + continue; + } + + this.testIdToInternal.delete(item.id); + this.testItemToInternal.delete(item.actual); + for (const child of item.previousChildren) { + queue.push(this.testIdToInternal.get(child)); + } + } + } + + @throttle(200) + protected throttleSendDiff() { + const diff = this.collectDiff(); + if (diff.length) { + this.publishDiff(diff); + } + } +} + +/** + * @private + */ +interface MirroredCollectionTestItem extends IncrementalTestCollectionItem { + revived: vscode.TestItem; + depth: number; + wrapped?: vscode.TestItem; +} + +class MirroredChangeCollector extends IncrementalChangeCollector { + private readonly added = new Set(); + private readonly updated = new Set(); + private readonly removed = new Set(); + + private readonly alreadyRemoved = new Set(); + + public get isEmpty() { + return this.added.size === 0 && this.removed.size === 0 && this.updated.size === 0; + } + + constructor(private readonly collection: MirroredTestCollection, private readonly emitter: Emitter) { + super(); + } + + /** + * @override + */ + public add(node: MirroredCollectionTestItem): void { + this.added.add(node); + } + + /** + * @override + */ + public update(node: MirroredCollectionTestItem): void { + Object.assign(node.revived, TestItem.to(node.item)); + if (!this.added.has(node)) { + this.updated.add(node); + } + } + + /** + * @override + */ + public remove(node: MirroredCollectionTestItem): void { + if (this.added.has(node)) { + this.added.delete(node); + return; + } + + this.updated.delete(node); + + if (node.parent && this.alreadyRemoved.has(node.parent)) { + this.alreadyRemoved.add(node.id); + return; + } + + this.removed.add(node); + } + + /** + * @override + */ + public getChangeEvent(): vscode.TestChangeEvent { + const { collection, added, updated, removed } = this; + return { + get added() { return [...added].map(collection.getPublicTestItem, collection); }, + get updated() { return [...updated].map(collection.getPublicTestItem, collection); }, + get removed() { return [...removed].map(collection.getPublicTestItem, collection); }, + get commonChangeAncestor() { + let ancestorPath: MirroredCollectionTestItem[] | undefined; + const buildAncestorPath = (node: MirroredCollectionTestItem | undefined) => { + if (!node) { + return undefined; + } + + // add the node and all its parents to the list of ancestors. If + // the node is detached, do not return a path (its parent will + // also have been passed to remove() and be present) + const path: MirroredCollectionTestItem[] = new Array(node.depth + 1); + for (let i = node.depth; i >= 0; i--) { + if (!node) { + return undefined; // detached child + } + + path[node.depth] = node; + node = node.parent ? collection.getMirroredTestDataById(node.parent) : undefined; + } + + return path; + }; + + const addAncestorPath = (node: MirroredCollectionTestItem) => { + // fast path: if the common ancestor is already the root, no more work to do + if (ancestorPath && ancestorPath.length === 0) { + return; + } + + const thisPath = buildAncestorPath(node); + if (!thisPath) { + return; + } + + if (!ancestorPath) { + ancestorPath = thisPath; + return; + } + + // removes node from the path to the ancestor that don't match + // the corresponding node in *this* path. + for (let i = ancestorPath.length - 1; i >= 0; i--) { + if (ancestorPath[i] !== thisPath[i]) { + ancestorPath.pop(); + } + } + }; + + const addParentAncestor = (node: MirroredCollectionTestItem) => { + if (ancestorPath && ancestorPath.length === 0) { + // no-op + } else if (node.parent === null) { + ancestorPath = []; + } else { + const parent = collection.getMirroredTestDataById(node.parent); + if (parent) { + addAncestorPath(parent); + } + } + }; + + for (const node of added) { addParentAncestor(node); } + for (const node of updated) { addAncestorPath(node); } + for (const node of removed) { addParentAncestor(node); } + + const ancestor = ancestorPath && ancestorPath[ancestorPath.length - 1]; + return ancestor ? collection.getPublicTestItem(ancestor) : null; + }, + }; + } + + public complete() { + if (!this.isEmpty) { + this.emitter.fire(this.getChangeEvent()); + } + } +} + +/** + * Maintains tests in this extension host sent from the main thread. + * @private + */ +export class MirroredTestCollection extends AbstractIncrementalTestCollection { + private changeEmitter = new Emitter(); + + /** + * Change emitter that fires with the same sematics as `TestObserver.onDidChangeTests`. + */ + public readonly onDidChangeTests = this.changeEmitter.event; + + /** + * Gets a list of root test items. + */ + public get rootTestItems() { + return this.getAllAsTestItem([...this.roots]); + } + + /** + * Translates the item IDs to TestItems for exposure to extensions. + */ + public getAllAsTestItem(itemIds: Iterable): vscode.TestItem[] { + let output: vscode.TestItem[] = []; + for (const itemId of itemIds) { + const item = this.items.get(itemId); + if (item) { + output.push(this.getPublicTestItem(item)); + } + } + + return output; + } + + /** + * + * If the test ID exists, returns its underlying ID. + */ + public getMirroredTestDataById(itemId: string) { + return this.items.get(itemId); + } + + /** + * If the test item is a mirrored test item, returns its underlying ID. + */ + public getMirroredTestDataByReference(item: vscode.TestItem) { + const id = getMirroredItemId(item); + return id ? this.items.get(id) : undefined; + } + + /** + * @override + */ + protected createItem(item: InternalTestItem, parent?: MirroredCollectionTestItem): MirroredCollectionTestItem { + return { ...item, revived: TestItem.to(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() }; + } + + /** + * @override + */ + protected createChangeCollector() { + return new MirroredChangeCollector(this, this.changeEmitter); + } + + /** + * Gets the public test item instance for the given mirrored record. + */ + public getPublicTestItem(item: MirroredCollectionTestItem): vscode.TestItem { + if (!item.wrapped) { + item.wrapped = new ExtHostTestItem(item, this); + } + + return item.wrapped; + } +} + +const getMirroredItemId = (item: vscode.TestItem) => { + return (item as any)[MirroredItemId] as string | undefined; +}; + +const MirroredItemId = Symbol('MirroredItemId'); + +class ExtHostTestItem implements vscode.TestItem, RequiredTestItem { + readonly #internal: MirroredCollectionTestItem; + readonly #collection: MirroredTestCollection; + + public get label() { return this.#internal.revived.label; } + public get description() { return this.#internal.revived.description; } + public get state() { return this.#internal.revived.state; } + public get location() { return this.#internal.revived.location; } + public get runnable() { return this.#internal.revived.runnable ?? true; } + public get debuggable() { return this.#internal.revived.debuggable ?? false; } + public get children() { + return this.#collection.getAllAsTestItem(this.#internal.children); + } + + get [MirroredItemId]() { return this.#internal.id; } + + constructor(internal: MirroredCollectionTestItem, collection: MirroredTestCollection) { + this.#internal = internal; + this.#collection = collection; + } + + public toJSON() { + const serialized: RequiredTestItem = { + label: this.label, + description: this.description, + state: this.state, + location: this.location, + runnable: this.runnable, + debuggable: this.debuggable, + children: this.children.map(c => (c as ExtHostTestItem).toJSON()), + }; + + return serialized; + } +} + +interface IObserverData { + observers: number; + tests: MirroredTestCollection; + listener: IDisposable; + pendingDeletion?: IDisposable; +} + +abstract class AbstractTestObserverFactory { + private readonly resources = new Map(); + + public checkout(resourceUri: URI): vscode.TestObserver { + const resourceKey = resourceUri.toString(); + const resource = this.resources.get(resourceKey) ?? this.createObserverData(resourceUri); + + resource.observers++; + + return { + onDidChangeTest: resource.tests.onDidChangeTests, + onDidDiscoverInitialTests: new Emitter().event, // todo@connor4312 + get tests() { + return resource.tests.rootTestItems; + }, + dispose: once(() => { + if (!--resource.observers) { + resource.pendingDeletion = this.eventuallyDispose(resourceUri); + } + }), + }; + } + + /** + * Gets the internal test data by its reference, in any observer. + */ + public getMirroredTestDataByReference(ref: vscode.TestItem) { + for (const { tests } of this.resources.values()) { + const v = tests.getMirroredTestDataByReference(ref); + if (v) { + return v; + } + } + + return undefined; + } + + /** + * Called when no observers are listening for the resource any more. Should + * defer unlistening on the resource, and return a disposiable + * to halt the process in case new listeners come in. + */ + protected eventuallyDispose(resourceUri: URI) { + return disposableTimeout(() => this.unlisten(resourceUri), 10 * 1000); + } + + /** + * Starts listening to test information for the given resource. + */ + protected abstract listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void): Disposable; + + private createObserverData(resourceUri: URI): IObserverData { + const tests = new MirroredTestCollection(); + const listener = this.listen(resourceUri, diff => tests.apply(diff)); + const data: IObserverData = { observers: 0, tests, listener }; + this.resources.set(resourceUri.toString(), data); + return data; + } + + /** + * Called when a resource is no longer in use. + */ + protected unlisten(resourceUri: URI) { + const key = resourceUri.toString(); + const resource = this.resources.get(key); + if (resource) { + resource.observers = -1; + resource.pendingDeletion?.dispose(); + resource.listener.dispose(); + this.resources.delete(key); + } + } +} + +class WorkspaceFolderTestObserverFactory extends AbstractTestObserverFactory { + private diffListeners = new Map void>(); + + constructor(private readonly proxy: MainThreadTestingShape) { + super(); + } + + /** + * Publishees the diff for the workspace folder with the given uri. + */ + public acceptDiff(resourceUri: URI, diff: TestsDiff) { + this.diffListeners.get(resourceUri.toString())?.(diff); + } + + /** + * @override + */ + public listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void) { + this.proxy.$subscribeToDiffs(ExtHostTestingResource.Workspace, resourceUri); + + const uriString = resourceUri.toString(); + this.diffListeners.set(uriString, onDiff); + + return new Disposable(() => { + this.proxy.$unsubscribeFromDiffs(ExtHostTestingResource.Workspace, resourceUri); + this.diffListeners.delete(uriString); + }); + } +} + +class TextDocumentTestObserverFactory extends AbstractTestObserverFactory { + private diffListeners = new Map void>(); + + constructor(private readonly proxy: MainThreadTestingShape, private documents: IExtHostDocumentsAndEditors) { + super(); + } + + /** + * Publishees the diff for the document with the given uri. + */ + public acceptDiff(resourceUri: URI, diff: TestsDiff) { + this.diffListeners.get(resourceUri.toString())?.(diff); + } + + /** + * @override + */ + public listen(resourceUri: URI, onDiff: (diff: TestsDiff) => void) { + const document = this.documents.getDocument(resourceUri); + if (!document) { + return new Disposable(() => undefined); + } + + const uriString = resourceUri.toString(); + this.diffListeners.set(uriString, onDiff); + + const disposeListener = this.documents.onDidRemoveDocuments(evt => { + if (evt.some(delta => delta.document.uri.toString() === uriString)) { + this.unlisten(resourceUri); + } + }); + + this.proxy.$subscribeToDiffs(ExtHostTestingResource.TextDocument, resourceUri); + return new Disposable(() => { + this.proxy.$unsubscribeFromDiffs(ExtHostTestingResource.TextDocument, resourceUri); + disposeListener.dispose(); + this.diffListeners.delete(uriString); + }); + } +} diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index d7194fc2..63d30701 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -17,7 +17,6 @@ import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringTy import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters'; import { IMarkdownString } from 'vs/base/common/htmlContent'; @@ -33,7 +32,6 @@ function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeIte if (label && typeof label === 'object' && typeof label.label === 'string') { - checkProposedApiEnabled(extension); let highlights: [number, number][] | undefined = undefined; if (Array.isArray(label.highlights)) { highlights = (<[number, number][]>label.highlights).filter((highlight => highlight.length === 2 && typeof highlight[0] === 'number' && typeof highlight[1] === 'number')); @@ -183,7 +181,7 @@ type TreeData = { message: boolean, element: T | Root | false }; interface TreeNode extends IDisposable { item: ITreeItem; - extensionItem: vscode.TreeItem2; + extensionItem: vscode.TreeItem; parent: TreeNode | Root; children?: TreeNode[]; } @@ -293,7 +291,7 @@ class ExtHostTreeView extends Disposable { return this.elements.get(treeItemHandle); } - reveal(element: T, options?: IRevealOptions): Promise { + reveal(element: T | undefined, options?: IRevealOptions): Promise { options = options ? options : { select: true, focus: false }; const select = isUndefinedOrNull(options.select) ? true : options.select; const focus = isUndefinedOrNull(options.focus) ? false : options.focus; @@ -302,10 +300,15 @@ class ExtHostTreeView extends Disposable { if (typeof this.dataProvider.getParent !== 'function') { return Promise.reject(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`)); } - return this.refreshPromise - .then(() => this.resolveUnknownParentChain(element)) - .then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1]) - .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus, expand })), error => this.logService.error(error)); + + if (element) { + return this.refreshPromise + .then(() => this.resolveUnknownParentChain(element)) + .then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1]) + .then(treeNode => this.proxy.$reveal(this.viewId, { item: treeNode.item, parentChain: parentChain.map(p => p.item) }, { select, focus, expand })), error => this.logService.error(error)); + } else { + return this.proxy.$reveal(this.viewId, undefined, { select, focus, expand }); + } } private _message: string = ''; @@ -375,7 +378,7 @@ class ExtHostTreeView extends Disposable { if (element) { const node = this.nodes.get(element); if (node) { - const resolve = await this.dataProvider.resolveTreeItem(element, node.extensionItem); + const resolve = await this.dataProvider.resolveTreeItem(node.extensionItem, element) ?? node.extensionItem; // Resolvable elements. Currently only tooltip. node.item.tooltip = this.getTooltip(resolve.tooltip); return node.item; @@ -565,13 +568,12 @@ class ExtHostTreeView extends Disposable { private getTooltip(tooltip?: string | vscode.MarkdownString): string | IMarkdownString | undefined { if (MarkdownStringType.isMarkdownString(tooltip)) { - checkProposedApiEnabled(this.extension); return MarkdownString.from(tooltip); } return tooltip; } - private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem2, parent: TreeNode | Root): TreeNode { + private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode { const disposable = new DisposableStore(); const handle = this.createHandle(element, extensionTreeItem, parent); const icon = this.getLightIconPath(extensionTreeItem); @@ -600,7 +602,7 @@ class ExtHostTreeView extends Disposable { }; } - private getThemeIcon(extensionTreeItem: vscode.TreeItem2): ThemeIcon | undefined { + private getThemeIcon(extensionTreeItem: vscode.TreeItem): ThemeIcon | undefined { return extensionTreeItem.iconPath instanceof ThemeIcon ? extensionTreeItem.iconPath : undefined; } diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index ca249267..29a50ae2 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -6,10 +6,11 @@ import { ExtHostTunnelServiceShape, MainContext, MainThreadTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import * as vscode from 'vscode'; -import { RemoteTunnel, TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export interface TunnelDto { remoteAddress: { port: number, host: string }; @@ -32,7 +33,7 @@ export interface Tunnel extends vscode.Disposable { export interface IExtHostTunnelService extends ExtHostTunnelServiceShape { readonly _serviceBrand: undefined; - openTunnel(forward: TunnelOptions): Promise; + openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise; getTunnels(): Promise; onDidChangeTunnels: vscode.Event; setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise; @@ -51,23 +52,17 @@ export class ExtHostTunnelService implements IExtHostTunnelService { this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService); } - async openTunnel(forward: TunnelOptions): Promise { + async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise { return undefined; } async getTunnels(): Promise { return []; } - async $findCandidatePorts(): Promise<{ host: string, port: number; detail: string; }[]> { - return []; - } - async $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise { - return candidates.map(() => true); - } async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise { await this._proxy.$tunnelServiceReady(); return { dispose: () => { } }; } - $forwardPort(tunnelOptions: TunnelOptions): Promise | undefined { return undefined; } + $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined { return undefined; } async $closeTunnel(remote: { host: string, port: number }): Promise { } async $onDidTunnelsChange(): Promise { } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c0eb2a21..d80dfae4 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -7,13 +7,12 @@ import * as modes from 'vs/editor/common/modes'; import * as types from './extHostTypes'; import * as search from 'vs/workbench/contrib/search/common/search'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; import type * as vscode from 'vscode'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; -import { SaveReason } from 'vs/workbench/common/editor'; +import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor'; import { IPosition } from 'vs/editor/common/core/position'; import * as editorRange from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; @@ -33,6 +32,7 @@ import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { CellOutputKind, IDisplayOutput, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; export interface PositionLike { line: number; @@ -220,7 +220,7 @@ export namespace DiagnosticSeverity { } export namespace ViewColumn { - export function from(column?: vscode.ViewColumn): EditorViewColumn { + export function from(column?: vscode.ViewColumn): EditorGroupColumn { if (typeof column === 'number' && column >= types.ViewColumn.One) { return column - 1; // adjust zero index (ViewColumn.ONE => 0) } @@ -232,12 +232,12 @@ export namespace ViewColumn { return ACTIVE_GROUP; // default is always the active group } - export function to(position: EditorViewColumn): vscode.ViewColumn { + export function to(position: EditorGroupColumn): vscode.ViewColumn { if (typeof position === 'number' && position >= 0) { return position + 1; // adjust to index (ViewColumn.ONE => 1) } - throw new Error(`invalid 'EditorViewColumn'`); + throw new Error(`invalid 'EditorGroupColumn'`); } } @@ -274,8 +274,8 @@ export namespace MarkdownString { if (isCodeblock(markup)) { const { language, value } = markup; res = { value: '```' + language + '\n' + value + '\n```\n' }; - } else if (htmlContent.isMarkdownString(markup)) { - res = markup; + } else if (types.MarkdownString.isMarkdownString(markup)) { + res = { value: markup.value, isTrusted: markup.isTrusted, supportThemeIcons: markup.supportThemeIcons }; } else if (typeof markup === 'string') { res = { value: markup }; } else { @@ -343,7 +343,7 @@ export namespace MarkdownString { return result; } - export function fromStrict(value: string | types.MarkdownString): undefined | string | htmlContent.IMarkdownString { + export function fromStrict(value: string | vscode.MarkdownString): undefined | string | htmlContent.IMarkdownString { if (!value) { return undefined; } @@ -1183,6 +1183,7 @@ export namespace FoldingRangeKind { export interface TextEditorOpenOptions extends vscode.TextDocumentShowOptions { background?: boolean; + override?: boolean; } export namespace TextEditorOpenOptions { @@ -1194,6 +1195,7 @@ export namespace TextEditorOpenOptions { inactive: options.background, preserveFocus: options.preserveFocus, selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined, + override: typeof options.override === 'boolean' ? false : undefined }; } @@ -1253,50 +1255,6 @@ export namespace LanguageSelector { } } -export namespace LogLevel { - export function from(extLevel: types.LogLevel): _MainLogLevel { - switch (extLevel) { - case types.LogLevel.Trace: - return _MainLogLevel.Trace; - case types.LogLevel.Debug: - return _MainLogLevel.Debug; - case types.LogLevel.Info: - return _MainLogLevel.Info; - case types.LogLevel.Warning: - return _MainLogLevel.Warning; - case types.LogLevel.Error: - return _MainLogLevel.Error; - case types.LogLevel.Critical: - return _MainLogLevel.Critical; - case types.LogLevel.Off: - return _MainLogLevel.Off; - default: - return _MainLogLevel.Info; - } - } - - export function to(mainLevel: _MainLogLevel): types.LogLevel { - switch (mainLevel) { - case _MainLogLevel.Trace: - return types.LogLevel.Trace; - case _MainLogLevel.Debug: - return types.LogLevel.Debug; - case _MainLogLevel.Info: - return types.LogLevel.Info; - case _MainLogLevel.Warning: - return types.LogLevel.Warning; - case _MainLogLevel.Error: - return types.LogLevel.Error; - case _MainLogLevel.Critical: - return types.LogLevel.Critical; - case _MainLogLevel.Off: - return types.LogLevel.Off; - default: - return types.LogLevel.Info; - } - } -} - export namespace NotebookCellOutput { export function from(output: types.NotebookCellOutput): IDisplayOutput { return output.toJSON(); @@ -1395,3 +1353,64 @@ export namespace NotebookDecorationRenderOptions { }; } } + +export namespace TestState { + export function from(item: vscode.TestState): ITestState { + return { + runState: item.runState, + duration: item.duration, + messages: item.messages.map(message => ({ + message: MarkdownString.fromStrict(message.message) || '', + severity: message.severity, + expectedOutput: message.expectedOutput, + actualOutput: message.actualOutput, + location: message.location ? location.from(message.location) : undefined, + })), + }; + } + + export function to(item: ITestState): vscode.TestState { + return new types.TestState( + item.runState, + item.messages.map(message => ({ + message: typeof message.message === 'string' ? message.message : MarkdownString.to(message.message), + severity: message.severity, + expectedOutput: message.expectedOutput, + actualOutput: message.actualOutput, + location: message.location && location.to({ + range: message.location.range, + uri: URI.revive(message.location.uri) + }), + })), + item.duration, + ); + } +} + + +export namespace TestItem { + export function from(item: vscode.TestItem): ITestItem { + return { + label: item.label, + location: item.location ? location.from(item.location) : undefined, + debuggable: item.debuggable, + description: item.description, + runnable: item.runnable, + state: TestState.from(item.state), + }; + } + + export function to(item: ITestItem): vscode.TestItem { + return { + label: item.label, + location: item.location && location.to({ + range: item.location.range, + uri: URI.revive(item.location.uri) + }), + debuggable: item.debuggable, + description: item.description, + runnable: item.runnable, + state: TestState.to(item.state), + }; + } +} diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 6eb858ee..069c5658 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { coalesceInPlace, equals } from 'vs/base/common/arrays'; -import { escapeCodicons } from 'vs/base/common/codicons'; import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; -import { isMarkdownString } from 'vs/base/common/htmlContent'; +import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent'; import { ResourceMap } from 'vs/base/common/map'; import { isStringArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -1276,54 +1275,10 @@ export class CodeLens { } } - -export class CodeInset { - - range: Range; - height?: number; - - constructor(range: Range, height?: number) { - this.range = range; - this.height = height; - } -} - - @es5ClassCompat -export class MarkdownString { +export class MarkdownString implements vscode.MarkdownString { - value: string; - isTrusted?: boolean; - readonly supportThemeIcons?: boolean; - - constructor(value?: string, supportThemeIcons: boolean = false) { - this.value = value ?? ''; - this.supportThemeIcons = supportThemeIcons; - } - - appendText(value: string): MarkdownString { - // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) - .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') - .replace(/\n/, '\n\n'); - - return this; - } - - appendMarkdown(value: string): MarkdownString { - this.value += value; - - return this; - } - - appendCodeblock(code: string, language: string = ''): MarkdownString { - this.value += '\n```'; - this.value += language; - this.value += '\n'; - this.value += code; - this.value += '\n```\n'; - return this; - } + readonly #delegate: BaseMarkdownString; static isMarkdownString(thing: any): thing is vscode.MarkdownString { if (thing instanceof MarkdownString) { @@ -1331,15 +1286,55 @@ export class MarkdownString { } return thing && thing.appendCodeblock && thing.appendMarkdown && thing.appendText && (thing.value !== undefined); } + + constructor(value?: string, supportThemeIcons: boolean = false) { + this.#delegate = new BaseMarkdownString(value, { supportThemeIcons }); + } + + get value(): string { + return this.#delegate.value; + } + set value(value: string) { + this.#delegate.value = value; + } + + get isTrusted(): boolean | undefined { + return this.#delegate.isTrusted; + } + + set isTrusted(value: boolean | undefined) { + this.#delegate.isTrusted = value; + } + + get supportThemeIcons(): boolean | undefined { + return this.#delegate.supportThemeIcons; + } + + appendText(value: string): vscode.MarkdownString { + this.#delegate.appendText(value); + return this; + } + + appendMarkdown(value: string): vscode.MarkdownString { + this.#delegate.appendMarkdown(value); + return this; + } + + appendCodeblock(value: string, language?: string): vscode.MarkdownString { + this.#delegate.appendCodeblock(language ?? '', value); + return this; + } + + } @es5ClassCompat export class ParameterInformation { label: string | [number, number]; - documentation?: string | MarkdownString; + documentation?: string | vscode.MarkdownString; - constructor(label: string | [number, number], documentation?: string | MarkdownString) { + constructor(label: string | [number, number], documentation?: string | vscode.MarkdownString) { this.label = label; this.documentation = documentation; } @@ -1349,11 +1344,11 @@ export class ParameterInformation { export class SignatureInformation { label: string; - documentation?: string | MarkdownString; + documentation?: string | vscode.MarkdownString; parameters: ParameterInformation[]; activeParameter?: number; - constructor(label: string, documentation?: string | MarkdownString) { + constructor(label: string, documentation?: string | vscode.MarkdownString) { this.label = label; this.documentation = documentation; this.parameters = []; @@ -1439,7 +1434,7 @@ export class CompletionItem implements vscode.CompletionItem { kind?: CompletionItemKind; tags?: CompletionItemTag[]; detail?: string; - documentation?: string | MarkdownString; + documentation?: string | vscode.MarkdownString; sortText?: string; filterText?: string; preselect?: boolean; @@ -2218,13 +2213,18 @@ export enum ConfigurationTarget { @es5ClassCompat export class RelativePattern implements IRelativePattern { base: string; - baseFolder?: URI; - pattern: string; - constructor(base: vscode.WorkspaceFolder | string, pattern: string) { + // expose a `baseFolder: URI` property as a workaround for the short-coming + // of `IRelativePattern` only supporting `base: string` which always translates + // to a `file://` URI. With `baseFolder` we can support non-file based folders + // in searches + // (https://github.com/microsoft/vscode/commit/6326543b11cf4998c8fd1564cab5c429a2416db3) + readonly baseFolder?: URI; + + constructor(base: vscode.WorkspaceFolder | URI | string, pattern: string) { if (typeof base !== 'string') { - if (!base || !URI.isUri(base.uri)) { + if (!base || !URI.isUri(base) && !URI.isUri(base.uri)) { throw illegalArgument('base'); } } @@ -2234,7 +2234,11 @@ export class RelativePattern implements IRelativePattern { } if (typeof base === 'string') { + this.baseFolder = URI.file(base); this.base = base; + } else if (URI.isUri(base)) { + this.baseFolder = base; + this.base = base.fsPath; } else { this.baseFolder = base.uri; this.base = base.uri.fsPath; @@ -2369,16 +2373,6 @@ export class EvaluatableExpression implements vscode.EvaluatableExpression { } } -export enum LogLevel { - Trace = 1, - Debug = 2, - Info = 3, - Warning = 4, - Error = 5, - Critical = 6, - Off = 7 -} - //#region file api export enum FileChangeType { @@ -2749,8 +2743,8 @@ export enum ExtensionKind { export class FileDecoration { static validate(d: FileDecoration): void { - if (d.badge && d.badge.length !== 1) { - throw new Error(`The 'badge'-property must be undefined or a single character`); + if (d.badge && d.badge.length !== 1 && d.badge.length !== 2) { + throw new Error(`The 'badge'-property must be undefined or a short character`); } if (!d.color && !d.badge && !d.tooltip) { throw new Error(`The decoration is empty`); @@ -2920,3 +2914,64 @@ export enum StandardTokenType { String = 2, RegEx = 4 } + + +export class LinkedEditingRanges { + constructor(public readonly ranges: Range[], public readonly wordPattern?: RegExp) { + } +} + +//#region Testing +export enum TestRunState { + Unset = 0, + Running = 1, + Passed = 2, + Failed = 3, + Skipped = 4, + Errored = 5 +} + +export enum TestMessageSeverity { + Error = 0, + Warning = 1, + Information = 2, + Hint = 3 +} + +@es5ClassCompat +export class TestState { + #runState: TestRunState; + #duration?: number; + #messages: ReadonlyArray>; + + public get runState() { + return this.#runState; + } + + public get duration() { + return this.#duration; + } + + public get messages() { + return this.#messages; + } + + constructor(runState: TestRunState, messages: vscode.TestMessage[] = [], duration?: number) { + this.#runState = runState; + this.#messages = Object.freeze(messages.map(m => Object.freeze(m))); + this.#duration = duration; + } +} + +type AllowedUndefined = 'description' | 'location'; + +/** + * Test item without any optional properties. Only some properties are + * permitted to be undefined, but they must still exist. + */ +export type RequiredTestItem = { + [K in keyof Required]: K extends AllowedUndefined ? vscode.TestItem[K] : Required[K] +}; + + +//#endregion diff --git a/src/vs/workbench/api/common/extHostWebviewPanels.ts b/src/vs/workbench/api/common/extHostWebviewPanels.ts index d29fdc63..797702b8 100644 --- a/src/vs/workbench/api/common/extHostWebviewPanels.ts +++ b/src/vs/workbench/api/common/extHostWebviewPanels.ts @@ -12,7 +12,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { convertWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { EditorGroupColumn } from 'vs/workbench/common/editor'; import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; import * as extHostTypes from './extHostTypes'; @@ -273,7 +273,7 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel viewType: string, title: string, state: any, - position: EditorViewColumn, + position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions ): Promise { const entry = this._serializers.get(viewType); diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 7ba5fbca..3e0de07e 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -10,17 +10,18 @@ import { Emitter, Event } from 'vs/base/common/event'; import { TernarySearchTree } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { Counter } from 'vs/base/common/numbers'; -import { isLinux } from 'vs/base/common/platform'; import { basename, basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources'; import { compare } from 'vs/base/common/strings'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { Severity } from 'vs/platform/notification/common/notification'; import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes'; @@ -59,6 +60,11 @@ function delta(oldFolders: vscode.WorkspaceFolder[], newFolders: vscode.Workspac return arrayDelta(oldSortedFolders, newSortedFolders, compare); } +function ignorePathCasing(uri: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): boolean { + const capabilities = extHostFileSystemInfo.getCapabilities(uri.scheme); + return !(capabilities && (capabilities & FileSystemProviderCapabilities.PathCaseSensitive)); +} + interface MutableWorkspaceFolder extends vscode.WorkspaceFolder { name: string; index: number; @@ -66,7 +72,7 @@ interface MutableWorkspaceFolder extends vscode.WorkspaceFolder { class ExtHostWorkspaceImpl extends Workspace { - static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace?: ExtHostWorkspaceImpl, previousUnconfirmedWorkspace?: ExtHostWorkspaceImpl): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } { + static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace: ExtHostWorkspaceImpl | undefined, previousUnconfirmedWorkspace: ExtHostWorkspaceImpl | undefined, extHostFileSystemInfo: IExtHostFileSystemInfo): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } { if (!data) { return { workspace: null, added: [], removed: [] }; } @@ -99,7 +105,7 @@ class ExtHostWorkspaceImpl extends Workspace { // make sure to restore sort order based on index newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1); - const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled); + const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)); const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri); return { workspace, added, removed }; @@ -117,10 +123,11 @@ class ExtHostWorkspaceImpl extends Workspace { } private readonly _workspaceFolders: vscode.WorkspaceFolder[] = []; - private readonly _structure = TernarySearchTree.forUris(!isLinux); + private readonly _structure: TernarySearchTree; - constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean) { - super(id, folders.map(f => new WorkspaceFolder(f)), configuration); + constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean, ignorePathCasing: (key: URI) => boolean) { + super(id, folders.map(f => new WorkspaceFolder(f)), configuration, ignorePathCasing); + this._structure = TernarySearchTree.forUris(ignorePathCasing); // setup the workspace folder data structure folders.forEach(folder => { @@ -170,22 +177,25 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac private readonly _proxy: MainThreadWorkspaceShape; private readonly _messageService: MainThreadMessageServiceShape; + private readonly _extHostFileSystemInfo: IExtHostFileSystemInfo; private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = []; constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtHostFileSystemInfo extHostFileSystemInfo: IExtHostFileSystemInfo, @ILogService logService: ILogService, ) { this._logService = logService; + this._extHostFileSystemInfo = extHostFileSystemInfo; this._requestIdProvider = new Counter(); this._barrier = new Barrier(); this._proxy = extHostRpc.getProxy(MainContext.MainThreadWorkspace); this._messageService = extHostRpc.getProxy(MainContext.MainThreadMessageService); const data = initData.workspace; - this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled) : undefined; + this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined; } $initializeWorkspace(data: IWorkspaceData | null): void { @@ -391,13 +401,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac configuration: this._actualWorkspace.configuration, folders, isUntitled: this._actualWorkspace.isUntitled - } as IWorkspaceData, this._actualWorkspace).workspace || undefined; + } as IWorkspaceData, this._actualWorkspace, undefined, this._extHostFileSystemInfo).workspace || undefined; } } $acceptWorkspaceData(data: IWorkspaceData | null): void { - const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace); + const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace, this._extHostFileSystemInfo); // Update our workspace object. We have a confirmed workspace, so we drop our // unconfirmed workspace. @@ -457,17 +467,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac } : options.previewOptions; - let includePattern: string | undefined; - let folder: URI | undefined; - if (options.include) { - if (typeof options.include === 'string') { - includePattern = options.include; - } else { - includePattern = options.include.pattern; - folder = (options.include as RelativePattern).baseFolder || URI.file(options.include.base); - } - } - + const { includePattern, folder } = parseSearchInclude(options.include); const excludePattern = (typeof options.exclude === 'string') ? options.exclude : options.exclude ? options.exclude.pattern : undefined; const queryOptions: ITextQueryBuilderOptions = { @@ -562,14 +562,12 @@ function parseSearchInclude(include: RelativePattern | string | undefined): { in includePattern = include; } else { includePattern = include.pattern; - - // include.base must be an absolute path includeFolder = include.baseFolder || URI.file(include.base); } } return { - includePattern: includePattern, + includePattern, folder: includeFolder }; } diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 2a393e99..32027cbe 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -73,19 +73,19 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.DebugToolBar, description: localize('menus.debugToolBar', "The debug toolbar menu") }, - { - key: 'menuBar/webNavigation', - id: MenuId.MenubarWebNavigationMenu, - description: localize('menus.webNavigation', "The top level navigational menu (web only)"), - proposed: true, - supportsSubmenus: false - }, { key: 'menuBar/file', id: MenuId.MenubarFileMenu, description: localize('menus.file', "The top level file menu"), proposed: true }, + { + key: 'menuBar/home', + id: MenuId.MenubarHomeMenu, + description: localize('menus.home', "The home indicator context menu (web only)"), + proposed: true, + supportsSubmenus: false + }, { key: 'scm/title', id: MenuId.SCMTitle, @@ -461,7 +461,7 @@ namespace schema { type: 'string' }, enablement: { - description: localize('vscode.extension.contributes.commandType.precondition', '(Optional) Condition which must be true to enable the command'), + description: localize('vscode.extension.contributes.commandType.precondition', '(Optional) Condition which must be true to enable the command in the UI (menu and keybindings). Does not prevent executing the command by other means, like the `executeCommand`-api.'), type: 'string' }, icon: { @@ -586,6 +586,10 @@ submenusExtensionPoint.setHandler(extensions => { collector.warn(localize('submenuId.invalid.id', "`{0}` is not a valid submenu identifier", entry.value.id)); return; } + if (_submenus.has(entry.value.id)) { + collector.warn(localize('submenuId.duplicate.id', "The `{0}` submenu was already previously registered.", entry.value.id)); + return; + } if (!entry.value.label) { collector.warn(localize('submenuId.invalid.label', "`{0}` is not a valid submenu label", entry.value.label)); return; @@ -616,6 +620,7 @@ submenusExtensionPoint.setHandler(extensions => { const _apiMenusByKey = new Map(Iterable.map(Iterable.from(apiMenus), menu => ([menu.key, menu]))); const _menuRegistrations = new DisposableStore(); +const _submenuMenuItems = new Map>(); const menusExtensionPoint = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: (schema.IUserFriendlyMenuItem | schema.IUserFriendlySubmenuItem)[] }>({ extensionPoint: 'menus', @@ -627,6 +632,7 @@ menusExtensionPoint.setHandler(extensions => { // remove all previous menu registrations _menuRegistrations.clear(); + _submenuMenuItems.clear(); const items: { id: MenuId, item: IMenuItem | ISubmenuItem }[] = []; @@ -694,6 +700,20 @@ menusExtensionPoint.setHandler(extensions => { continue; } + let submenuRegistrations = _submenuMenuItems.get(menu.id.id); + + if (!submenuRegistrations) { + submenuRegistrations = new Set(); + _submenuMenuItems.set(menu.id.id, submenuRegistrations); + } + + if (submenuRegistrations.has(submenu.id.id)) { + collector.warn(localize('submenuItem.duplicate', "The `{0}` submenu was already contributed to the `{1}` menu.", menuItem.submenu, entry.key)); + continue; + } + + submenuRegistrations.add(submenu.id.id); + item = { submenu: submenu.id, icon: submenu.icon, title: submenu.label, group: undefined, order: undefined, when: undefined }; } diff --git a/src/vs/workbench/api/common/shared/editor.ts b/src/vs/workbench/api/common/shared/editor.ts deleted file mode 100644 index 247c9b17..00000000 --- a/src/vs/workbench/api/common/shared/editor.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IEditorGroupsService, IEditorGroup, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { GroupIdentifier } from 'vs/workbench/common/editor'; -import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; - -export type EditorViewColumn = number; - -export function viewColumnToEditorGroup(editorGroupService: IEditorGroupsService, position?: EditorViewColumn): GroupIdentifier { - if (typeof position !== 'number' || position === ACTIVE_GROUP) { - return ACTIVE_GROUP; // prefer active group when position is undefined or passed in as such - } - - const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE); - - let candidate = groups[position]; - if (candidate) { - return candidate.id; // found direct match - } - - let firstGroup = groups[0]; - if (groups.length === 1 && firstGroup.count === 0) { - return firstGroup.id; // first editor should always open in first group independent from position provided - } - - return SIDE_GROUP; // open to the side if group not found or we are instructed to -} - -export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService, editorGroup: IEditorGroup | GroupIdentifier): EditorViewColumn { - const group = (typeof editorGroup === 'number') ? editorGroupService.getGroup(editorGroup) : editorGroup; - if (!group) { - throw new Error('Invalid group provided'); - } - - return editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).indexOf(group); -} \ No newline at end of file diff --git a/src/vs/workbench/api/common/shared/tasks.ts b/src/vs/workbench/api/common/shared/tasks.ts index 17396177..4de859f6 100644 --- a/src/vs/workbench/api/common/shared/tasks.ts +++ b/src/vs/workbench/api/common/shared/tasks.ts @@ -113,7 +113,7 @@ export interface TaskProcessStartedDTO { export interface TaskProcessEndedDTO { id: string; - exitCode: number; + exitCode: number | undefined; } diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index b3857616..bb5ec1cd 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -15,7 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; export interface OpenCommandPipeArgs { type: 'open'; fileURIs?: string[]; - folderURIs: string[]; + folderURIs?: string[]; forceNewWindow?: boolean; diffMode?: boolean; addMode?: boolean; @@ -24,6 +24,11 @@ export interface OpenCommandPipeArgs { waitMarkerFilePath?: string; } +export interface OpenExternalCommandPipeArgs { + type: 'openExternal'; + uris: string[]; +} + export interface StatusPipeArgs { type: 'status'; } @@ -34,6 +39,8 @@ export interface RunCommandPipeArgs { args: any[]; } +export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | OpenExternalCommandPipeArgs; + export interface ICommandsExecuter { executeCommand(id: string, ...args: any[]): Promise; } @@ -73,11 +80,14 @@ export class CLIServerBase { req.setEncoding('utf8'); req.on('data', (d: string) => chunks.push(d)); req.on('end', () => { - const data: OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | any = JSON.parse(chunks.join('')); + const data: PipeCommand | any = JSON.parse(chunks.join('')); switch (data.type) { case 'open': this.open(data, res); break; + case 'openExternal': + this.openExternal(data, res); + break; case 'status': this.getStatus(data, res); break; @@ -133,6 +143,14 @@ export class CLIServerBase { res.end(); } + private openExternal(data: OpenExternalCommandPipeArgs, res: http.ServerResponse) { + for (const uri of data.uris) { + this._commands.executeCommand('_workbench.openExternal', URI.parse(uri), { allowTunneling: true }); + } + res.writeHead(200); + res.end(); + } + private async getStatus(data: StatusPipeArgs, res: http.ServerResponse) { try { const status = await this._commands.executeCommand('_issues.getSystemStatus'); diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index c68c242a..ff76f109 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -14,7 +14,6 @@ import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensi import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug'; import { IExtHostConfiguration, ExtHostConfigProvider } from '../common/extHostConfiguration'; -import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; @@ -24,13 +23,14 @@ import { SignService } from 'vs/platform/sign/node/signService'; import { hasChildProcesses, prepareCommand, runInExternalTerminal } from 'vs/workbench/contrib/debug/node/terminals'; import { IDisposable } from 'vs/base/common/lifecycle'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; +import { createCancelablePromise, firstParallel } from 'vs/base/common/async'; export class ExtHostDebugService extends ExtHostDebugServiceBase { readonly _serviceBrand: undefined; - private _integratedTerminalInstance?: vscode.Terminal; + private _integratedTerminalInstances = new DebugTerminalCollection(); private _terminalDisposedListener: IDisposable | undefined; constructor( @@ -39,10 +39,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { @IExtHostExtensionService extensionService: IExtHostExtensionService, @IExtHostDocumentsAndEditors editorsService: IExtHostDocumentsAndEditors, @IExtHostConfiguration configurationService: IExtHostConfiguration, - @IExtHostTerminalService private _terminalService: IExtHostTerminalService, - @IExtHostCommands commandService: IExtHostCommands + @IExtHostTerminalService private _terminalService: IExtHostTerminalService ) { - super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService); + super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService); } protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined { @@ -76,40 +75,44 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { if (!this._terminalDisposedListener) { // React on terminal disposed and check if that is the debug terminal #12956 this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => { - if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) { - this._integratedTerminalInstance = undefined; - } + this._integratedTerminalInstances.onTerminalClosed(terminal); }); } - let needNewTerminal = true; // be pessimistic - if (this._integratedTerminalInstance) { - const pid = await this._integratedTerminalInstance.processId; - needNewTerminal = await hasChildProcesses(pid); // if no processes running in terminal reuse terminal - } - const configProvider = await this._configurationService.getConfigProvider(); const shell = this._terminalService.getDefaultShell(true, configProvider); + const shellArgs = this._terminalService.getDefaultShellArgs(true, configProvider); + + const shellConfig = JSON.stringify({ shell, shellArgs }); + let terminal = await this._integratedTerminalInstances.checkout(shellConfig); + let cwdForPrepareCommand: string | undefined; + let giveShellTimeToInitialize = false; - if (needNewTerminal || !this._integratedTerminalInstance) { - + if (!terminal) { const options: vscode.TerminalOptions = { shellPath: shell, - // shellArgs: this._terminalService._getDefaultShellArgs(configProvider), + shellArgs: shellArgs, cwd: args.cwd, name: args.title || nls.localize('debug.terminal.title', "debuggee"), }; - this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options, true); + giveShellTimeToInitialize = true; + terminal = this._terminalService.createTerminalFromOptions(options, true); + this._integratedTerminalInstances.insert(terminal, shellConfig); + } else { cwdForPrepareCommand = args.cwd; } - const terminal = this._integratedTerminalInstance; - terminal.show(); - const shellProcessId = await this._integratedTerminalInstance.processId; + const shellProcessId = await terminal.processId; + + if (giveShellTimeToInitialize) { + // give a new terminal some time to initialize the shell + await new Promise(resolve => setTimeout(resolve, 1000)); + } + const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env); terminal.sendText(command, true); @@ -123,6 +126,49 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { - return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment); + return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment, this._workspaceService); + } +} + +class DebugTerminalCollection { + /** + * Delay before a new terminal is a candidate for reuse. See #71850 + */ + private static minUseDelay = 1000; + + private _terminalInstances = new Map(); + + public async checkout(config: string) { + const entries = [...this._terminalInstances.keys()]; + const promises = entries.map((terminal) => createCancelablePromise(async ct => { + const pid = await terminal.processId; + if (await hasChildProcesses(pid)) { + return null; + } + + // important: date check and map operations must be synchronous + const now = Date.now(); + const termInfo = this._terminalInstances.get(terminal); + if (!termInfo || termInfo.lastUsedAt + DebugTerminalCollection.minUseDelay > now || ct.isCancellationRequested) { + return null; + } + + if (termInfo.config !== config) { + return null; + } + + termInfo.lastUsedAt = now; + return terminal; + })); + + return await firstParallel(promises, (t): t is vscode.Terminal => !!t); + } + + public insert(terminal: vscode.Terminal, termConfig: string) { + this._terminalInstances.set(terminal, { lastUsedAt: Date.now(), config: termConfig }); + } + + public onTerminalClosed(terminal: vscode.Terminal) { + this._terminalInstances.delete(terminal); } } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 01fc86ae..9d5951b8 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -50,6 +50,10 @@ export class ExtHostTask extends ExtHostTaskBase { } public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { + if (!task.execution) { + throw new Error('Tasks to execute must include an execution'); + } + const tTask = (task as types.Task); // We have a preserved ID. So the task didn't change. if (tTask._id !== undefined) { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index cdc252bc..01a34ff3 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -23,6 +23,7 @@ import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/co import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class ExtHostTerminalService extends BaseExtHostTerminalService { @@ -56,7 +57,15 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { public createTerminalFromOptions(options: vscode.TerminalOptions, isFeatureTerminal?: boolean): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, options, options.name); this._terminals.push(terminal); - terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv, options.hideFromUser, isFeatureTerminal); + terminal.create( + withNullAsUndefined(options.shellPath), + withNullAsUndefined(options.shellArgs), + withNullAsUndefined(options.cwd), + withNullAsUndefined(options.env), + /*options.waitOnExit*/ undefined, + withNullAsUndefined(options.strictEnv), + withNullAsUndefined(options.hideFromUser), + withNullAsUndefined(isFeatureTerminal)); return terminal; } diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index ffd3bd98..922451d1 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -16,7 +16,8 @@ import { isLinux } from 'vs/base/common/platform'; import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { asPromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; -import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; +import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; class ExtensionTunnel implements vscode.Tunnel { private _onDispose: Emitter = new Emitter(); @@ -36,9 +37,9 @@ class ExtensionTunnel implements vscode.Tunnel { export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService { readonly _serviceBrand: undefined; private readonly _proxy: MainThreadTunnelServiceShape; - private _forwardPortProvider: ((tunnelOptions: TunnelOptions) => Thenable | undefined) | undefined; + private _forwardPortProvider: ((tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined) | undefined; private _showCandidatePort: (host: string, port: number, detail: string) => Thenable = () => { return Promise.resolve(true); }; - private _extensionTunnels: Map> = new Map(); + private _extensionTunnels: Map> = new Map(); private _onDidChangeTunnels: Emitter = new Emitter(); onDidChangeTunnels: vscode.Event = this._onDidChangeTunnels.event; @@ -53,8 +54,8 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe } } - async openTunnel(forward: TunnelOptions): Promise { - const tunnel = await this._proxy.$openTunnel(forward); + async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise { + const tunnel = await this._proxy.$openTunnel(forward, extension.displayName); if (tunnel) { const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => { return this._proxy.$closeTunnel(tunnel.remoteAddress); @@ -69,21 +70,25 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe return this._proxy.$getTunnels(); } - registerCandidateFinder(): Promise { - return this._proxy.$registerCandidateFinder(); - } - - $filterCandidates(candidates: { host: string, port: number, detail: string }[]): Promise { - return Promise.all(candidates.map(candidate => { - return this._showCandidatePort(candidate.host, candidate.port, candidate.detail); - })); + registerCandidateFinder(): void { + // Every two seconds, scan to see if the candidate ports have changed; + if (isLinux) { + let oldPorts: { host: string, port: number, detail: string }[] | undefined = undefined; + setInterval(async () => { + const newPorts = await this.findCandidatePorts(); + if (!oldPorts || (JSON.stringify(oldPorts) !== JSON.stringify(newPorts))) { + oldPorts = newPorts; + this._proxy.$onFoundNewCandidates(oldPorts.filter(async (candidate) => await this._showCandidatePort(candidate.host, candidate.port, candidate.detail))); + return; + } + }, 2000); + } } async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise { if (provider) { if (provider.showCandidatePort) { this._showCandidatePort = provider.showCandidatePort; - await this._proxy.$setCandidateFilter(); } if (provider.tunnelFactory) { this._forwardPortProvider = provider.tunnelFactory; @@ -98,11 +103,14 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe }); } - async $closeTunnel(remote: { host: string, port: number }): Promise { + async $closeTunnel(remote: { host: string, port: number }, silent?: boolean): Promise { if (this._extensionTunnels.has(remote.host)) { const hostMap = this._extensionTunnels.get(remote.host)!; if (hostMap.has(remote.port)) { - hostMap.get(remote.port)!.dispose(); + if (silent) { + hostMap.get(remote.port)!.disposeListener.dispose(); + } + hostMap.get(remote.port)!.tunnel.dispose(); hostMap.delete(remote.port); } } @@ -112,16 +120,16 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe this._onDidChangeTunnels.fire(); } - $forwardPort(tunnelOptions: TunnelOptions): Promise | undefined { + $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined { if (this._forwardPortProvider) { - const providedPort = this._forwardPortProvider!(tunnelOptions); + const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions); if (providedPort !== undefined) { return asPromise(() => providedPort).then(tunnel => { if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) { this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map()); } - this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, tunnel); - this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress))); + const disposeListener = this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress))); + this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener }); return Promise.resolve(TunnelDto.fromApiTunnel(tunnel)); }); } @@ -130,11 +138,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe } - async $findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]> { - if (!isLinux) { - return []; - } - + async findCandidatePorts(): Promise<{ host: string, port: number, detail: string }[]> { const ports: { host: string, port: number, detail: string }[] = []; let tcp: string = ''; let tcp6: string = ''; @@ -189,15 +193,19 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe return ports; } - private getSockets(stdout: string) { + private getSockets(stdout: string): { pid: number, socket: number }[] { const lines = stdout.trim().split('\n'); - return lines.map(line => { + const mapped: { pid: number, socket: number }[] = []; + lines.forEach(line => { const match = /\/proc\/(\d+)\/fd\/\d+ -> socket:\[(\d+)\]/.exec(line)!; - return { - pid: parseInt(match[1], 10), - socket: parseInt(match[2], 10) - }; + if (match && match.length >= 3) { + mapped.push({ + pid: parseInt(match[1], 10), + socket: parseInt(match[2], 10) + }); + } }); + return mapped; } private loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] { diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 35f9395f..cdd2cdfc 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -8,7 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor, MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -21,7 +21,6 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { InEditorZenModeContext, IsCenteredLayoutContext, EditorAreaVisibleContext } from 'vs/workbench/common/editor'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SideBarVisibleContext } from 'vs/workbench/common/viewlet'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IViewDescriptorService, IViewsService, FocusedViewContext, ViewContainerLocation, IViewDescriptor } from 'vs/workbench/common/views'; import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -76,7 +75,7 @@ export class ToggleActivityBarVisibilityAction extends Action2 { const visibility = layoutService.isVisible(Parts.ACTIVITYBAR_PART); const newVisibilityValue = !visibility; - configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); + configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue); } } @@ -196,7 +195,7 @@ export class ToggleSidebarPositionAction extends Action { const position = this.layoutService.getSideBarPosition(); const newPositionValue = (position === Position.LEFT) ? 'right' : 'left'; - return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue); } static getLabel(layoutService: IWorkbenchLayoutService): string { @@ -317,7 +316,7 @@ export class ToggleStatusbarVisibilityAction extends Action { const visibility = this.layoutService.isVisible(Parts.STATUSBAR_PART); const newVisibilityValue = !visibility; - return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue); } } @@ -419,14 +418,13 @@ export class ToggleMenuBarAction extends Action { constructor( id: string, label: string, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(id, label); } run(): Promise { - let currentVisibilityValue = getMenuBarVisibility(this.configurationService, this.environmentService); + let currentVisibilityValue = getMenuBarVisibility(this.configurationService); if (typeof currentVisibilityValue !== 'string') { currentVisibilityValue = 'default'; } diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 5c8d8c70..b71489ac 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -356,19 +356,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // List if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusPreviousPage(); - list.reveal(list.getFocus()[0]); + focused.focusPreviousPage(); } // Tree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusPreviousPage(fakeKeyboardEvent); - list.reveal(list.getFocus()[0]); + focused.focusPreviousPage(fakeKeyboardEvent); } // Ensure DOM Focus @@ -386,19 +380,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // List if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - list.focusNextPage(); - list.reveal(list.getFocus()[0]); + focused.focusNextPage(); } // Tree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusNextPage(fakeKeyboardEvent); - list.reveal(list.getFocus()[0]); + focused.focusNextPage(fakeKeyboardEvent); } // Ensure DOM Focus diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index 7344a3a2..5833c329 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -12,13 +12,10 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IPanel } from 'vs/workbench/common/panel'; -import { Action2, MenuId, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions'; import { Direction } from 'vs/base/browser/ui/grid/grid'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isAncestor } from 'vs/base/browser/dom'; @@ -275,29 +272,6 @@ export class FocusPreviousPart extends Action { } } -class GoHomeContributor implements IWorkbenchContribution { - - constructor( - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService - ) { - const homeIndicator = environmentService.options?.homeIndicator; - if (homeIndicator) { - registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.goHome`, - title: nls.localize('goHome', "Go Home"), - menu: { id: MenuId.MenubarWebNavigationMenu } - }); - } - async run(): Promise { - window.location.href = homeIndicator.href; - } - }); - } - } -} - // --- Actions Registration const actionsRegistry = Registry.as(Extensions.WorkbenchActions); @@ -308,6 +282,3 @@ actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateLeftAc actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateRightAction, undefined), 'View: Navigate to the View on the Right', CATEGORIES.View.value); actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusNextPart, { primary: KeyCode.F6 }), 'View: Focus Next Part', CATEGORIES.View.value); actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(FocusPreviousPart, { primary: KeyMod.Shift | KeyCode.F6 }), 'View: Focus Previous Part', CATEGORIES.View.value); - -const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(GoHomeContributor, LifecyclePhase.Ready); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 700fb6ce..b7357376 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -141,7 +141,7 @@ export class OpenWorkspaceConfigFileAction extends Action { async run(): Promise { const configuration = this.workspaceContextService.getWorkspace().configuration; if (configuration) { - await this.editorService.openEditor({ resource: configuration }); + await this.editorService.openEditor({ resource: configuration, options: { pinned: true } }); } } } diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index 24dc2f9d..7f43e0fb 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -9,7 +9,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/commo import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { FileKind } from 'vs/platform/files/common/files'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -18,6 +18,10 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { IOpenWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder'; export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace..."); @@ -61,7 +65,7 @@ CommandsRegistry.registerCommand({ title: nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"), canSelectFolders: true, canSelectMany: true, - defaultUri: dialogsService.defaultFolderPath() + defaultUri: await dialogsService.defaultFolderPath() }); if (!folders || !folders.length) { @@ -116,3 +120,46 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async functio return; }); + +// API Command registration + +interface IOpenFolderAPICommandOptions { + forceNewWindow?: boolean; + forceReuseWindow?: boolean; + noRecentEntry?: boolean; +} + +CommandsRegistry.registerCommand({ + id: 'vscode.openFolder', + handler: (accessor: ServicesAccessor, uri?: URI, arg?: boolean | IOpenFolderAPICommandOptions) => { + const commandService = accessor.get(ICommandService); + + // Be compatible to previous args by converting to options + if (typeof arg === 'boolean') { + arg = { forceNewWindow: arg }; + } + + // Without URI, ask to pick a folder or workpsace to open + if (!uri) { + return commandService.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg?.forceNewWindow }); + } + + uri = URI.revive(uri); + + const options: IOpenWindowOptions = { + forceNewWindow: arg?.forceNewWindow, + forceReuseWindow: arg?.forceReuseWindow, + noRecentEntry: arg?.noRecentEntry + }; + + const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri }; + return commandService.executeCommand('_files.windowOpen', [uriToOpen], options); + }, + description: { + description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.', + args: [ + { name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI }, + { name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' } + ] + } +}); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 53f39bc8..baab1343 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -16,7 +16,7 @@ import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { MIME_BINARY } from 'vs/base/common/mime'; -import { isWindows, isWeb } from 'vs/base/common/platform'; +import { isWindows } from 'vs/base/common/platform'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorIdentifier, GroupIdentifier } from 'vs/workbench/common/editor'; @@ -27,7 +27,6 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { isStandalone } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Emitter } from 'vs/base/common/event'; @@ -320,8 +319,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: event.dataTransfer.setData(DataTransfers.TEXT, sources.map(source => source.resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(source.resource.fsPath)) : source.resource.toString()).join(lineDelimiter)); // Download URL: enables support to drag a tab as file to desktop (only single file supported) - // Disabled for PWA web due to: https://github.com/microsoft/vscode/issues/83441 - if (!sources[0].isDirectory && (!isWeb || !isStandalone)) { + if (!sources[0].isDirectory) { event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(sources[0].resource), FileAccess.asBrowserUri(sources[0].resource).toString()].join(':')); } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 13f62c49..460594ee 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, isAncestor, getClientArea, Dimension, position, size, IDimension } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -16,7 +16,7 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -41,7 +41,6 @@ import { INotificationService, NotificationsFilter } from 'vs/platform/notificat import { IThemeService } from 'vs/platform/theme/common/themeService'; import { WINDOW_ACTIVE_BORDER, WINDOW_INACTIVE_BORDER } from 'vs/workbench/common/theme'; import { LineNumbersType } from 'vs/editor/common/config/editorOptions'; -import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; import { URI } from 'vs/base/common/uri'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -91,21 +90,6 @@ enum Classes { WINDOW_BORDER = 'border' } -interface PanelActivityState { - id: string; - name?: string; - pinned: boolean; - order: number; - visible: boolean; -} - -interface SideBarActivityState { - id: string; - pinned: boolean; - order: number; - visible: boolean; -} - export abstract class Layout extends Disposable implements IWorkbenchLayoutService { declare readonly _serviceBrand: undefined; @@ -319,7 +303,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._register(addDisposableListener(this.container, EventType.SCROLL, () => this.container.scrollTop = 0)); // Menubar visibility changes - if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService) === 'custom') { this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } @@ -357,8 +341,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + // Change edge snapping accordingly + this.workbenchGrid.edgeSnapping = this.state.fullscreen; + // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (getTitleBarStyle(this.configurationService) === 'custom') { // Propagate to grid this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART)); @@ -407,7 +394,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Menubar visibility - const newMenubarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService); + const newMenubarVisibility = getMenuBarVisibility(this.configurationService); this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); // Centered Layout @@ -451,7 +438,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private updateWindowBorder(skipLayout: boolean = false) { - if (isWeb || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') { + if (isWeb || getTitleBarStyle(this.configurationService) !== 'custom') { return; } @@ -495,7 +482,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.fullscreen = isFullscreen(); // Menubar visibility - this.state.menuBar.visibility = getMenuBarVisibility(this.configurationService, this.environmentService); + this.state.menuBar.visibility = getMenuBarVisibility(this.configurationService); // Activity bar visibility this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); @@ -582,167 +569,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const { views } = defaultLayout; if (views?.length) { this.state.views.defaults = views.map(v => v.id); - - return; - } - - // TODO@eamodio Everything below here is deprecated and will be removed once Codespaces migrates - - const { sidebar } = defaultLayout; - if (sidebar) { - if (sidebar.visible !== undefined) { - if (sidebar.visible) { - storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); - } else { - storageService.store(Storage.SIDEBAR_HIDDEN, true, StorageScope.WORKSPACE); - } - } - - if (sidebar.containers?.length) { - const sidebarState: SideBarActivityState[] = []; - - let order = -1; - for (const container of sidebar.containers.sort((a, b) => (a.order ?? 1) - (b.order ?? 1))) { - let viewletId; - switch (container.id) { - case 'explorer': - viewletId = 'workbench.view.explorer'; - break; - case 'run': - viewletId = 'workbench.view.debug'; - break; - case 'scm': - viewletId = 'workbench.view.scm'; - break; - case 'search': - viewletId = 'workbench.view.search'; - break; - case 'extensions': - viewletId = 'workbench.view.extensions'; - break; - case 'remote': - viewletId = 'workbench.view.remote'; - break; - default: - viewletId = `workbench.view.extension.${container.id}`; - } - - if (container.active) { - storageService.store(SidebarPart.activeViewletSettingsKey, viewletId, StorageScope.WORKSPACE); - } - - if (container.order !== undefined || (container.active === undefined && container.visible !== undefined)) { - order = container.order ?? (order + 1); - const state: SideBarActivityState = { - id: viewletId, - order: order, - pinned: (container.active || container.visible) ?? true, - visible: (container.active || container.visible) ?? true - }; - - sidebarState.push(state); - } - - if (container.views !== undefined) { - const viewsState: { id: string, isHidden?: boolean, order?: number }[] = []; - const viewsWorkspaceState: { [id: string]: { collapsed: boolean, isHidden?: boolean, size?: number } } = {}; - - for (const view of container.views) { - if (view.order !== undefined || view.visible !== undefined) { - viewsState.push({ - id: view.id, - isHidden: view.visible === undefined ? undefined : !view.visible, - order: view.order === undefined ? undefined : view.order - }); - } - - if (view.collapsed !== undefined) { - viewsWorkspaceState[view.id] = { - collapsed: view.collapsed, - isHidden: view.visible === undefined ? undefined : !view.visible, - }; - } - } - - storageService.store(`${viewletId}.state.hidden`, JSON.stringify(viewsState), StorageScope.GLOBAL); - storageService.store(`${viewletId}.state`, JSON.stringify(viewsWorkspaceState), StorageScope.WORKSPACE); - } - } - - if (sidebarState.length) { - storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, JSON.stringify(sidebarState), StorageScope.GLOBAL); - } - } - } - - const { panel } = defaultLayout; - if (panel) { - if (panel.visible !== undefined) { - if (panel.visible) { - storageService.store(Storage.PANEL_HIDDEN, false, StorageScope.WORKSPACE); - } else { - storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); - } - } - - if (panel.containers?.length) { - const panelState: PanelActivityState[] = []; - - let order = -1; - for (const container of panel.containers.sort((a, b) => (a.order ?? 1) - (b.order ?? 1))) { - let name; - let panelId = container.id; - switch (panelId) { - case 'terminal': - name = 'Terminal'; - panelId = 'workbench.panel.terminal'; - break; - case 'debug': - name = 'Debug Console'; - panelId = 'workbench.panel.repl'; - break; - case 'problems': - name = 'Problems'; - panelId = 'workbench.panel.markers'; - break; - case 'output': - name = 'Output'; - panelId = 'workbench.panel.output'; - break; - case 'comments': - name = 'Comments'; - panelId = 'workbench.panel.comments'; - break; - case 'refactor': - name = 'Refactor Preview'; - panelId = 'refactorPreview'; - break; - default: - continue; - } - - if (container.active) { - storageService.store(PanelPart.activePanelSettingsKey, panelId, StorageScope.WORKSPACE); - } - - if (container.order !== undefined || (container.active === undefined && container.visible !== undefined)) { - order = container.order ?? (order + 1); - const state: PanelActivityState = { - id: panelId, - name: name, - order: order, - pinned: (container.active || container.visible) ?? true, - visible: (container.active || container.visible) ?? true - }; - - panelState.push(state); - } - } - - if (panelState.length) { - storageService.store(PanelPart.PINNED_PANELS, JSON.stringify(panelState), StorageScope.GLOBAL); - } - } } } @@ -750,7 +576,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const initialFilesToOpen = this.getInitialFilesToOpen(); // Only restore editors if we are not instructed to open files initially - this.state.editor.restoreEditors = initialFilesToOpen === undefined; + // or when `window.restoreWindows` setting is explicitly set to `preserve` + const forceRestoreEditors = this.configurationService.getValue('window.restoreWindows') === 'preserve'; + this.state.editor.restoreEditors = !!forceRestoreEditors || initialFilesToOpen === undefined; // Files to open, diff or create if (initialFilesToOpen !== undefined) { @@ -996,7 +824,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const container = this.getContainer(part); - return !!container && isAncestor(activeElement, container); + return !!container && isAncestorUsingFlowTo(activeElement, container); } focusPart(part: Parts): void { @@ -1050,7 +878,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi isVisible(part: Parts): boolean { switch (part) { case Parts.TITLEBAR_PART: - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + if (getTitleBarStyle(this.configurationService) === 'native') { return false; } else if (!this.state.fullscreen && !isWeb) { return true; @@ -1246,7 +1074,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // State if (this.state.zenMode.active) { - this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE); + this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE, StorageTarget.USER); // Exit zen mode on shutdown unless configured to keep this.state.zenMode.transitionDisposables.add(this.storageService.onWillSaveState(e => { @@ -1310,6 +1138,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.container.prepend(workbenchGrid.element); this.container.setAttribute('role', 'application'); this.workbenchGrid = workbenchGrid; + this.workbenchGrid.edgeSnapping = this.state.fullscreen; [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar].forEach((part: Part) => { this._register(part.onDidVisibilityChange((visible) => { @@ -1331,18 +1160,18 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi ? grid.getViewCachedVisibleSize(this.sideBarPartView) : grid.getViewSize(this.sideBarPartView).width; - this.storageService.store(Storage.SIDEBAR_SIZE, sideBarSize, StorageScope.GLOBAL); + this.storageService.store(Storage.SIDEBAR_SIZE, sideBarSize, StorageScope.GLOBAL, StorageTarget.MACHINE); const panelSize = this.state.panel.hidden ? grid.getViewCachedVisibleSize(this.panelPartView) : (this.state.panel.position === Position.BOTTOM ? grid.getViewSize(this.panelPartView).height : grid.getViewSize(this.panelPartView).width); - this.storageService.store(Storage.PANEL_SIZE, panelSize, StorageScope.GLOBAL); - this.storageService.store(Storage.PANEL_DIMENSION, positionToString(this.state.panel.position), StorageScope.GLOBAL); + this.storageService.store(Storage.PANEL_SIZE, panelSize, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(Storage.PANEL_DIMENSION, positionToString(this.state.panel.position), StorageScope.GLOBAL, StorageTarget.MACHINE); const gridSize = grid.getViewSize(); - this.storageService.store(Storage.GRID_WIDTH, gridSize.width, StorageScope.GLOBAL); - this.storageService.store(Storage.GRID_HEIGHT, gridSize.height, StorageScope.GLOBAL); + this.storageService.store(Storage.GRID_WIDTH, gridSize.width, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(Storage.GRID_HEIGHT, gridSize.height, StorageScope.GLOBAL, StorageTarget.MACHINE); })); } @@ -1373,7 +1202,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi centerEditorLayout(active: boolean, skipLayout?: boolean): void { this.state.editor.centered = active; - this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE); + this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE, StorageTarget.USER); let smartActive = active; const activeEditor = this.editorService.activeEditor; @@ -1486,7 +1315,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Remember in settings if (hidden) { - this.storageService.store(Storage.EDITOR_HIDDEN, true, StorageScope.WORKSPACE); + this.storageService.store(Storage.EDITOR_HIDDEN, true, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.EDITOR_HIDDEN, StorageScope.WORKSPACE); } @@ -1547,7 +1376,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Remember in settings const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; if (hidden !== defaultHidden) { - this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); } @@ -1612,14 +1441,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Remember in settings if (!hidden) { - this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); + this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); // Remember this setting only when panel is hiding if (this.state.panel.wasLastMaximized) { - this.storageService.store(Storage.PANEL_LAST_IS_MAXIMIZED, true, StorageScope.WORKSPACE); + this.storageService.store(Storage.PANEL_LAST_IS_MAXIMIZED, true, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(Storage.PANEL_LAST_IS_MAXIMIZED, StorageScope.WORKSPACE); @@ -1637,10 +1466,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (!this.state.panel.hidden) { if (this.state.panel.position === Position.BOTTOM) { this.state.panel.lastNonMaximizedHeight = size.height; - this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_HEIGHT, this.state.panel.lastNonMaximizedHeight, StorageScope.GLOBAL); + this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_HEIGHT, this.state.panel.lastNonMaximizedHeight, StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.state.panel.lastNonMaximizedWidth = size.width; - this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_WIDTH, this.state.panel.lastNonMaximizedWidth, StorageScope.GLOBAL); + this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_WIDTH, this.state.panel.lastNonMaximizedWidth, StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -1715,7 +1544,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.panel.position = position; // Save panel position - this.storageService.store(Storage.PANEL_POSITION, newPositionValue, StorageScope.WORKSPACE); + this.storageService.store(Storage.PANEL_POSITION, newPositionValue, StorageScope.WORKSPACE, StorageTarget.USER); // Adjust CSS const panelContainer = assertIsDefined(panelPart.getContainer()); diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index b3271863..cf008e43 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -220,6 +220,10 @@ body.web { opacity: 1 !important; } +.monaco-workbench input[type="checkbox"]:focus { + outline-offset: 2px; +} + .monaco-workbench [tabindex="0"]:active, .monaco-workbench [tabindex="-1"]:active, .monaco-workbench select:active, diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index fca51281..49bbae45 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -10,15 +10,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { dispose } from 'vs/base/common/lifecycle'; -import { SyncActionDescriptor, IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IMenuService, MenuId, IMenu, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarColors, ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; -import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { CATEGORIES } from 'vs/workbench/common/actions'; import { IActivity } from 'vs/workbench/common/activity'; import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; @@ -26,44 +25,31 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { Codicon } from 'vs/base/common/codicons'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { AuthenticationSession } from 'vs/editor/common/modes'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; -import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class ViewContainerActivityAction extends ActivityAction { private static readonly preventDoubleClickDelay = 300; - private readonly viewletService: IViewletService; - private readonly layoutService: IWorkbenchLayoutService; - private readonly telemetryService: ITelemetryService; - private readonly configurationService: IConfigurationService; - - private lastRun: number; + private lastRun = 0; constructor( activity: IActivity, - @IViewletService viewletService: IViewletService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @ITelemetryService telemetryService: ITelemetryService, - @IConfigurationService configurationService: IConfigurationService + @IViewletService private readonly viewletService: IViewletService, + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(activity); - - this.lastRun = 0; - this.viewletService = viewletService; - this.layoutService = layoutService; - this.telemetryService = telemetryService; - this.configurationService = configurationService; } updateActivity(activity: IActivity): void { @@ -105,6 +91,7 @@ export class ViewContainerActivityAction extends ActivityAction { this.logAction('show'); await this.viewletService.openViewlet(this.activity.id, true); + return this.activate(); } @@ -117,21 +104,18 @@ export class ViewContainerActivityAction extends ActivityAction { } } -export const ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts'; +class MenuActivityActionViewItem extends ActivityActionViewItem { -export class AccountsActionViewItem extends ActivityActionViewItem { constructor( + private readonly menuId: MenuId, action: ActivityAction, colors: (theme: IColorTheme) => ICompositeBarColors, @IThemeService themeService: IThemeService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IMenuService protected menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IAuthenticationService private readonly authenticationService: IAuthenticationService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IStorageService private readonly storageService: IStorageService, - @IProductService private readonly productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IMenuService protected readonly menuService: IMenuService, + @IContextMenuService protected readonly contextMenuService: IContextMenuService, + @IContextKeyService protected readonly contextKeyService: IContextKeyService, + @IConfigurationService protected readonly configurationService: IConfigurationService, + @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService ) { super(action, { draggable: false, colors, icon: true }, themeService); } @@ -161,12 +145,103 @@ export class AccountsActionViewItem extends ActivityActionViewItem { })); } - private async getActions(accountsMenu: IMenu) { + protected async showContextMenu(e?: MouseEvent): Promise { + const disposables = new DisposableStore(); + + const menu = disposables.add(this.menuService.createMenu(this.menuId, this.contextKeyService)); + const actions = await this.resolveActions(menu, disposables); + + const isUsingCustomMenu = isWeb || (getTitleBarStyle(this.configurationService) !== 'native' && !isMacintosh); // see #40262 + const position = this.configurationService.getValue('workbench.sideBar.location'); + + this.contextMenuService.showContextMenu({ + getAnchor: () => isUsingCustomMenu ? this.container : e || this.container, + anchorAlignment: isUsingCustomMenu ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined, + anchorAxisAlignment: isUsingCustomMenu ? AnchorAxisAlignment.HORIZONTAL : AnchorAxisAlignment.VERTICAL, + getActions: () => actions, + onHide: () => disposables.dispose() + }); + } + + protected async resolveActions(menu: IMenu, disposables: DisposableStore): Promise { + const actions: IAction[] = []; + + disposables.add(createAndFillInActionBarActions(menu, undefined, { primary: [], secondary: actions })); + + return actions; + } +} + +export class HomeActivityActionViewItem extends MenuActivityActionViewItem { + + static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator'; + + constructor( + private readonly goHomeHref: string, + action: ActivityAction, + colors: (theme: IColorTheme) => ICompositeBarColors, + @IThemeService themeService: IThemeService, + @IMenuService menuService: IMenuService, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IStorageService private readonly storageService: IStorageService + ) { + super(MenuId.MenubarHomeMenu, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService); + } + + protected async resolveActions(homeMenu: IMenu, disposables: DisposableStore): Promise { + const actions = []; + + // Go Home + actions.push(disposables.add(new Action('goHome', nls.localize('goHome', "Go Home"), undefined, true, async () => window.location.href = this.goHomeHref))); + actions.push(disposables.add(new Separator())); + + // Contributed + const contributedActions = await super.resolveActions(homeMenu, disposables); + actions.push(...contributedActions); + + // Hide + if (contributedActions.length > 0) { + actions.push(disposables.add(new Separator())); + } + actions.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => { + this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, false, StorageScope.GLOBAL, StorageTarget.USER); + }))); + + return actions; + } +} + +export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { + + static readonly ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts'; + + constructor( + action: ActivityAction, + colors: (theme: IColorTheme) => ICompositeBarColors, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IStorageService private readonly storageService: IStorageService, + @IProductService private readonly productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(MenuId.AccountsContext, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService); + } + + protected async resolveActions(accountsMenu: IMenu, disposables: DisposableStore): Promise { + await super.resolveActions(accountsMenu, disposables); + const otherCommands = accountsMenu.getActions(); const providers = this.authenticationService.getProviderIds(); - const allSessions = providers.map(async id => { + const allSessions = providers.map(async providerId => { try { - const sessions = await this.authenticationService.getSessions(id); + const sessions = await this.authenticationService.getSessions(providerId); const groupedSessions: { [label: string]: AuthenticationSession[] } = {}; sessions.forEach(session => { @@ -177,14 +252,9 @@ export class AccountsActionViewItem extends ActivityActionViewItem { } }); - return { - providerId: id, - sessions: groupedSessions - }; + return { providerId, sessions: groupedSessions }; } catch { - return { - providerId: id - }; + return { providerId }; } }); @@ -196,132 +266,67 @@ export class AccountsActionViewItem extends ActivityActionViewItem { if (sessionInfo.sessions) { Object.keys(sessionInfo.sessions).forEach(accountName => { - const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id)); - const manageExtensionsAction = new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, _ => { + const manageExtensionsAction = disposables.add(new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, () => { return this.authenticationService.manageTrustedExtensionsForAccount(sessionInfo.providerId, accountName); - }); - const signOutAction = new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, _ => { - return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName); - }); + })); - const actions = [manageExtensionsAction]; + const signOutAction = disposables.add(new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, () => { + return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName); + })); + + const providerSubMenuActions = [manageExtensionsAction]; + + const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id)); if (!hasEmbedderAccountSession || authenticationSession?.canSignOut) { - actions.push(signOutAction); + providerSubMenuActions.push(signOutAction); } - const menu = new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, actions); - menus.push(menu); + const providerSubMenu = disposables.add(new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, providerSubMenuActions)); + menus.push(providerSubMenu); }); } else { - const menu = new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName)); - menus.push(menu); + const providerUnavailableAction = disposables.add(new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName))); + menus.push(providerUnavailableAction); } }); if (menus.length && otherCommands.length) { - menus.push(new Separator()); + menus.push(disposables.add(new Separator())); } otherCommands.forEach((group, i) => { const actions = group[1]; menus = menus.concat(actions); if (i !== otherCommands.length - 1) { - menus.push(new Separator()); + menus.push(disposables.add(new Separator())); } }); if (menus.length) { - menus.push(new Separator()); + menus.push(disposables.add(new Separator())); } - menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => { - this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL); - return Promise.resolve(); - })); + menus.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => { + this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + }))); return menus; } - - private async showContextMenu(e?: MouseEvent): Promise { - const accountsActions: IAction[] = []; - const accountsMenu = this.menuService.createMenu(MenuId.AccountsContext, this.contextKeyService); - const actionsDisposable = createAndFillInActionBarActions(accountsMenu, undefined, { primary: [], secondary: accountsActions }); - const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native'; - const position = this.configurationService.getValue('workbench.sideBar.location'); - - const containerPosition = DOM.getDomNodePagePosition(this.container); - const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top }; - const actions = await this.getActions(accountsMenu); - this.contextMenuService.showContextMenu({ - getAnchor: () => !native ? location : e || this.container, - anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined, - getActions: () => actions, - onHide: () => { - accountsMenu.dispose(); - dispose(actionsDisposable); - } - }); - } } -export class GlobalActivityActionViewItem extends ActivityActionViewItem { +export class GlobalActivityActionViewItem extends MenuActivityActionViewItem { constructor( action: ActivityAction, colors: (theme: IColorTheme) => ICompositeBarColors, @IThemeService themeService: IThemeService, - @IMenuService private readonly menuService: IMenuService, - @IContextMenuService protected readonly contextMenuService: IContextMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IMenuService menuService: IMenuService, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { - super(action, { draggable: false, colors, icon: true }, themeService); - } - - render(container: HTMLElement): void { - super.render(container); - - // Context menus are triggered on mouse down so that an item can be picked - // and executed with releasing the mouse over it - - this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { - DOM.EventHelper.stop(e, true); - this.showContextMenu(e); - })); - - this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { - let event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { - DOM.EventHelper.stop(e, true); - this.showContextMenu(); - } - })); - - this._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => { - DOM.EventHelper.stop(e, true); - this.showContextMenu(); - })); - } - - private showContextMenu(e?: MouseEvent): void { - const globalActivityActions: IAction[] = []; - const globalActivityMenu = this.menuService.createMenu(MenuId.GlobalActivity, this.contextKeyService); - const actionsDisposable = createAndFillInActionBarActions(globalActivityMenu, undefined, { primary: [], secondary: globalActivityActions }); - const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native'; - const position = this.configurationService.getValue('workbench.sideBar.location'); - - const containerPosition = DOM.getDomNodePagePosition(this.container); - const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top + containerPosition.height }; - this.contextMenuService.showContextMenu({ - getAnchor: () => !native ? location : e || this.container, - anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined, - getActions: () => globalActivityActions, - onHide: () => { - globalActivityMenu.dispose(); - dispose(actionsDisposable); - } - }); + super(MenuId.GlobalActivity, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService); } } @@ -338,106 +343,62 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne } } -class SwitchSideBarViewAction extends Action { +class SwitchSideBarViewAction extends Action2 { constructor( - id: string, - name: string, - @IViewletService private readonly viewletService: IViewletService, - @IActivityBarService private readonly activityBarService: IActivityBarService + desc: Readonly, + private readonly offset: number ) { - super(id, name); + super(desc); } - async run(offset: number): Promise { - const visibleViewletIds = this.activityBarService.getVisibleViewContainerIds(); + async run(accessor: ServicesAccessor): Promise { + const activityBarService = accessor.get(IActivityBarService); + const viewletService = accessor.get(IViewletService); - const activeViewlet = this.viewletService.getActiveViewlet(); + const visibleViewletIds = activityBarService.getVisibleViewContainerIds(); + + const activeViewlet = viewletService.getActiveViewlet(); if (!activeViewlet) { return; } let targetViewletId: string | undefined; for (let i = 0; i < visibleViewletIds.length; i++) { if (visibleViewletIds[i] === activeViewlet.getId()) { - targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + offset) % visibleViewletIds.length]; + targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + this.offset) % visibleViewletIds.length]; break; } } - await this.viewletService.openViewlet(targetViewletId, true); + await viewletService.openViewlet(targetViewletId, true); } } -export class PreviousSideBarViewAction extends SwitchSideBarViewAction { - - static readonly ID = 'workbench.action.previousSideBarView'; - static readonly LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View'); - - constructor( - id: string, - name: string, - @IViewletService viewletService: IViewletService, - @IActivityBarService activityBarService: IActivityBarService - ) { - super(id, name, viewletService, activityBarService); - } - - run(): Promise { - return super.run(-1); - } -} - -export class NextSideBarViewAction extends SwitchSideBarViewAction { - - static readonly ID = 'workbench.action.nextSideBarView'; - static readonly LABEL = nls.localize('nextSideBarView', 'Next Side Bar View'); - - constructor( - id: string, - name: string, - @IViewletService viewletService: IViewletService, - @IActivityBarService activityBarService: IActivityBarService - ) { - super(id, name, viewletService, activityBarService); - } - - run(): Promise { - return super.run(1); - } -} - -export class HomeAction extends Action { - - constructor( - private readonly href: string, - name: string, - icon: Codicon - ) { - super('workbench.action.home', name, icon.classNames); - } - - async run(event: MouseEvent): Promise { - let openInNewWindow = false; - if (isMacintosh) { - openInNewWindow = event.metaKey; - } else { - openInNewWindow = event.ctrlKey; - } - - if (openInNewWindow) { - DOM.windowOpenNoOpener(this.href); - } else { - window.location.href = this.href; +registerAction2( + class PreviousSideBarViewAction extends SwitchSideBarViewAction { + constructor() { + super({ + id: 'workbench.action.previousSideBarView', + title: { value: nls.localize('previousSideBarView', "Previous Side Bar View"), original: 'Previous Side Bar View' }, + category: CATEGORIES.View, + f1: true + }, -1); } } -} +); -export class HomeActionViewItem extends ActionViewItem { - - constructor(action: IAction) { - super(undefined, action, { icon: true, label: false, useEventAsContext: true }); +registerAction2( + class NextSideBarViewAction extends SwitchSideBarViewAction { + constructor() { + super({ + id: 'workbench.action.nextSideBarView', + title: { value: nls.localize('nextSideBarView', "Next Side Bar View"), original: 'Next Side Bar View' }, + category: CATEGORIES.View, + f1: true + }, 1); + } } -} +); registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { const activityBarBackgroundColor = theme.getColor(ACTIVITY_BAR_BACKGROUND); @@ -510,6 +471,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = left: 9px; height: 32px; width: 32px; + z-index: 1; } .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:before, @@ -549,7 +511,3 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = } } }); - -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(PreviousSideBarViewAction), 'View: Previous Side Bar View', CATEGORIES.View.value); -registry.registerWorkbenchAction(SyncActionDescriptor.from(NextSideBarViewAction), 'View: Next Side Bar View', CATEGORIES.View.value); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 2a3a93d0..39a3ca76 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -8,24 +8,24 @@ import * as nls from 'vs/nls'; import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { Part } from 'vs/workbench/browser/part'; -import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActionViewItem, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; +import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActivityActionViewItem, HomeActivityActionViewItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions'; -import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar'; import { Dimension, createCSSRule, asCSSUrl, addDisposableListener, EventType } from 'vs/base/browser/dom'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions'; import { IViewDescriptorService, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewContainerModel, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { isUndefinedOrNull, assertIsDefined, isString } from 'vs/base/common/types'; +import { assertIsDefined } from 'vs/base/common/types'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Schemas } from 'vs/base/common/network'; @@ -34,7 +34,6 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getMenuBarVisibility } from 'vs/platform/windows/common/windows'; import { isWeb } from 'vs/base/common/platform'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { Before2D } from 'vs/workbench/browser/dnd'; import { Codicon, iconRegistry } from 'vs/base/common/codicons'; import { Action, Separator } from 'vs/base/common/actions'; @@ -43,41 +42,45 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { CATEGORIES } from 'vs/workbench/common/actions'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; interface IPlaceholderViewContainer { - id: string; - name?: string; - iconUrl?: UriComponents; - iconCSS?: string; - views?: { when?: string }[]; + readonly id: string; + readonly name?: string; + readonly iconUrl?: UriComponents; + readonly themeIcon?: ThemeIcon; + readonly views?: { when?: string }[]; } interface IPinnedViewContainer { - id: string; - pinned: boolean; - order?: number; - visible: boolean; + readonly id: string; + readonly pinned: boolean; + readonly order?: number; + readonly visible: boolean; } interface ICachedViewContainer { - id: string; + readonly id: string; name?: string; - icon?: URI | string; - pinned: boolean; - order?: number; + icon?: URI | ThemeIcon; + readonly pinned: boolean; + readonly order?: number; visible: boolean; views?: { when?: string }[]; } +const settingsViewBarIcon = registerIcon('settings-view-bar-icon', Codicon.settingsGear, nls.localize('settingsViewBarIcon', 'Settings icon in the view bar.')); +const accountsViewBarIcon = registerIcon('accounts-view-bar-icon', Codicon.account, nls.localize('accountsViewBarIcon', 'Accounts icon in the view bar.')); + export class ActivitybarPart extends Part implements IActivityBarService { declare readonly _serviceBrand: undefined; - private static readonly ACTION_HEIGHT = 48; - static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2'; + private static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2'; private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets'; - private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator'; + private static readonly ACTION_HEIGHT = 48; private static readonly ACCOUNTS_ACTION_INDEX = 0; + //#region IView readonly minimumWidth: number = 48; @@ -100,17 +103,17 @@ export class ActivitybarPart extends Part implements IActivityBarService { private globalActivityAction: ActivityAction | undefined; private globalActivityActionBar: ActionBar | undefined; - private readonly globalActivity: ICompositeActivity[] = []; private globalActivitiesContainer: HTMLElement | undefined; + private readonly globalActivity: ICompositeActivity[] = []; private accountsActivityAction: ActivityAction | undefined; - private accountsActivity: ICompositeActivity[] = []; + private readonly accountsActivity: ICompositeActivity[] = []; private readonly compositeActions = new Map(); private readonly viewContainerDisposables = new Map(); - private readonly keyboardNavigationDisposables = new DisposableStore(); + private readonly keyboardNavigationDisposables = this._register(new DisposableStore()); private readonly location = ViewContainerLocation.Sidebar; @@ -125,27 +128,36 @@ export class ActivitybarPart extends Part implements IActivityBarService { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); - storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.PINNED_VIEW_CONTAINERS, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: ACCOUNTS_VISIBILITY_PREFERENCE_KEY, version: 1 }); - - this.migrateFromOldCachedViewContainersValue(); - for (const cachedViewContainer of this.cachedViewContainers) { - if (environmentService.remoteAuthority // In remote window, hide activity bar entries until registered. - || this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer) + if ( + environmentService.remoteAuthority || // In remote window, hide activity bar entries until registered + this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer) ) { cachedViewContainer.visible = false; } } + this.compositeBar = this.createCompositeBar(); + + this.onDidRegisterViewContainers(this.getViewContainers()); + + this.registerListeners(); + } + + private createCompositeBar() { const cachedItems = this.cachedViewContainers - .map(v => ({ id: v.id, name: v.name, visible: v.visible, order: v.order, pinned: v.pinned })); - this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, { + .map(container => ({ + id: container.id, + name: container.name, + visible: container.visible, + order: container.order, + pinned: container.pinned + })); + + return this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, { icon: true, orientation: ActionsOrientation.VERTICAL, preventLoopNavigation: true, @@ -154,8 +166,9 @@ export class ActivitybarPart extends Part implements IActivityBarService { getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction, getOnCompositeClickAction: (compositeId: string) => new Action(compositeId, '', '', true, () => this.viewsService.isViewContainerVisible(compositeId) ? Promise.resolve(this.viewsService.closeViewContainer(compositeId)) : this.viewsService.openViewContainer(compositeId)), getContextMenuActions: () => { - const menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService); const actions = []; + + // Home if (this.homeBarContainer) { actions.push(new Action( 'toggleHomeBarAction', @@ -166,22 +179,26 @@ export class ActivitybarPart extends Part implements IActivityBarService { )); } + // Menu + const menuBarVisibility = getMenuBarVisibility(this.configurationService); if (menuBarVisibility === 'compact' || (menuBarVisibility === 'hidden' && isWeb)) { actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu"))); } - const toggleAccountsVisibilityAction = new Action( + // Accounts + actions.push(new Action( 'toggleAccountsVisibility', this.accountsVisibilityPreference ? nls.localize('hideAccounts', "Hide Accounts") : nls.localize('showAccounts', "Show Accounts"), undefined, true, async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; } - ); - - actions.push(toggleAccountsVisibilityAction); + )); actions.push(new Separator()); + // Toggle Sidebar actions.push(this.instantiationService.createInstance(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.layoutService))); + + // Toggle Activity Bar actions.push(new Action( ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"), @@ -204,19 +221,12 @@ export class ActivitybarPart extends Part implements IActivityBarService { colors: (theme: IColorTheme) => this.getActivitybarItemColors(theme), overflowActionSize: ActivitybarPart.ACTION_HEIGHT })); - - this.onDidRegisterViewContainers(this.getViewContainers()); - this.registerListeners(); - } - - focusActivityBar(): void { - this.compositeBar.focus(); } private getContextMenuActionsForComposite(compositeId: string): Action[] { - const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!; - const actions = []; + + const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!; const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer)!; if (defaultLocation !== this.viewDescriptorService.getViewContainerLocation(viewContainer)) { actions.push(new Action('resetLocationAction', nls.localize('resetLocation', "Reset Location"), undefined, true, async () => { @@ -253,13 +263,13 @@ export class ActivitybarPart extends Part implements IActivityBarService { disposables.clear(); this.onDidRegisterExtensions(); this.compositeBar.onDidChange(() => this.saveCachedViewContainers(), this, disposables); - this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables); + this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables); })); // Register for configuration changes this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('window.menuBarVisibility')) { - if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') { + if (getMenuBarVisibility(this.configurationService) === 'compact') { this.installMenubar(); } else { this.uninstallMenubar(); @@ -277,6 +287,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (from === this.location) { this.onDidDeregisterViewContainer(container); } + if (to === this.location) { this.onDidRegisterViewContainers([container]); } @@ -306,6 +317,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private onDidViewContainerVisible(id: string): void { const viewContainer = this.getViewContainer(id); if (viewContainer) { + // Update the composite bar by adding this.compositeBar.addComposite(viewContainer); this.compositeBar.activateComposite(viewContainer.id); @@ -313,7 +325,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (viewContainer.hideIfEmpty) { const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer); if (viewContainerModel.activeViewDescriptors.length === 0) { - this.hideComposite(viewContainer.id); // Update the composite bar by hiding + // Update the composite bar by hiding + this.hideComposite(viewContainer.id); } } } @@ -339,6 +352,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { if (typeof priority !== 'number') { priority = 0; } + const activity: ICompositeActivity = { badge, clazz, priority }; const activityCache = activityId === GLOBAL_ACTIVITY_ID ? this.globalActivity : this.accountsActivity; @@ -387,16 +401,18 @@ export class ActivitybarPart extends Part implements IActivityBarService { private getCumulativeNumberBadge(activityCache: ICompositeActivity[], priority: number): NumberBadge { const numberActivities = activityCache.filter(activity => activity.badge instanceof NumberBadge && activity.priority === priority); - let number = numberActivities.reduce((result, activity) => { return result + (activity.badge).number; }, 0); - let descriptorFn = (): string => { + const number = numberActivities.reduce((result, activity) => { return result + (activity.badge).number; }, 0); + const descriptorFn = (): string => { return numberActivities.reduce((result, activity, index) => { result = result + (activity.badge).getDescription(); if (index < numberActivities.length - 1) { - result = result + '\n'; + result = `${result}\n`; } + return result; }, ''); }; + return new NumberBadge(number, descriptorFn); } @@ -414,11 +430,19 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private installMenubar() { + if (this.menuBar) { + return; // prevent menu bar from installing twice #110720 + } + this.menuBarContainer = document.createElement('div'); this.menuBarContainer.classList.add('menubar'); const content = assertIsDefined(this.content); - content.prepend(this.menuBarContainer); + if (this.homeBarContainer) { + content.insertBefore(this.menuBarContainer, this.homeBarContainer.nextSibling); + } else { + content.prepend(this.menuBarContainer); + } // Menubar: install a custom menu bar depending on configuration this.menuBar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); @@ -442,12 +466,12 @@ export class ActivitybarPart extends Part implements IActivityBarService { codicon = Codicon.code; } - this.createHomeBar(homeIndicator.href, homeIndicator.title, codicon); + this.createHomeBar(homeIndicator.href, codicon); this.onDidChangeHomeBarVisibility(); } // Install menubar if compact - if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') { + if (getMenuBarVisibility(this.configurationService) === 'compact') { this.installMenubar(); } @@ -456,11 +480,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { // Global action bar this.globalActivitiesContainer = document.createElement('div'); - this.globalActivitiesContainer.classList.add('global-activity'); this.content.appendChild(this.globalActivitiesContainer); this.createGlobalActivityActionBar(this.globalActivitiesContainer); + // Keyboard Navigation this.registerKeyboardNavigationListeners(); return this.content; @@ -528,23 +552,19 @@ export class ActivitybarPart extends Part implements IActivityBarService { } })); } - - - } - private createHomeBar(href: string, title: string, icon: Codicon): void { + private createHomeBar(href: string, icon: Codicon): void { this.homeBarContainer = document.createElement('div'); this.homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home")); this.homeBarContainer.setAttribute('role', 'toolbar'); this.homeBarContainer.classList.add('home-bar'); this.homeBar = this._register(new ActionBar(this.homeBarContainer, { + actionViewItemProvider: action => this.instantiationService.createInstance(HomeActivityActionViewItem, href, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)), orientation: ActionsOrientation.VERTICAL, - animated: false, ariaLabel: nls.localize('home', "Home"), - actionViewItemProvider: action => new HomeActionViewItem(action), - allowContextMenu: true, + animated: false, preventLoopNavigation: true, ignoreOrientationForPreviousAndNextKey: true })); @@ -552,35 +572,15 @@ export class ActivitybarPart extends Part implements IActivityBarService { const homeBarIconBadge = document.createElement('div'); homeBarIconBadge.classList.add('home-bar-icon-badge'); this.homeBarContainer.appendChild(homeBarIconBadge); - this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, href, title, icon))); + + this.homeBar.push(this._register(new ActivityAction({ + id: 'workbench.actions.home', + name: nls.localize('home', "Home"), + cssClass: icon.classNames + }))); const content = assertIsDefined(this.content); - content.prepend(this.homeBarContainer); - } - - updateStyles(): void { - super.updateStyles(); - - const container = assertIsDefined(this.getContainer()); - const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || ''; - container.style.backgroundColor = background; - - const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || ''; - container.classList.toggle('bordered', !!borderColor); - container.style.borderColor = borderColor ? borderColor : ''; - } - - private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors { - return { - activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND), - inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND), - activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER), - activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND), - badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), - badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), - dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER), - activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined, - }; + content.appendChild(this.homeBarContainer); } private createGlobalActivityActionBar(container: HTMLElement): void { @@ -591,7 +591,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } if (action.id === 'workbench.actions.accounts') { - return this.instantiationService.createInstance(AccountsActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)); + return this.instantiationService.createInstance(AccountsActivityActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)); } throw new Error(`No view item for action '${action.id}'`); @@ -603,18 +603,18 @@ export class ActivitybarPart extends Part implements IActivityBarService { ignoreOrientationForPreviousAndNextKey: true })); - this.globalActivityAction = new ActivityAction({ + this.globalActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.manage', name: nls.localize('manage', "Manage"), - cssClass: Codicon.settingsGear.classNames - }); + cssClass: ThemeIcon.asClassName(settingsViewBarIcon) + })); if (this.accountsVisibilityPreference) { - this.accountsActivityAction = new ActivityAction({ + this.accountsActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.accounts', name: nls.localize('accounts', "Accounts"), - cssClass: Codicon.account.classNames - }); + cssClass: ThemeIcon.asClassName(accountsViewBarIcon) + })); this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); } @@ -628,11 +628,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX); this.accountsActivityAction = undefined; } else { - this.accountsActivityAction = new ActivityAction({ + this.accountsActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.accounts', name: nls.localize('accounts', "Accounts"), cssClass: Codicon.account.classNames - }); + })); this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); } } @@ -723,7 +723,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { return ActivitybarPart.toActivity(id, name, icon, focusCommand?.id || id); } - private static toActivity(id: string, name: string, icon: URI | string | undefined, keybindingId: string | undefined): IActivity { + private static toActivity(id: string, name: string, icon: URI | ThemeIcon | undefined, keybindingId: string | undefined): IActivity { let cssClass: string | undefined = undefined; let iconUrl: URI | undefined = undefined; if (URI.isUri(icon)) { @@ -736,9 +736,10 @@ export class ActivitybarPart extends Part implements IActivityBarService { -webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 24px; `); - } else if (isString(icon)) { - cssClass = icon; + } else if (ThemeIcon.isThemeIcon(icon)) { + cssClass = ThemeIcon.asClassName(icon); } + return { id, name, cssClass, iconUrl, keybindingId }; } @@ -810,6 +811,35 @@ export class ActivitybarPart extends Part implements IActivityBarService { .map(v => v.id); } + focusActivityBar(): void { + this.compositeBar.focus(); + } + + updateStyles(): void { + super.updateStyles(); + + const container = assertIsDefined(this.getContainer()); + const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || ''; + container.style.backgroundColor = background; + + const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || ''; + container.classList.toggle('bordered', !!borderColor); + container.style.borderColor = borderColor ? borderColor : ''; + } + + private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors { + return { + activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND), + inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND), + activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER), + activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND), + badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), + badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), + dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER), + activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined, + }; + } + layout(width: number, height: number): void { if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) { return; @@ -834,6 +864,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private getViewContainer(id: string): ViewContainer | undefined { const viewContainer = this.viewDescriptorService.getViewContainerById(id); + return viewContainer && this.viewDescriptorService.getViewContainerLocation(viewContainer) === this.location ? viewContainer : undefined; } @@ -841,7 +872,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { return this.viewDescriptorService.getViewContainersByLocation(this.location); } - private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { + private onDidStorageValueChange(e: IStorageValueChangeEvent): void { if (e.key === ActivitybarPart.PINNED_VIEW_CONTAINERS && e.scope === StorageScope.GLOBAL && this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) { this._pinnedViewContainersValue = undefined; @@ -870,11 +901,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.compositeBar.setCompositeBarItems(newCompositeItems); } - if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) { + if (e.key === HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) { this.onDidChangeHomeBarVisibility(); } - if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) { + if (e.key === AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) { this.toggleAccountsActivity(); } } @@ -917,12 +948,13 @@ export class ActivitybarPart extends Part implements IActivityBarService { const cachedViewContainer = this._cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0]; if (cachedViewContainer) { cachedViewContainer.name = placeholderViewContainer.name; - cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS : + cachedViewContainer.icon = placeholderViewContainer.themeIcon ? ThemeIcon.revive(placeholderViewContainer.themeIcon) : placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined; cachedViewContainer.views = placeholderViewContainer.views; } } } + return this._cachedViewContainers; } @@ -933,10 +965,11 @@ export class ActivitybarPart extends Part implements IActivityBarService { visible, order }))); + this.setPlaceholderViewContainers(cachedViewContainers.map(({ id, icon, name, views }) => ({ id, iconUrl: URI.isUri(icon) ? icon : undefined, - iconCSS: isString(icon) ? icon : undefined, + themeIcon: ThemeIcon.isThemeIcon(icon) ? icon : undefined, name, views }))); @@ -971,7 +1004,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private setStoredPinnedViewContainersValue(value: string): void { - this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL); + this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.USER); } private getPlaceholderViewContainers(): IPlaceholderViewContainer[] { @@ -1003,37 +1036,23 @@ export class ActivitybarPart extends Part implements IActivityBarService { } private setStoredPlaceholderViewContainersValue(value: string): void { - this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL); + this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.MACHINE); } private get homeBarVisibilityPreference(): boolean { - return this.storageService.getBoolean(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true); + return this.storageService.getBoolean(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true); } private set homeBarVisibilityPreference(value: boolean) { - this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL); + this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL, StorageTarget.USER); } private get accountsVisibilityPreference(): boolean { - return this.storageService.getBoolean(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true); + return this.storageService.getBoolean(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true); } private set accountsVisibilityPreference(value: boolean) { - this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL); - } - - private migrateFromOldCachedViewContainersValue(): void { - const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL); - if (value !== undefined) { - const storedStates: Array = JSON.parse(value); - const cachedViewContainers = storedStates.map(c => { - const serialized: ICachedViewContainer = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true, name: undefined, icon: undefined, views: undefined } : c; - serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible; - return serialized; - }); - this.storeCachedViewContainersState(cachedViewContainers); - this.storageService.remove('workbench.activity.pinnedViewlets', StorageScope.GLOBAL); - } + this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL, StorageTarget.USER); } toJSON(): object { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 39fa9ebd..57d6f2a8 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -9,10 +9,6 @@ margin-bottom: 4px; } -.monaco-workbench .activitybar > .content > .home-bar > .monaco-action-bar .action-item { - margin-bottom: 0; -} - .monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::before, .monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::after { position: absolute; diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css index ced2d815..4f2d86b1 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -51,10 +51,6 @@ position: relative; width: 100%; height: 48px; - display: flex; - align-items: center; - justify-content: center; - order: -1; } .monaco-workbench .activitybar > .content > .home-bar > .home-bar-icon-badge { @@ -88,10 +84,6 @@ margin-bottom: auto; } -.monaco-workbench .activitybar > .content > .composite-bar-excess { - height: 100%; -} - /** Menu Bar */ .monaco-workbench .activitybar .menubar { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 5c481faf..1350ddfe 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -18,7 +18,7 @@ import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite'; import { IComposite } from 'vs/workbench/common/composite'; import { CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -199,7 +199,7 @@ export abstract class CompositePart extends Part { // Store in preferences const id = this.activeComposite.getId(); if (id !== this.defaultCompositeId) { - this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE); + this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(this.activeCompositeSettingsKey, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts new file mode 100644 index 00000000..b5fc1d1f --- /dev/null +++ b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IDialogHandler, IDialogResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IDialogsModel, IDialogViewItem } from 'vs/workbench/common/dialogs'; +import { BrowserDialogHandler } from 'vs/workbench/browser/parts/dialogs/dialogHandler'; +import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution { + private impl: IDialogHandler; + + private model: IDialogsModel; + private currentDialog: IDialogViewItem | undefined; + + constructor( + @IDialogService private dialogService: IDialogService, + @ILogService logService: ILogService, + @ILayoutService layoutService: ILayoutService, + @IThemeService themeService: IThemeService, + @IKeybindingService keybindingService: IKeybindingService, + @IProductService productService: IProductService, + @IClipboardService clipboardService: IClipboardService + ) { + super(); + + this.impl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService); + + this.model = (this.dialogService as DialogService).model; + + this._register(this.model.onDidShowDialog(() => { + if (!this.currentDialog) { + this.processDialogs(); + } + })); + + this.processDialogs(); + } + + private async processDialogs(): Promise { + while (this.model.dialogs.length) { + this.currentDialog = this.model.dialogs[0]; + + let result: IDialogResult | undefined = undefined; + if (this.currentDialog.args.confirmArgs) { + const args = this.currentDialog.args.confirmArgs; + result = await this.impl.confirm(args.confirmation); + } else if (this.currentDialog.args.inputArgs) { + const args = this.currentDialog.args.inputArgs; + result = await this.impl.input(args.severity, args.message, args.buttons, args.inputs, args.options); + } else if (this.currentDialog.args.showArgs) { + const args = this.currentDialog.args.showArgs; + result = await this.impl.show(args.severity, args.message, args.buttons, args.options); + } else { + await this.impl.about(); + } + + this.currentDialog.close(result); + this.currentDialog = undefined; + } + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts similarity index 91% rename from src/vs/workbench/services/dialogs/browser/dialogService.ts rename to src/vs/workbench/browser/parts/dialogs/dialogHandler.ts index 85d83f37..fe65e6da 100644 --- a/src/vs/workbench/services/dialogs/browser/dialogService.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IDialogService, IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput, IDialogHandler } from 'vs/platform/dialogs/common/dialogs'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; import Severity from 'vs/base/common/severity'; @@ -17,12 +17,9 @@ import { EventHelper } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IProductService } from 'vs/platform/product/common/productService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { fromNow } from 'vs/base/common/date'; -export class DialogService implements IDialogService { - - declare readonly _serviceBrand: undefined; +export class BrowserDialogHandler implements IDialogHandler { private static readonly ALLOWABLE_COMMANDS = [ 'copy', @@ -91,7 +88,7 @@ export class DialogService implements IDialogService { keyEventProcessor: (event: StandardKeyboardEvent) => { const resolved = this.keybindingService.softDispatch(event, this.layoutService.container); if (resolved && resolved.commandId) { - if (DialogService.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) { + if (BrowserDialogHandler.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) { EventHelper.stop(event, true); } } @@ -144,5 +141,3 @@ export class DialogService implements IDialogService { } } } - -registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index e9bbf8c7..2de9f55a 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -20,7 +20,7 @@ import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/commo import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; -import { BinarySize } from 'vs/platform/files/common/files'; +import { ByteSize } from 'vs/platform/files/common/files'; export interface IOpenCallbacks { openInternal: (input: EditorInput, options: EditorOptions | undefined) => Promise; @@ -182,7 +182,7 @@ interface ResourceViewerDelegate { class ResourceViewer { - private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally + private static readonly MAX_OPEN_INTERNAL_SIZE = ByteSize.MB * 200; // max size until we offer an action to open internally static show( descriptor: IResourceDescriptor, @@ -211,7 +211,7 @@ class FileTooLargeFileView { scrollbar: DomScrollableElement, delegate: ResourceViewerDelegate ) { - const size = BinarySize.formatSize(descriptorSize); + const size = ByteSize.formatSize(descriptorSize); delegate.metadataClb(size); clearNode(container); @@ -233,7 +233,7 @@ class FileSeemsBinaryFileView { scrollbar: DomScrollableElement, delegate: ResourceViewerDelegate ) { - delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : ''); + delegate.metadataClb(typeof descriptor.size === 'number' ? ByteSize.formatSize(descriptor.size) : ''); clearNode(container); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index b743f504..541ea9b2 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -491,7 +491,7 @@ export class BreadcrumbsControl { if (element instanceof FileElement) { if (element.kind === FileKind.FILE) { // open file in any editor - this._editorService.openEditor({ resource: element.uri, options: { pinned: pinned } }, group); + this._editorService.openEditor({ resource: element.uri, options: { pinned } }, group); } else { // show next picker let items = this._widget.getItems(); @@ -508,7 +508,8 @@ export class BreadcrumbsControl { resource: model.uri, options: { selection: Range.collapseToStart(element.symbol.selectionRange), - selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, + pinned } }, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP); } @@ -753,13 +754,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // open symbol in editor return editors.openEditor({ resource: outlineElement.uri, - options: { selection: Range.collapseToStart(element.symbol.selectionRange) } + options: { selection: Range.collapseToStart(element.symbol.selectionRange), pinned: true } }, SIDE_GROUP); } else if (element && URI.isUri(element.resource)) { // open file in editor return editors.openEditor({ resource: element.resource, + options: { pinned: true } }, SIDE_GROUP); } else { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index d1585c4d..001398ec 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -99,7 +99,7 @@ export abstract class BreadcrumbsPicker { this._treeContainer = document.createElement('div'); this._treeContainer.style.background = color ? color.toString() : ''; this._treeContainer.style.paddingTop = '2px'; - this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getColorTheme().getColor(widgetShadow)}`; + this._treeContainer.style.boxShadow = `0 0 8px 2px ${this._themeService.getColorTheme().getColor(widgetShadow)}`; this._domNode.appendChild(this._treeContainer); this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 }; @@ -436,7 +436,6 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { } protected _getTargetFromEvent(element: any): any | undefined { - // todo@joh if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) { return new FileElement((element as IFileStat).resource, FileKind.FILE); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 05f02021..fdb0a57a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -55,6 +55,8 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess, AllEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { FileAccess } from 'vs/base/common/network'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -238,27 +240,27 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized); if (primaryInput && secondaryInput) { - return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput); + return this.createEditorInput(instantiationService, deserialized.name, deserialized.description, secondaryInput, primaryInput); } } return undefined; } - protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; + protected abstract createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput; } class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return new SideBySideEditorInput(name, description, secondaryInput, primaryInput); } } class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { - return new DiffEditorInput(name, description, secondaryInput, primaryInput); + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + return instantiationService.createInstance(DiffEditorInput, name, description, secondaryInput, primaryInput, undefined); } } @@ -462,6 +464,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands. MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_KEEP_EDITORS_COMMAND_ID, title: nls.localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; } @@ -494,14 +497,14 @@ appendEditorToolItem( { id: SplitEditorAction.ID, title: nls.localize('splitEditorRight', "Split Editor Right"), - icon: { id: 'codicon/split-horizontal' } + icon: Codicon.splitHorizontal }, ContextKeyExpr.not('splitEditorsVertically'), 100000, // towards the end { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitEditorDown', "Split Editor Down"), - icon: { id: 'codicon/split-vertical' } + icon: Codicon.splitVertical } ); @@ -509,14 +512,14 @@ appendEditorToolItem( { id: SplitEditorAction.ID, title: nls.localize('splitEditorDown', "Split Editor Down"), - icon: { id: 'codicon/split-vertical' } + icon: Codicon.splitVertical }, ContextKeyExpr.has('splitEditorsVertically'), 100000, // towards the end { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitEditorRight', "Split Editor Right"), - icon: { id: 'codicon/split-horizontal' } + icon: Codicon.splitHorizontal } ); @@ -525,14 +528,14 @@ appendEditorToolItem( { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close' } + icon: Codicon.close }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext.toNegated()), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All"), - icon: { id: 'codicon/close-all' } + icon: Codicon.closeAll } ); @@ -541,14 +544,14 @@ appendEditorToolItem( { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close-dirty' } + icon: Codicon.closeDirty }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext.toNegated()), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All"), - icon: { id: 'codicon/close-all' } + icon: Codicon.closeAll } ); @@ -557,14 +560,14 @@ appendEditorToolItem( { id: editorCommands.UNPIN_EDITOR_COMMAND_ID, title: nls.localize('unpin', "Unpin"), - icon: { id: 'codicon/pinned' } + icon: Codicon.pinned }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close' } + icon: Codicon.close } ); @@ -573,23 +576,28 @@ appendEditorToolItem( { id: editorCommands.UNPIN_EDITOR_COMMAND_ID, title: nls.localize('unpin', "Unpin"), - icon: { id: 'codicon/pinned-dirty' } + icon: Codicon.pinnedDirty }, ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext), 1000000, // towards the far end { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close"), - icon: { id: 'codicon/close' } + icon: Codicon.close } ); +const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, nls.localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); +const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, nls.localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); +const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, nls.localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.')); + + // Diff Editor Title Menu: Previous Change appendEditorToolItem( { id: editorCommands.GOTO_PREVIOUS_CHANGE, title: nls.localize('navigate.prev.label', "Previous Change"), - icon: { id: 'codicon/arrow-up' } + icon: previousChangeIcon }, TextCompareEditorActiveContext, 10 @@ -600,7 +608,7 @@ appendEditorToolItem( { id: editorCommands.GOTO_NEXT_CHANGE, title: nls.localize('navigate.next.label', "Next Change"), - icon: { id: 'codicon/arrow-down' } + icon: nextChangeIcon }, TextCompareEditorActiveContext, 11 @@ -611,7 +619,7 @@ appendEditorToolItem( { id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, title: nls.localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"), - icon: { id: 'codicon/whitespace' } + icon: toggleWhitespace }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)), 20 @@ -622,7 +630,7 @@ appendEditorToolItem( { id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, title: nls.localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"), - icon: { id: 'codicon/whitespace~disabled' } + icon: ThemeIcon.modify(toggleWhitespace, 'disabled') }, ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)), 20 diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index ef35a87e..c05ce6d7 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -38,7 +38,8 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { openSideBySideDirection: 'right', closeEmptyGroups: true, labelFormat: 'default', - splitSizing: 'distribute' + splitSizing: 'distribute', + splitOnDragAndDrop: true }; export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean { @@ -64,12 +65,12 @@ export interface IEditorOpeningEvent extends IEditorIdentifier { /** * The options used when opening the editor. */ - options?: IEditorOptions; + readonly options?: IEditorOptions; /** * Context indicates how the editor open event is initialized. */ - context?: OpenEditorContext; + readonly context?: OpenEditorContext; /** * Allows to prevent the opening of an editor by providing a callback diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 4b62e947..dcece534 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -7,22 +7,26 @@ import * as nls from 'vs/nls'; import { isObject, isString, isUndefined, isNumber, withNullAsUndefined } from 'vs/base/common/types'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder } from 'vs/workbench/common/editor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder, viewColumnToEditorGroup, EditorGroupColumn } from 'vs/workbench/common/editor'; +import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IListService } from 'vs/platform/list/browser/listService'; +import { IListService, IOpenEvent } from 'vs/platform/list/browser/listService'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { distinct, coalesce } from 'vs/base/common/arrays'; import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; @@ -36,6 +40,7 @@ export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOt export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor'; export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups'; export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor'; +export const TOGGLE_KEEP_EDITORS_COMMAND_ID = 'workbench.action.toggleKeepEditors'; export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup'; export const PIN_EDITOR_COMMAND_ID = 'workbench.action.pinEditor'; @@ -44,6 +49,9 @@ export const UNPIN_EDITOR_COMMAND_ID = 'workbench.action.unpinEditor'; export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide'; export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange'; export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange'; +export const DIFF_FOCUS_PRIMARY_SIDE = 'workbench.action.compareEditor.focusPrimarySide'; +export const DIFF_FOCUS_SECONDARY_SIDE = 'workbench.action.compareEditor.focusSecondarySide'; +export const DIFF_FOCUS_OTHER_SIDE = 'workbench.action.compareEditor.focusOtherSide'; export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace'; export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp'; @@ -58,6 +66,10 @@ export const FOCUS_BELOW_GROUP_WITHOUT_WRAP_COMMAND_ID = 'workbench.action.focus export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex'; +export const API_OPEN_EDITOR_COMMAND_ID = '_workbench.open'; +export const API_OPEN_DIFF_EDITOR_COMMAND_ID = '_workbench.diff'; +export const API_OPEN_WITH_EDITOR_COMMAND_ID = '_workbench.openWith'; + export interface ActiveEditorMoveArguments { to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next'; by: 'tab' | 'group'; @@ -227,13 +239,45 @@ function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisi } function registerEditorGroupsLayoutCommand(): void { - CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => { - if (!args || typeof args !== 'object') { + + function applyEditorLayout(accessor: ServicesAccessor, layout: EditorGroupLayout): void { + if (!layout || typeof layout !== 'object') { return; } const editorGroupService = accessor.get(IEditorGroupsService); - editorGroupService.applyLayout(args); + editorGroupService.applyLayout(layout); + } + + CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => { + applyEditorLayout(accessor, args); + }); + + // API Command + CommandsRegistry.registerCommand({ + id: 'vscode.setEditorLayout', + handler: (accessor: ServicesAccessor, args: EditorGroupLayout) => applyEditorLayout(accessor, args), + description: { + description: 'Set Editor Layout', + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['groups'], + 'properties': { + 'orientation': { + 'type': 'number', + 'default': 0, + 'enum': [0, 1] + }, + 'groups': { + '$ref': '#/definitions/editorGroupsSchema', + 'default': [{}, {}] + } + } + } + }] + } }); } @@ -265,30 +309,68 @@ function registerDiffEditorCommands(): void { handler: accessor => navigateInDiffEditor(accessor, false) }); - function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { + function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined { const editorService = accessor.get(IEditorService); - const candidates = [editorService.activeEditorPane, ...editorService.visibleEditorPanes].filter(editor => editor instanceof TextDiffEditor); - if (candidates.length > 0) { - const navigator = (candidates[0]).getDiffNavigator(); + for (const editor of [editorService.activeEditorPane, ...editorService.visibleEditorPanes]) { + if (editor instanceof TextDiffEditor) { + return editor; + } + } + + return undefined; + } + + function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + const navigator = activeTextDiffEditor.getDiffNavigator(); if (navigator) { next ? navigator.next() : navigator.previous(); } } } + enum FocusTextDiffEditorMode { + Original, + Modified, + Toggle + } + + function focusInDiffEditor(accessor: ServicesAccessor, mode: FocusTextDiffEditorMode): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + switch (mode) { + case FocusTextDiffEditorMode.Original: + activeTextDiffEditor.getControl()?.getOriginalEditor().focus(); + break; + case FocusTextDiffEditorMode.Modified: + activeTextDiffEditor.getControl()?.getModifiedEditor().focus(); + break; + case FocusTextDiffEditorMode.Toggle: + if (activeTextDiffEditor.getControl()?.getModifiedEditor().hasWidgetFocus()) { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original); + } else { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified); + } + } + } + } + function toggleDiffSideBySide(accessor: ServicesAccessor): void { const configurationService = accessor.get(IConfigurationService); const newValue = !configurationService.getValue('diffEditor.renderSideBySide'); - configurationService.updateValue('diffEditor.renderSideBySide', newValue, ConfigurationTarget.USER); + configurationService.updateValue('diffEditor.renderSideBySide', newValue); } function toggleDiffIgnoreTrimWhitespace(accessor: ServicesAccessor): void { const configurationService = accessor.get(IConfigurationService); const newValue = !configurationService.getValue('diffEditor.ignoreTrimWhitespace'); - configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue, ConfigurationTarget.USER); + configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue); } KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -299,6 +381,30 @@ function registerDiffEditorCommands(): void { handler: accessor => toggleDiffSideBySide(accessor) }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_PRIMARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_SECONDARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_OTHER_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Toggle) + }); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, @@ -320,6 +426,88 @@ function registerDiffEditorCommands(): void { }); } +function registerOpenEditorAPICommands(): void { + + function mixinContext(context: IOpenEvent | undefined, options: ITextEditorOptions | undefined, column: EditorGroupColumn | undefined): [ITextEditorOptions | undefined, EditorGroupColumn | undefined] { + if (!context) { + return [options, column]; + } + + return [ + { ...context.editorOptions, ...(options ?? Object.create(null)) }, + context.sideBySide ? SIDE_GROUP : column + ]; + } + + CommandsRegistry.registerCommand(API_OPEN_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, resourceArg: UriComponents, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], label?: string, context?: IOpenEvent) { + const editorService = accessor.get(IEditorService); + const editorGroupService = accessor.get(IEditorGroupsService); + const openerService = accessor.get(IOpenerService); + + const resource = URI.revive(resourceArg); + const [columnArg, optionsArg] = columnAndOptions ?? []; + + // use editor options or editor view column as a hint to use the editor service for opening + if (optionsArg || typeof columnArg === 'number') { + const [options, column] = mixinContext(context, optionsArg, columnArg); + + await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, column)); + } + + // do not allow to execute commands from here + else if (resource.scheme === 'command') { + return; + } + + // finally, delegate to opener service + else { + await openerService.open(resource, { openToSide: context?.sideBySide, editorOptions: context?.editorOptions }); + } + }); + + CommandsRegistry.registerCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, leftResource: UriComponents, rightResource: UriComponents, label?: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], context?: IOpenEvent) { + const editorService = accessor.get(IEditorService); + const editorGroupService = accessor.get(IEditorGroupsService); + + const [columnArg, optionsArg] = columnAndOptions ?? []; + const [options, column] = mixinContext(context, optionsArg, columnArg); + + await editorService.openEditor({ + leftResource: URI.revive(leftResource), + rightResource: URI.revive(rightResource), + label, + options + }, viewColumnToEditorGroup(editorGroupService, column)); + }); + + CommandsRegistry.registerCommand(API_OPEN_WITH_EDITOR_COMMAND_ID, (accessor: ServicesAccessor, resource: UriComponents, id: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?]) => { + const editorService = accessor.get(IEditorService); + const editorGroupsService = accessor.get(IEditorGroupsService); + const configurationService = accessor.get(IConfigurationService); + const quickInputService = accessor.get(IQuickInputService); + + const [columnArg, optionsArg] = columnAndOptions ?? []; + let group: IEditorGroup | undefined = undefined; + + if (columnArg === SIDE_GROUP) { + const direction = preferredSideBySideGroupDirection(configurationService); + + let neighbourGroup = editorGroupsService.findGroup({ direction }); + if (!neighbourGroup) { + neighbourGroup = editorGroupsService.addGroup(editorGroupsService.activeGroup, direction); + } + group = neighbourGroup; + } else { + group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, columnArg)) ?? editorGroupsService.activeGroup; + } + + const textOptions: ITextEditorOptions = optionsArg ? { ...optionsArg, override: false } : { override: false }; + + const input = editorService.createEditorInput({ resource: URI.revive(resource) }); + return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService); + }); +} + function registerOpenEditorAtIndexCommands(): void { const openEditorAtIndex: ICommandHandler = (accessor: ServicesAccessor, editorIndex: number): void => { const editorService = accessor.get(IEditorService); @@ -710,6 +898,31 @@ function registerOtherEditorCommands(): void { } }); + CommandsRegistry.registerCommand({ + id: TOGGLE_KEEP_EDITORS_COMMAND_ID, + handler: accessor => { + const configurationService = accessor.get(IConfigurationService); + const notificationService = accessor.get(INotificationService); + const openerService = accessor.get(IOpenerService); + + // Update setting + const currentSetting = configurationService.getValue('workbench.editor.enablePreview'); + const newSetting = currentSetting === true ? false : true; + configurationService.updateValue('workbench.editor.enablePreview', newSetting); + + // Inform user + notificationService.prompt( + Severity.Info, + newSetting ? + nls.localize('enablePreview', "Preview editors have been enabled in settings.") : + nls.localize('disablePreview', "Preview editors have been disabled in settings."), + [{ + label: nls.localize('learnMode', "Learn More"), run: () => openerService.open('https://go.microsoft.com/fwlink/?linkid=2147473') + }] + ); + } + }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: PIN_EDITOR_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, @@ -877,6 +1090,7 @@ export function setup(): void { registerActiveEditorMoveCommand(); registerEditorGroupsLayoutCommand(); registerDiffEditorCommands(); + registerOpenEditorAPICommands(); registerOpenEditorAtIndexCommands(); registerCloseEditorCommands(); registerOtherEditorCommands(); diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 9c48c54f..50f57156 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -12,7 +12,7 @@ import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; -import { GroupDirection, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupDirection, IEditorGroupsService, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -25,6 +25,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; +import { ByteSize } from 'vs/platform/files/common/files'; interface IDropOperation { splitDirection?: GroupDirection; @@ -34,7 +35,7 @@ class DropOverlay extends Themable { private static readonly OVERLAY_ID = 'monaco-workbench-editor-drop-overlay'; - private static readonly MAX_FILE_UPLOAD_SIZE = 100 * 1024 * 1024; // 100mb + private static readonly MAX_FILE_UPLOAD_SIZE = 100 * ByteSize.MB; private container: HTMLElement | undefined; private overlay: HTMLElement | undefined; @@ -54,7 +55,8 @@ class DropOverlay extends Themable { @IInstantiationService private instantiationService: IInstantiationService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @IEditorService private readonly editorService: IEditorService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService ) { super(themeService); @@ -143,8 +145,14 @@ class DropOverlay extends Themable { } } - // Position overlay - this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup); + // Position overlay and conditionally enable or disable + // editor group splitting support based on setting and + // keymodifiers used. + let splitOnDragAndDrop = !!this.editorGroupService.partOptions.splitOnDragAndDrop; + if (this.isToggleSplitOperation(e)) { + splitOnDragAndDrop = !splitOnDragAndDrop; + } + this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup, splitOnDragAndDrop); // Make sure to stop any running cleanup scheduler to remove the overlay if (this.cleanupOverlayScheduler.isScheduled()) { @@ -319,7 +327,7 @@ class DropOverlay extends Themable { // Try to come up with a good file path for the untitled // editor by asking the file dialog service for the default let proposedFilePath: URI | undefined = undefined; - const defaultFilePath = this.fileDialogService.defaultFilePath(); + const defaultFilePath = await this.fileDialogService.defaultFilePath(); if (defaultFilePath) { proposedFilePath = joinPath(defaultFilePath, name); } @@ -362,24 +370,33 @@ class DropOverlay extends Themable { return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh); } - private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean): void { + private isToggleSplitOperation(e: DragEvent): boolean { + return (e.altKey && !isMacintosh) || (e.shiftKey && isMacintosh); + } + + private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean, enableSplitting: boolean): void { const preferSplitVertically = this.accessor.partOptions.openSideBySideDirection === 'right'; const editorControlWidth = this.groupView.element.clientWidth; const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight(); let edgeWidthThresholdFactor: number; - if (isDraggingGroup) { - edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction - } else { - edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors - } - let edgeHeightThresholdFactor: number; - if (isDraggingGroup) { - edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction + if (enableSplitting) { + if (isDraggingGroup) { + edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction + } else { + edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors + } + + if (isDraggingGroup) { + edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction + } else { + edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors + } } else { - edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors + edgeWidthThresholdFactor = 0; + edgeHeightThresholdFactor = 0; } const edgeWidthThreshold = editorControlWidth * edgeWidthThresholdFactor; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index d01c17c4..a212456d 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1742,28 +1742,17 @@ class EditorOpeningEvent implements IEditorOpeningEvent { private override: (() => Promise) | undefined = undefined; constructor( - private _group: GroupIdentifier, - private _editor: EditorInput, + public readonly groupId: GroupIdentifier, + public readonly editor: EditorInput, private _options: EditorOptions | undefined, - private _context: OpenEditorContext | undefined + public readonly context: OpenEditorContext | undefined ) { } - get groupId(): GroupIdentifier { - return this._group; - } - - get editor(): EditorInput { - return this._editor; - } - get options(): EditorOptions | undefined { return this._options; } - get context(): OpenEditorContext | undefined { - return this._context; - } prevent(callback: () => Promise): void { this.override = callback; @@ -1775,9 +1764,9 @@ class EditorOpeningEvent implements IEditorOpeningEvent { } export interface EditorReplacement { - editor: EditorInput; - replacement: EditorInput; - options?: EditorOptions; + readonly editor: EditorInput; + readonly replacement: EditorInput; + readonly options?: EditorOptions; } registerThemingParticipant((theme, collector, environment) => { diff --git a/src/vs/workbench/browser/parts/editor/editorPane.ts b/src/vs/workbench/browser/parts/editor/editorPane.ts index 07a33a40..92dc1975 100644 --- a/src/vs/workbench/browser/parts/editor/editorPane.ts +++ b/src/vs/workbench/browser/parts/editor/editorPane.ts @@ -9,7 +9,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorGroup, IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { LRUCache, Touch } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; @@ -156,7 +156,7 @@ export abstract class EditorPane extends Composite implements IEditorPane { let editorMemento = EditorPane.EDITOR_MEMENTOS.get(mementoKey); if (!editorMemento) { - editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService); + editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE), limit, editorGroupService); EditorPane.EDITOR_MEMENTOS.set(mementoKey, editorMemento); } @@ -220,18 +220,7 @@ export class EditorMemento implements IEditorMemento { // Automatically clear when editor input gets disposed if any if (resourceOrEditor instanceof EditorInput) { - const editor = resourceOrEditor; - - if (!this.editorDisposables) { - this.editorDisposables = new Map(); - } - - if (!this.editorDisposables.has(editor)) { - this.editorDisposables.set(editor, Event.once(resourceOrEditor.onDispose)(() => { - this.clearEditorState(resource); - this.editorDisposables?.delete(editor); - })); - } + this.clearEditorStateOnDispose(resource, resourceOrEditor); } } @@ -240,7 +229,7 @@ export class EditorMemento implements IEditorMemento { loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, fallbackToOtherGroupState?: boolean): T | undefined { const resource = this.doGetResource(resourceOrEditor); if (!resource || !group) { - return undefined; // we are not in a good state to load any state for a resource + return; // we are not in a good state to load any state for a resource } const cache = this.doLoad(); @@ -261,12 +250,16 @@ export class EditorMemento implements IEditorMemento { } } - return undefined; + return; } clearEditorState(resource: URI, group?: IEditorGroup): void; clearEditorState(editor: EditorInput, group?: IEditorGroup): void; clearEditorState(resourceOrEditor: URI | EditorInput, group?: IEditorGroup): void { + if (resourceOrEditor instanceof EditorInput) { + this.editorDisposables?.delete(resourceOrEditor); + } + const resource = this.doGetResource(resourceOrEditor); if (resource) { const cache = this.doLoad(); @@ -286,6 +279,19 @@ export class EditorMemento implements IEditorMemento { } } + clearEditorStateOnDispose(resource: URI, editor: EditorInput): void { + if (!this.editorDisposables) { + this.editorDisposables = new Map(); + } + + if (!this.editorDisposables.has(editor)) { + this.editorDisposables.set(editor, Event.once(editor.onDispose)(() => { + this.clearEditorState(resource); + this.editorDisposables?.delete(editor); + })); + } + } + moveEditorState(source: URI, target: URI, comparer: IExtUri): void { const cache = this.doLoad(); @@ -342,7 +348,7 @@ export class EditorMemento implements IEditorMemento { saveState(): void { const cache = this.doLoad(); - // Cleanup once during shutdown + // Cleanup once during session if (!this.cleanedUp) { this.cleanUp(); this.cleanedUp = true; diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index e6e216a5..82815689 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -19,7 +19,7 @@ import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsE import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; @@ -148,8 +148,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.gridWidgetView = new GridWidgetView(); - this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE); - this.globalMemento = this.getMemento(StorageScope.GLOBAL); + this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + this.globalMemento = this.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); this._whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve)); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index bfa6f87d..1d85f0ad 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/editorstatus'; import * as nls from 'vs/nls'; import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; -import { format, compare } from 'vs/base/common/strings'; +import { format, compare, splitLines } from 'vs/base/common/strings'; import { extname, basename, isEqual } from 'vs/base/common/resources'; import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -346,12 +346,12 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { [{ label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"), run: () => { - this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER); + this.configurationService.updateValue('editor.accessibilitySupport', 'on'); } }, { label: nls.localize('screenReaderDetectedExplanation.answerNo', "No"), run: () => { - this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER); + this.configurationService.updateValue('editor.accessibilitySupport', 'off'); } }], { sticky: true } @@ -918,7 +918,7 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable { this.currentMarker = this.getMarker(); if (this.hasToUpdateStatus(previousMarker, this.currentMarker)) { if (this.currentMarker) { - const line = this.currentMarker.message.split(/\r\n|\r|\n/g)[0]; + const line = splitLines(this.currentMarker.message)[0]; const text = `${this.getType(this.currentMarker)} ${line}`; if (!this.statusBarEntryAccessor.value) { this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ text: '', ariaLabel: '' }, 'statusbar.currentProblem', nls.localize('currentProblem', "Current Problem"), StatusbarAlignment.LEFT); @@ -1046,12 +1046,19 @@ export class ChangeModeAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextFileService private readonly textFileService: ITextFileService + @ITextFileService private readonly textFileService: ITextFileService, + @ICommandService private readonly commandService: ICommandService ) { super(actionId, actionLabel); } async run(): Promise { + const activeEditorPane = this.editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; + if (activeEditorPane?.isNotebookEditor) { + // it's inside notebook editor + return this.commandService.executeCommand('notebook.cell.changeLanguage'); + } + const activeTextEditorControl = getCodeEditor(this.editorService.activeTextEditorControl); if (!activeTextEditorControl) { await this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); @@ -1143,7 +1150,7 @@ export class ChangeModeAction extends Action { // User decided to configure settings for current language if (pick === configureModeSettings) { - this.preferencesService.openGlobalSettings(true, { editSetting: `[${withUndefinedAsNull(currentModeId)}]` }); + this.preferencesService.openGlobalSettings(true, { revealSetting: { key: `[${withUndefinedAsNull(currentModeId)}]`, edit: true } }); return; } diff --git a/src/vs/workbench/browser/parts/editor/editorWidgets.ts b/src/vs/workbench/browser/parts/editor/editorWidgets.ts index 5d721772..02601bc9 100644 --- a/src/vs/workbench/browser/parts/editor/editorWidgets.ts +++ b/src/vs/workbench/browser/parts/editor/editorWidgets.ts @@ -38,6 +38,8 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { super(); this._domNode = $('.floating-click-widget'); + this._domNode.style.padding = '10px'; + this._domNode.style.cursor = 'pointer'; if (keyBindingAction) { const keybinding = keybindingService.lookupKeybinding(keyBindingAction); diff --git a/src/vs/workbench/browser/parts/editor/editorsObserver.ts b/src/vs/workbench/browser/parts/editor/editorsObserver.ts index 2733dfb4..285653b9 100644 --- a/src/vs/workbench/browser/parts/editor/editorsObserver.ts +++ b/src/vs/workbench/browser/parts/editor/editorsObserver.ts @@ -5,7 +5,7 @@ import { IEditorInput, IEditorInputFactoryRegistry, IEditorIdentifier, GroupIdentifier, Extensions, IEditorPartOptionsChangeEvent, EditorsOrder, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { Event, Emitter } from 'vs/base/common/event'; import { IEditorGroupsService, IEditorGroup, GroupChangeKind, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -339,7 +339,7 @@ export class EditorsObserver extends Disposable { if (this.mostRecentEditorsMap.isEmpty()) { this.storageService.remove(EditorsObserver.STORAGE_KEY, StorageScope.WORKSPACE); } else { - this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE); + this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css index 98ddd9e9..3b8bac45 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css @@ -21,7 +21,7 @@ padding-right: 6px; } -/* todo@joh move somewhere else */ +/* breadcrumbs-picker-style */ .monaco-workbench .monaco-breadcrumbs-picker .arrow { position: absolute; diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index bed3f209..bcd835f4 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -589,8 +589,8 @@ export class TabsTitleControl extends TitleControl { const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index }); - const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner, }); - tabActionBar.onDidBeforeRun(e => { + const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner }); + tabActionBar.onBeforeRun(e => { if (e.action.id === this.closeEditorAction.id) { this.blockRevealActiveTabOnce(); } diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 115d6ee9..4557a2c4 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -24,7 +24,6 @@ import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/co import { DisposableStore } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { URI } from 'vs/base/common/uri'; -import { Event } from 'vs/base/common/event'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -32,6 +31,7 @@ import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/edit import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { isEqual } from 'vs/base/common/resources'; import { multibyteAwareBtoa } from 'vs/base/browser/dom'; +import { IFileService } from 'vs/platform/files/common/files'; /** * The text editor that leverages the diff text editor for the editing experience. @@ -62,9 +62,28 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @IEditorService editorService: IEditorService, @IThemeService themeService: IThemeService, - @IEditorGroupsService editorGroupService: IEditorGroupsService + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IFileService private readonly fileService: IFileService ) { super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService); + + // Listen to file system provider changes + this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onDidFileSystemProviderChange(e.scheme))); + this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onDidFileSystemProviderChange(e.scheme))); + } + + private onDidFileSystemProviderChange(scheme: string): void { + const control = this.getControl(); + const input = this.input; + + if (control && input instanceof DiffEditorInput) { + if (input.originalInput.resource?.scheme === scheme || input.modifiedInput.resource?.scheme === scheme) { + control.updateOptions({ + readOnly: input.modifiedInput.isReadonly(), + originalEditable: !input.originalInput.isReadonly() + }); + } + } } protected onWillCloseEditorInGroup(editor: IEditorInput): void { @@ -175,7 +194,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan const originalInput = input.originalInput; const modifiedInput = input.modifiedInput; - const binaryDiffInput = new DiffEditorInput(input.getName(), input.getDescription(), originalInput, modifiedInput, true); + const binaryDiffInput = this.instantiationService.createInstance(DiffEditorInput, input.getName(), input.getDescription(), originalInput, modifiedInput, true); // Forward binary flag to input if supported const fileEditorInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory(); @@ -217,16 +236,17 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Handle diff editor specially by merging in diffEditor configuration if (isObject(configuration.diffEditor)) { - // User settings defines `diffEditor.codeLens`, but there is also `editor.codeLens`. - // Due to the mixin, the two settings cannot be distinguished anymore. - // - // So we map `diffEditor.codeLens` to `diffEditor.originalCodeLens` and `diffEditor.modifiedCodeLens`. const diffEditorConfiguration = objects.deepClone(configuration.diffEditor); - diffEditorConfiguration.originalCodeLens = diffEditorConfiguration.codeLens; - diffEditorConfiguration.modifiedCodeLens = diffEditorConfiguration.codeLens; + + // User settings defines `diffEditor.codeLens`, but here we rename that to `diffEditor.diffCodeLens` to avoid collisions with `editor.codeLens`. + diffEditorConfiguration.diffCodeLens = diffEditorConfiguration.codeLens; delete diffEditorConfiguration.codeLens; - objects.mixin(editorConfiguration, diffEditorConfiguration); + // User settings defines `diffEditor.wordWrap`, but here we rename that to `diffEditor.diffWordWrap` to avoid collisions with `editor.wordWrap`. + diffEditorConfiguration.diffWordWrap = <'off' | 'on' | 'inherit' | undefined>diffEditorConfiguration.wordWrap; + delete diffEditorConfiguration.wordWrap; + + Object.assign(editorConfiguration, diffEditorConfiguration); } return editorConfiguration; @@ -309,12 +329,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan // Otherwise save it else { - super.saveTextEditorViewState(resource); - - // Make sure to clean up when the input gets disposed - Event.once(input.onDispose)(() => { - super.clearTextEditorViewState([resource], this.group); - }); + super.saveTextEditorViewState(resource, input); } } diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index f2a77542..9cae4d21 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -67,6 +67,7 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa @IEditorGroupsService protected editorGroupService: IEditorGroupsService ) { super(id, telemetryService, themeService, storageService); + this._instantiationService = instantiationService; this.editorMemento = this.getEditorMemento(editorGroupService, BaseTextEditor.TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100); @@ -223,13 +224,27 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa return this.editorControl; } - protected saveTextEditorViewState(resource: URI): void { + getViewState(): IEditorViewState | undefined { + const resource = this.input?.resource; + if (resource) { + return withNullAsUndefined(this.retrieveTextEditorViewState(resource)); + } + + return undefined; + } + + + protected saveTextEditorViewState(resource: URI, cleanUpOnDispose?: IEditorInput): void { const editorViewState = this.retrieveTextEditorViewState(resource); if (!editorViewState || !this.group) { return; } this.editorMemento.saveEditorState(this.group, resource, editorViewState); + + if (cleanUpOnDispose) { + this.editorMemento.clearEditorStateOnDispose(resource, cleanUpOnDispose); + } } protected shouldRestoreTextEditorViewState(editor: IEditorInput, context?: IEditorOpenContext): boolean { @@ -243,15 +258,6 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa return true; } - getViewState(): IEditorViewState | undefined { - const resource = this.input?.resource; - if (resource) { - return withNullAsUndefined(this.retrieveTextEditorViewState(resource)); - } - - return undefined; - } - protected retrieveTextEditorViewState(resource: URI): IEditorViewState | null { const control = this.getControl(); if (!isCodeEditor(control)) { @@ -284,9 +290,9 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa } protected clearTextEditorViewState(resources: URI[], group?: IEditorGroup): void { - resources.forEach(resource => { + for (const resource of resources) { this.editorMemento.clearEditorState(resource, group); - }); + } } private updateEditorConfiguration(configuration?: IEditorConfiguration): void { diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index a99323c2..2bb2640d 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -16,7 +16,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { Event } from 'vs/base/common/event'; import { ScrollType, IEditor } from 'vs/editor/common/editorCommon'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -158,12 +157,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor { // Otherwise save it else { - super.saveTextEditorViewState(resource); - - // Make sure to clean up when the input gets disposed - Event.once(input.onDispose)(() => { - super.clearTextEditorViewState([resource]); - }); + super.saveTextEditorViewState(resource, input); } } } diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index c6be6928..8bd0a402 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -105,18 +105,27 @@ overflow: hidden; } +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button-dropdown, +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button { + margin: 4px 5px; /* allows button focus outline to be visible */ +} + .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-button { + outline-offset: 2px !important; +} + +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button { width: fit-content; width: -moz-fit-content; padding: 5px 10px; - margin: 4px 5px; /* allows button focus outline to be visible */ + display: inline-block; /* to enable ellipsis in text overflow */ font-size: 12px; overflow: hidden; text-overflow: ellipsis; } -.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button { - display: inline-block; /* to enable ellipsis in text overflow */ +.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-dropdown-button { + padding: 5px } /** Notification: Progress */ diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css index f1ab3c2c..987f9a36 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css @@ -26,7 +26,7 @@ } .monaco-workbench > .notifications-toasts .notification-toast-container > .notification-toast { - margin: 5px; /* enables separation and drop shadows around toasts */ + margin: 8px; /* enables separation and drop shadows around toasts */ transform: translate3d(0px, 100%, 0px); /* move the notification 50px to the bottom (to prevent bleed through) */ opacity: 0; /* fade the toast in */ transition: transform 300ms ease-out, opacity 300ms ease-out; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts index 477b90cf..7aaa2506 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts @@ -12,14 +12,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { CLEAR_NOTIFICATION, EXPAND_NOTIFICATION, COLLAPSE_NOTIFICATION, CLEAR_ALL_NOTIFICATIONS, HIDE_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -const clearIcon = registerIcon('notifications-clear', Codicon.close); -const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll); -const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown); -const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp); -const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown); -const configureIcon = registerIcon('notifications-configure', Codicon.gear); +const clearIcon = registerIcon('notifications-clear', Codicon.close, localize('clearIcon', 'Icon for the clear action in notifications.')); +const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll, localize('clearAllIcon', 'Icon for the clear all action in notifications.')); +const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown, localize('hideIcon', 'Icon for the hide action in notifications.')); +const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp, localize('expandIcon', 'Icon for the expand action in notifications.')); +const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown, localize('collapseIcon', 'Icon for the collapse action in notifications.')); +const configureIcon = registerIcon('notifications-configure', Codicon.gear, localize('configureIcon', 'Icon for the configure action in notifications.')); export class ClearNotificationAction extends Action { @@ -31,7 +33,7 @@ export class ClearNotificationAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, clearIcon.classNames); + super(id, label, ThemeIcon.asClassName(clearIcon)); } async run(notification: INotificationViewItem): Promise { @@ -49,7 +51,7 @@ export class ClearAllNotificationsAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, clearAllIcon.classNames); + super(id, label, ThemeIcon.asClassName(clearAllIcon)); } async run(): Promise { @@ -67,7 +69,7 @@ export class HideNotificationsCenterAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, hideIcon.classNames); + super(id, label, ThemeIcon.asClassName(hideIcon)); } async run(): Promise { @@ -85,7 +87,7 @@ export class ExpandNotificationAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, expandIcon.classNames); + super(id, label, ThemeIcon.asClassName(expandIcon)); } async run(notification: INotificationViewItem): Promise { @@ -103,7 +105,7 @@ export class CollapseNotificationAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, collapseIcon.classNames); + super(id, label, ThemeIcon.asClassName(collapseIcon)); } async run(notification: INotificationViewItem): Promise { @@ -121,7 +123,7 @@ export class ConfigureNotificationAction extends Action { label: string, public readonly configurationActions: ReadonlyArray ) { - super(id, label, configureIcon.classNames); + super(id, label, ThemeIcon.asClassName(configureIcon)); } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 6e8b4e94..d4074bbe 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -251,7 +251,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente protected updateStyles(): void { if (this.notificationsCenterContainer && this.notificationsCenterHeader) { const widgetShadowColor = this.getColor(widgetShadow); - this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : ''; + this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : ''; const borderColor = this.getColor(NOTIFICATIONS_CENTER_BORDER); this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : ''; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index a935e757..20b12501 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -15,20 +15,20 @@ import { IListService, WorkbenchList } from 'vs/platform/list/browser/listServic // Center export const SHOW_NOTIFICATIONS_CENTER = 'notifications.showList'; export const HIDE_NOTIFICATIONS_CENTER = 'notifications.hideList'; -export const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList'; +const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList'; // Toasts -export const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts'; -export const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts'; -export const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast'; -export const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast'; -export const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast'; -export const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast'; +const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts'; +const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts'; +const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast'; +const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast'; +const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast'; +const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast'; // Notification export const COLLAPSE_NOTIFICATION = 'notification.collapse'; export const EXPAND_NOTIFICATION = 'notification.expand'; -export const TOGGLE_NOTIFICATION = 'notification.toggle'; +const TOGGLE_NOTIFICATION = 'notification.toggle'; export const CLEAR_NOTIFICATION = 'notification.clear'; export const CLEAR_ALL_NOTIFICATIONS = 'notifications.clearAll'; @@ -159,12 +159,20 @@ export function registerNotificationCommands(center: INotificationsCenterControl }); // Hide Toasts - KeybindingsRegistry.registerCommandAndKeybindingRule({ + CommandsRegistry.registerCommand(HIDE_NOTIFICATION_TOAST, accessor => toasts.hide()); + + KeybindingsRegistry.registerKeybindingRule({ id: HIDE_NOTIFICATION_TOAST, - weight: KeybindingWeight.WorkbenchContrib + 50, + weight: KeybindingWeight.WorkbenchContrib - 50, // lower when not focused (e.g. let editor suggest win over this command) when: NotificationsToastsVisibleContext, - primary: KeyCode.Escape, - handler: accessor => toasts.hide() + primary: KeyCode.Escape + }); + + KeybindingsRegistry.registerKeybindingRule({ + id: HIDE_NOTIFICATION_TOAST, + weight: KeybindingWeight.WorkbenchContrib + 100, // higher when focused + when: ContextKeyExpr.and(NotificationsToastsVisibleContext, NotificationFocusedContext), + primary: KeyCode.Escape }); // Focus Toasts diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 22f91523..6eb5f2ce 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -482,7 +482,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast toast.style.background = backgroundColor ? backgroundColor : ''; const widgetShadowColor = this.getColor(widgetShadow); - toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : ''; + toast.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : ''; const borderColor = this.getColor(NOTIFICATIONS_TOAST_BORDER); toast.style.border = borderColor ? `1px solid ${borderColor}` : ''; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index a78fd319..51272ab8 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -8,11 +8,11 @@ import { clearNode, addDisposableListener, EventType, EventHelper, $ } from 'vs/ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { ButtonGroup } from 'vs/base/browser/ui/button/button'; +import { ButtonBar } from 'vs/base/browser/ui/button/button'; import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { ActionRunner, ActionWithMenuAction, IAction, IActionRunner } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { dispose, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -285,7 +285,8 @@ export class NotificationTemplateRenderer extends Disposable { @IOpenerService private readonly openerService: IOpenerService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService private readonly themeService: IThemeService, - @IKeybindingService private readonly keybindingService: IKeybindingService + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, ) { super(); @@ -441,27 +442,40 @@ export class NotificationTemplateRenderer extends Disposable { const primaryActions = notification.actions ? notification.actions.primary : undefined; if (notification.expanded && isNonEmptyArray(primaryActions)) { - const buttonGroup = new ButtonGroup(this.template.buttonsContainer, primaryActions.length, { title: true /* assign titles to buttons in case they overflow */ }); - buttonGroup.buttons.forEach((button, index) => { - const action = primaryActions[index]; - button.label = action.label; - - this.inputDisposables.add(button.onDidClick(e => { - EventHelper.stop(e, true); - + const that = this; + const actionRunner: IActionRunner = new class extends ActionRunner { + protected async runAction(action: IAction): Promise { // Run action - this.actionRunner.run(action, notification); + that.actionRunner.run(action, notification); // Hide notification (unless explicitly prevented) if (!(action instanceof ChoiceAction) || !action.keepOpen) { notification.close(); } + } + }(); + const buttonToolbar = this.inputDisposables.add(new ButtonBar(this.template.buttonsContainer)); + for (const action of primaryActions) { + const buttonOptions = { title: true, /* assign titles to buttons in case they overflow */ }; + const dropdownActions = action instanceof ChoiceAction ? action.menu + : action instanceof ActionWithMenuAction ? action.actions : undefined; + const button = this.inputDisposables.add( + dropdownActions + ? buttonToolbar.addButtonWithDropdown({ + ...buttonOptions, + contextMenuProvider: this.contextMenuService, + actions: dropdownActions, + actionRunner + }) + : buttonToolbar.addButton(buttonOptions)); + button.label = action.label; + this.inputDisposables.add(button.onDidClick(e => { + EventHelper.stop(e, true); + actionRunner.run(action); })); this.inputDisposables.add(attachButtonStyler(button, this.themeService)); - }); - - this.inputDisposables.add(buttonGroup); + } } } diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 96d407d0..27fc613f 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -84,10 +84,6 @@ justify-content: center; } -.monaco-workbench .part.panel > .composite.title > .composite-bar-excess { - width: 100px; -} - .monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar { line-height: 27px; /* matches panel titles in settings */ height: 35px; diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 41674f53..74619ece 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -18,11 +18,13 @@ import { IActivity } from 'vs/workbench/common/activity'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ActivePanelContext, PanelPositionContext } from 'vs/workbench/common/panel'; import { ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp); -const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown); -const closeIcon = registerIcon('panel-close', Codicon.close); +const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, nls.localize('maximizeIcon', 'Icon to maximize a panel.')); +const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, nls.localize('restoreIcon', 'Icon to restore a panel.')); +const closeIcon = registerIcon('panel-close', Codicon.close, nls.localize('closeIcon', 'Icon to close a panel.')); export class ClosePanelAction extends Action { @@ -34,7 +36,7 @@ export class ClosePanelAction extends Action { name: string, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { - super(id, name, closeIcon.classNames); + super(id, name, ThemeIcon.asClassName(closeIcon)); } async run(): Promise { @@ -106,11 +108,11 @@ export class ToggleMaximizedPanelAction extends Action { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IEditorGroupsService editorGroupsService: IEditorGroupsService ) { - super(id, label, layoutService.isPanelMaximized() ? restoreIcon.classNames : maximizeIcon.classNames); + super(id, label, layoutService.isPanelMaximized() ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon)); this.toDispose.add(editorGroupsService.onDidLayout(() => { const maximized = this.layoutService.isPanelMaximized(); - this.class = maximized ? restoreIcon.classNames : maximizeIcon.classNames; + this.class = maximized ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon); this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL; })); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index e7480b64..332a9ab4 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -13,7 +13,7 @@ import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/ import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -37,7 +37,6 @@ import { ViewContainer, IViewDescriptorService, IViewContainerModel, ViewContain import { MenuId } from 'vs/platform/actions/common/actions'; import { ViewMenuActions, ViewContainerMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { Before2D, CompositeDragAndDropObserver, ICompositeDragAndDrop, toggleDropEffect } from 'vs/workbench/browser/dnd'; import { IActivity } from 'vs/workbench/common/activity'; @@ -118,7 +117,6 @@ export class PanelPart extends CompositePart implements IPanelService { @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IExtensionService private readonly extensionService: IExtensionService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super( notificationService, @@ -140,7 +138,6 @@ export class PanelPart extends CompositePart implements IPanelService { ); this.panelRegistry = Registry.as(PanelExtensions.Panels); - storageKeysSyncRegistryService.registerStorageKey({ key: PanelPart.PINNED_PANELS, version: 1 }); this.dndHandler = new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel, (id: string, focus?: boolean) => (this.openPanel(id, focus) as Promise).then(panel => panel || null), @@ -338,7 +335,7 @@ export class PanelPart extends CompositePart implements IPanelService { disposables.clear(); this.onDidRegisterExtensions(); this.compositeBar.onDidChange(() => this.saveCachedPanels(), this, disposables); - this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables); + this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables); })); } @@ -670,7 +667,7 @@ export class PanelPart extends CompositePart implements IPanelService { return this.toolBar.getItemsWidth(); } - private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { + private onDidStorageValueChange(e: IStorageValueChangeEvent): void { if (e.key === PanelPart.PINNED_PANELS && e.scope === StorageScope.GLOBAL && this.cachedPanelsValue !== this.getStoredCachedPanelsValue() /* This checks if current window changed the value or not */) { this._cachedPanelsValue = undefined; @@ -760,7 +757,7 @@ export class PanelPart extends CompositePart implements IPanelService { } private setStoredCachedViewletsValue(value: string): void { - this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL); + this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL, StorageTarget.USER); } private getPlaceholderViewContainers(): IPlaceholderViewContainer[] { @@ -792,7 +789,7 @@ export class PanelPart extends CompositePart implements IPanelService { } private setStoredPlaceholderViewContainersValue(value: string): void { - this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE); + this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE, StorageTarget.MACHINE); } private getViewContainer(panelId: string): ViewContainer | undefined { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index ced7b648..75903969 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -23,7 +23,7 @@ import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; import { EventHelper, createStyleSheet, addDisposableListener, EventType, hide, show, isAncestor, appendChildren } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { coalesce } from 'vs/base/common/arrays'; @@ -32,7 +32,6 @@ import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/la import { assertIsDefined } from 'vs/base/common/types'; import { Emitter } from 'vs/base/common/event'; import { Command } from 'vs/editor/common/modes'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -102,10 +101,10 @@ class StatusbarViewModel extends Disposable { } private registerListeners(): void { - this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e))); } - private onDidStorageChange(event: IWorkspaceStorageChangeEvent): void { + private onDidStorageValueChange(event: IStorageValueChangeEvent): void { if (event.key === StatusbarViewModel.HIDDEN_ENTRIES_KEY && event.scope === StorageScope.GLOBAL) { // Keep current hidden entries @@ -275,7 +274,7 @@ class StatusbarViewModel extends Disposable { private saveState(): void { if (this.hidden.size > 0) { - this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL); + this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL, StorageTarget.USER); } else { this.storageService.remove(StatusbarViewModel.HIDDEN_ENTRIES_KEY, StorageScope.GLOBAL); } @@ -405,12 +404,9 @@ export class StatusbarPart extends Part implements IStatusbarService { @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @IContextMenuService private contextMenuService: IContextMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); - storageKeysSyncRegistryService.registerStorageKey({ key: StatusbarViewModel.HIDDEN_ENTRIES_KEY, version: 1 }); - this.registerListeners(); } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 1f3b424a..99fcf2b6 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -11,7 +11,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { isMacintosh, isWeb, isIOS } from 'vs/base/common/platform'; +import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -21,7 +21,7 @@ import { MENUBAR_SELECTION_FOREGROUND, MENUBAR_SELECTION_BACKGROUND, MENUBAR_SEL import { URI } from 'vs/base/common/uri'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -122,7 +122,7 @@ export abstract class MenubarControl extends Disposable { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); // Listen to update service - this.updateService.onStateChange(() => this.updateMenubar()); + this.updateService.onStateChange(() => this.onUpdateStateChange()); // Listen for changes in recently opened menu this._register(this.workspacesService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); @@ -148,6 +148,14 @@ export abstract class MenubarControl extends Disposable { return label; } + protected onUpdateStateChange(): void { + this.updateMenubar(); + } + + protected onUpdateKeybindings(): void { + this.updateMenubar(); + } + protected getOpenRecentActions(): (Separator | IAction & { uri: URI })[] { if (!this.recentlyOpened) { return []; @@ -191,13 +199,27 @@ export abstract class MenubarControl extends Disposable { if (event.affectsConfiguration('editor.accessibilitySupport')) { this.notifyUserOfCustomMenubarAccessibility(); } + + // Since we try not update when hidden, we should + // try to update the recently opened list on visibility changes + if (event.affectsConfiguration('window.menuBarVisibility')) { + this.onRecentlyOpenedChange(); + } } - private onRecentlyOpenedChange(): void { - this.workspacesService.getRecentlyOpened().then(recentlyOpened => { - this.recentlyOpened = recentlyOpened; - this.updateMenubar(); - }); + private get menubarHidden(): boolean { + return isMacintosh && isNative ? false : getMenuBarVisibility(this.configurationService) === 'hidden'; + } + + protected onRecentlyOpenedChange(): void { + + // Do not update recently opened when the menubar is hidden #108712 + if (!this.menubarHidden) { + this.workspacesService.getRecentlyOpened().then(recentlyOpened => { + this.recentlyOpened = recentlyOpened; + this.updateMenubar(); + }); + } } private createOpenRecentMenuAction(recent: IRecent): IAction & { uri: URI } { @@ -241,7 +263,7 @@ export abstract class MenubarControl extends Disposable { } const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.GLOBAL, false); - const usingCustomMenubar = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom'; + const usingCustomMenubar = getTitleBarStyle(this.configurationService) === 'custom'; if (hasBeenNotified || usingCustomMenubar || !this.accessibilityService.isScreenReaderOptimized()) { return; @@ -257,7 +279,7 @@ export abstract class MenubarControl extends Disposable { } ]); - this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL); + this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL, StorageTarget.USER); } } @@ -266,6 +288,7 @@ export class CustomMenubarControl extends MenubarControl { private container: HTMLElement | undefined; private alwaysOnMnemonics: boolean = false; private focusInsideMenubar: boolean = false; + private visible: boolean = true; private readonly _onVisibilityChange: Emitter; private readonly _onFocusStateChange: Emitter; @@ -475,7 +498,7 @@ export class CustomMenubarControl extends MenubarControl { } private get currentMenubarVisibility(): MenuBarVisibility { - return getMenuBarVisibility(this.configurationService, this.environmentService); + return getMenuBarVisibility(this.configurationService); } private get currentDisableMenuBarAltFocus(): boolean { @@ -530,6 +553,12 @@ export class CustomMenubarControl extends MenubarControl { return currentSidebarLocation === 'right' ? Direction.Left : Direction.Right; } + private onDidVisibilityChange(visible: boolean): void { + this.visible = visible; + this.onRecentlyOpenedChange(); + this._onVisibilityChange.fire(visible); + } + private setupCustomMenubar(firstTime: boolean): void { // If there is no container, we cannot setup the menubar if (!this.container) { @@ -554,7 +583,7 @@ export class CustomMenubarControl extends MenubarControl { } })); - this._register(this.menubar.onVisibilityChange(e => this._onVisibilityChange.fire(e))); + this._register(this.menubar.onVisibilityChange(e => this.onDidVisibilityChange(e))); // Before we focus the menubar, stop updates to it so that focus-related context keys will work this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, () => { @@ -645,29 +674,15 @@ export class CustomMenubarControl extends MenubarControl { visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, - compactMode: this.currentCompactMenuMode, - getCompactMenuActions: () => { - if (!isWeb) { - return []; // only for web - } - - const webNavigationActions: IAction[] = []; - const webNavigationMenu = this.menuService.createMenu(MenuId.MenubarWebNavigationMenu, this.contextKeyService); - for (const groups of webNavigationMenu.getActions()) { - const [, actions] = groups; - for (const action of actions) { - action.label = mnemonicMenuLabel(this.calculateActionLabel(action)); - webNavigationActions.push(action); - } - } - webNavigationMenu.dispose(); - - return webNavigationActions; - } + compactMode: this.currentCompactMenuMode }; } protected onDidChangeWindowFocus(hasFocus: boolean): void { + if (!this.visible) { + return; + } + super.onDidChangeWindowFocus(hasFocus); if (this.container) { @@ -682,6 +697,30 @@ export class CustomMenubarControl extends MenubarControl { } } + protected onUpdateStateChange(): void { + if (!this.visible) { + return; + } + + super.onUpdateStateChange(); + } + + protected onRecentlyOpenedChange(): void { + if (!this.visible) { + return; + } + + super.onRecentlyOpenedChange(); + } + + protected onUpdateKeybindings(): void { + if (!this.visible) { + return; + } + + super.onUpdateKeybindings(); + } + protected registerListeners(): void { super.registerListeners(); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 1dc512aa..4783cb52 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -53,8 +53,8 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; - get minimumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); } - get maximumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); } + get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1); } + get maximumHeight(): number { return this.minimumHeight; } //#endregion @@ -100,7 +100,7 @@ export class TitlebarPart extends Part implements ITitleService { this.contextMenu = this._register(menuService.createMenu(MenuId.TitleBarContext, contextKeyService)); - this.titleBarStyle = getTitleBarStyle(this.configurationService, this.environmentService); + this.titleBarStyle = getTitleBarStyle(this.configurationService); this.registerListeners(); } @@ -461,13 +461,13 @@ export class TitlebarPart extends Part implements ITitleService { } protected get currentMenubarVisibility(): MenuBarVisibility { - return getMenuBarVisibility(this.configurationService, this.environmentService); + return getMenuBarVisibility(this.configurationService); } updateLayout(dimension: Dimension): void { this.lastLayoutDimensions = dimension; - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (getTitleBarStyle(this.configurationService) === 'custom') { // Only prevent zooming behavior on macOS or when the menubar is not visible if ((!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden') { this.title.style.zoom = `${1 / getZoomFactor()}`; diff --git a/src/vs/workbench/contrib/views/browser/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css similarity index 98% rename from src/vs/workbench/contrib/views/browser/media/views.css rename to src/vs/workbench/browser/parts/views/media/views.css index 8b30fccc..8f2df047 100644 --- a/src/vs/workbench/contrib/views/browser/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -64,11 +64,15 @@ } .monaco-workbench .pane > .pane-body > .welcome-view .monaco-button { - max-width: 260px; margin-left: auto; margin-right: auto; } +.monaco-workbench .pane > .pane-body.wide > .welcome-view .monaco-button { + margin-left: inherit; + max-width: 260px; +} + .monaco-workbench .pane > .pane-body .welcome-view-content { padding: 0 20px 0 20px; box-sizing: border-box; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 3669114b..dbd8ffce 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -3,20 +3,55 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toDisposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./media/views'; +import { toDisposable, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService } from 'vs/workbench/common/views'; +import { MenuId, IMenuService, MenuItemAction, registerAction2, Action2, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, ViewContainer, ViewContainerLocation, ResolvableTreeItem } from 'vs/workbench/common/views'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; +import { MenuEntryActionViewItem, createAndFillInContextMenuActions, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import * as DOM from 'vs/base/browser/dom'; +import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { URI } from 'vs/base/common/uri'; +import { dirname, basename } from 'vs/base/common/resources'; +import { FileKind } from 'vs/platform/files/common/files'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { localize } from 'vs/nls'; +import { timeout } from 'vs/base/common/async'; +import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; +import { isString } from 'vs/base/common/types'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IIconLabelMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; +import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; +import { Codicon } from 'vs/base/common/codicons'; export class TreeViewPane extends ViewPane { @@ -87,3 +122,1050 @@ export class TreeViewPane extends ViewPane { this.treeView.setVisibility(this.isBodyVisible()); } } + +class Root implements ITreeItem { + label = { label: 'root' }; + handle = '0'; + parentHandle: string | undefined = undefined; + collapsibleState = TreeItemCollapsibleState.Expanded; + children: ITreeItem[] | undefined = undefined; +} + +const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data."); + +class Tree extends WorkbenchAsyncDataTree { } + +export class TreeView extends Disposable implements ITreeView { + + private isVisible: boolean = false; + private _hasIconForParentNode = false; + private _hasIconForLeafNode = false; + + private readonly collapseAllContextKey: RawContextKey; + private readonly collapseAllContext: IContextKey; + private readonly collapseAllToggleContextKey: RawContextKey; + private readonly collapseAllToggleContext: IContextKey; + private readonly refreshContextKey: RawContextKey; + private readonly refreshContext: IContextKey; + + private focused: boolean = false; + private domNode!: HTMLElement; + private treeContainer!: HTMLElement; + private _messageValue: string | undefined; + private _canSelectMany: boolean = false; + private messageElement!: HTMLDivElement; + private tree: Tree | undefined; + private treeLabels: ResourceLabels | undefined; + + private root: ITreeItem; + private elementsToRefresh: ITreeItem[] = []; + + private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); + readonly onDidExpandItem: Event = this._onDidExpandItem.event; + + private readonly _onDidCollapseItem: Emitter = this._register(new Emitter()); + readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; + + private _onDidChangeSelection: Emitter = this._register(new Emitter()); + readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; + + private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); + readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + + private readonly _onDidChangeActions: Emitter = this._register(new Emitter()); + readonly onDidChangeActions: Event = this._onDidChangeActions.event; + + private readonly _onDidChangeWelcomeState: Emitter = this._register(new Emitter()); + readonly onDidChangeWelcomeState: Event = this._onDidChangeWelcomeState.event; + + private readonly _onDidChangeTitle: Emitter = this._register(new Emitter()); + readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; + + private readonly _onDidChangeDescription: Emitter = this._register(new Emitter()); + readonly onDidChangeDescription: Event = this._onDidChangeDescription.event; + + private readonly _onDidCompleteRefresh: Emitter = this._register(new Emitter()); + + constructor( + readonly id: string, + private _title: string, + @IThemeService private readonly themeService: IThemeService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ICommandService private readonly commandService: ICommandService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProgressService protected readonly progressService: IProgressService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @INotificationService private readonly notificationService: INotificationService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IHoverService private readonly hoverService: IHoverService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(); + this.root = new Root(); + this.collapseAllContextKey = new RawContextKey(`treeView.${this.id}.enableCollapseAll`, false); + this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService); + this.collapseAllToggleContextKey = new RawContextKey(`treeView.${this.id}.toggleCollapseAll`, false); + this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(contextKeyService); + this.refreshContextKey = new RawContextKey(`treeView.${this.id}.enableRefresh`, false); + this.refreshContext = this.refreshContextKey.bindTo(contextKeyService); + + this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); + this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('explorer.decorations')) { + this.doRefresh([this.root]); /** soft refresh **/ + } + })); + this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { + if (views.some(v => v.id === this.id)) { + this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } }); + } + })); + this.registerActions(); + + this.create(); + } + + get viewContainer(): ViewContainer { + return this.viewDescriptorService.getViewContainerByViewId(this.id)!; + } + + get viewLocation(): ViewContainerLocation { + return this.viewDescriptorService.getViewLocationById(this.id)!; + } + + private _dataProvider: ITreeViewDataProvider | undefined; + get dataProvider(): ITreeViewDataProvider | undefined { + return this._dataProvider; + } + + set dataProvider(dataProvider: ITreeViewDataProvider | undefined) { + if (this.tree === undefined) { + this.createTree(); + } + + if (dataProvider) { + const self = this; + this._dataProvider = new class implements ITreeViewDataProvider { + private _isEmpty: boolean = true; + private _onDidChangeEmpty: Emitter = new Emitter(); + public onDidChangeEmpty: Event = this._onDidChangeEmpty.event; + + get isTreeEmpty(): boolean { + return this._isEmpty; + } + + async getChildren(node?: ITreeItem): Promise { + let children: ITreeItem[]; + if (node && node.children) { + children = node.children; + } else { + node = node ?? self.root; + children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); + node.children = children; + } + if (node instanceof Root) { + const oldEmpty = this._isEmpty; + this._isEmpty = children.length === 0; + if (oldEmpty !== this._isEmpty) { + this._onDidChangeEmpty.fire(); + } + } + return children; + } + }; + if (this._dataProvider.onDidChangeEmpty) { + this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire())); + } + this.updateMessage(); + this.refresh(); + } else { + this._dataProvider = undefined; + this.updateMessage(); + } + + this._onDidChangeWelcomeState.fire(); + } + + private _message: string | undefined; + get message(): string | undefined { + return this._message; + } + + set message(message: string | undefined) { + this._message = message; + this.updateMessage(); + this._onDidChangeWelcomeState.fire(); + } + + get title(): string { + return this._title; + } + + set title(name: string) { + this._title = name; + this._onDidChangeTitle.fire(this._title); + } + + private _description: string | undefined; + get description(): string | undefined { + return this._description; + } + + set description(description: string | undefined) { + this._description = description; + this._onDidChangeDescription.fire(this._description); + } + + get canSelectMany(): boolean { + return this._canSelectMany; + } + + set canSelectMany(canSelectMany: boolean) { + this._canSelectMany = canSelectMany; + } + + get hasIconForParentNode(): boolean { + return this._hasIconForParentNode; + } + + get hasIconForLeafNode(): boolean { + return this._hasIconForLeafNode; + } + + get visible(): boolean { + return this.isVisible; + } + + get showCollapseAllAction(): boolean { + return !!this.collapseAllContext.get(); + } + + set showCollapseAllAction(showCollapseAllAction: boolean) { + this.collapseAllContext.set(showCollapseAllAction); + } + + get showRefreshAction(): boolean { + return !!this.refreshContext.get(); + } + + set showRefreshAction(showRefreshAction: boolean) { + this.refreshContext.set(showRefreshAction); + } + + private registerActions() { + const that = this; + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.treeView.${that.id}.refresh`, + title: localize('refresh', "Refresh"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.refreshContextKey), + group: 'navigation', + order: Number.MAX_SAFE_INTEGER - 1, + }, + icon: Codicon.refresh + }); + } + async run(): Promise { + return that.refresh(); + } + })); + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.actions.treeView.${that.id}.collapseAll`, + title: localize('collapseAll', "Collapse All"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.collapseAllContextKey), + group: 'navigation', + order: Number.MAX_SAFE_INTEGER, + }, + precondition: that.collapseAllToggleContextKey, + icon: Codicon.collapseAll + }); + } + async run(): Promise { + if (that.tree) { + return new CollapseAllAction(that.tree, true).run(); + } + } + })); + } + + setVisibility(isVisible: boolean): void { + isVisible = !!isVisible; + if (this.isVisible === isVisible) { + return; + } + + this.isVisible = isVisible; + + if (this.tree) { + if (this.isVisible) { + DOM.show(this.tree.getHTMLElement()); + } else { + DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it + } + + if (this.isVisible && this.elementsToRefresh.length) { + this.doRefresh(this.elementsToRefresh); + this.elementsToRefresh = []; + } + } + + this._onDidChangeVisibility.fire(this.isVisible); + } + + focus(reveal: boolean = true): void { + if (this.tree && this.root.children && this.root.children.length > 0) { + // Make sure the current selected element is revealed + const selectedElement = this.tree.getSelection()[0]; + if (selectedElement && reveal) { + this.tree.reveal(selectedElement, 0.5); + } + + // Pass Focus to Viewer + this.tree.domFocus(); + } else if (this.tree) { + this.tree.domFocus(); + } else { + this.domNode.focus(); + } + } + + show(container: HTMLElement): void { + DOM.append(container, this.domNode); + } + + private create() { + this.domNode = DOM.$('.tree-explorer-viewlet-tree-view'); + this.messageElement = DOM.append(this.domNode, DOM.$('.message')); + this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree')); + this.treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); + const focusTracker = this._register(DOM.trackFocus(this.domNode)); + this._register(focusTracker.onDidFocus(() => this.focused = true)); + this._register(focusTracker.onDidBlur(() => this.focused = false)); + } + + private createTree() { + const actionViewItemProvider = (action: IAction) => { + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } else if (action instanceof SubmenuItemAction) { + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); + } + + return undefined; + }; + const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); + this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); + const aligner = new Aligner(this.themeService); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); + const widgetAriaLabel = this._title; + + this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer], + dataSource, { + identityProvider: new TreeViewIdentityProvider(), + accessibilityProvider: { + getAriaLabel(element: ITreeItem): string { + if (element.accessibilityInformation) { + return element.accessibilityInformation.label; + } + + if (isString(element.tooltip)) { + return element.tooltip; + } else { + let buildAriaLabel: string = ''; + if (element.label) { + buildAriaLabel += element.label.label + ' '; + } + if (element.description) { + buildAriaLabel += element.description; + } + return buildAriaLabel; + } + }, + getRole(element: ITreeItem): string | undefined { + return element.accessibilityInformation?.role ?? 'treeitem'; + }, + getWidgetAriaLabel(): string { + return widgetAriaLabel; + } + }, + keyboardNavigationLabelProvider: { + getKeyboardNavigationLabel: (item: ITreeItem) => { + return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); + } + }, + expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command, + collapseByDefault: (e: ITreeItem): boolean => { + return e.collapsibleState !== TreeItemCollapsibleState.Expanded; + }, + multipleSelectionSupport: this.canSelectMany, + overrideStyles: { + listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND + } + }) as WorkbenchAsyncDataTree); + treeMenus.setContextKeyService(this.tree.contextKeyService); + aligner.tree = this.tree; + const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection()); + renderer.actionRunner = actionRunner; + + this.tree.contextKeyService.createKey(this.id, true); + this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); + this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); + this._register(this.tree.onDidChangeCollapseState(e => { + if (!e.node.element) { + return; + } + + const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element; + if (e.node.collapsed) { + this._onDidCollapseItem.fire(element); + } else { + this._onDidExpandItem.fire(element); + } + })); + this.tree.setInput(this.root).then(() => this.updateContentAreas()); + + this._register(this.tree.onDidOpen(e => { + if (!e.browserEvent) { + return; + } + const selection = this.tree!.getSelection(); + const command = selection.length === 1 ? selection[0].command : undefined; + if (command) { + let args = command.arguments || []; + if (command.id === API_OPEN_EDITOR_COMMAND_ID || command.id === API_OPEN_DIFF_EDITOR_COMMAND_ID) { + // Some commands owned by us should receive the + // `IOpenEvent` as context to open properly + args = [...args, e]; + } + + this.commandService.executeCommand(command.id, ...args); + } + })); + + } + + private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { + this.hoverService.hideHover(); + const node: ITreeItem | null = treeEvent.element; + if (node === null) { + return; + } + const event: UIEvent = treeEvent.browserEvent; + + event.preventDefault(); + event.stopPropagation(); + + this.tree!.setFocus([node]); + const actions = treeMenus.getResourceContextActions(node); + if (!actions.length) { + return; + } + this.contextMenuService.showContextMenu({ + getAnchor: () => treeEvent.anchor, + + getActions: () => actions, + + getActionViewItem: (action) => { + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return undefined; + }, + + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree!.domFocus(); + } + }, + + getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), + + actionRunner + }); + } + + protected updateMessage(): void { + if (this._message) { + this.showMessage(this._message); + } else if (!this.dataProvider) { + this.showMessage(noDataProviderMessage); + } else { + this.hideMessage(); + } + this.updateContentAreas(); + } + + private showMessage(message: string): void { + this.messageElement.classList.remove('hide'); + this.resetMessageElement(); + this._messageValue = message; + if (!isFalsyOrWhitespace(this._message)) { + this.messageElement.textContent = this._messageValue; + } + this.layout(this._height, this._width); + } + + private hideMessage(): void { + this.resetMessageElement(); + this.messageElement.classList.add('hide'); + this.layout(this._height, this._width); + } + + private resetMessageElement(): void { + DOM.clearNode(this.messageElement); + } + + private _height: number = 0; + private _width: number = 0; + layout(height: number, width: number) { + if (height && width) { + this._height = height; + this._width = width; + const treeHeight = height - DOM.getTotalHeight(this.messageElement); + this.treeContainer.style.height = treeHeight + 'px'; + if (this.tree) { + this.tree.layout(treeHeight, width); + } + } + } + + getOptimalWidth(): number { + if (this.tree) { + const parentNode = this.tree.getHTMLElement(); + const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); + return DOM.getLargestChildWidth(parentNode, childNodes); + } + return 0; + } + + async refresh(elements?: ITreeItem[]): Promise { + if (this.dataProvider && this.tree) { + if (this.refreshing) { + await Event.toPromise(this._onDidCompleteRefresh.event); + } + if (!elements) { + elements = [this.root]; + // remove all waiting elements to refresh if root is asked to refresh + this.elementsToRefresh = []; + } + for (const element of elements) { + element.children = undefined; // reset children + } + if (this.isVisible) { + return this.doRefresh(elements); + } else { + if (this.elementsToRefresh.length) { + const seen: Set = new Set(); + this.elementsToRefresh.forEach(element => seen.add(element.handle)); + for (const element of elements) { + if (!seen.has(element.handle)) { + this.elementsToRefresh.push(element); + } + } + } else { + this.elementsToRefresh.push(...elements); + } + } + } + return undefined; + } + + async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { + const tree = this.tree; + if (tree) { + itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; + await Promise.all(itemOrItems.map(element => { + return tree.expand(element, false); + })); + } + } + + setSelection(items: ITreeItem[]): void { + if (this.tree) { + this.tree.setSelection(items); + } + } + + setFocus(item: ITreeItem): void { + if (this.tree) { + this.focus(); + this.tree.setFocus([item]); + } + } + + async reveal(item: ITreeItem): Promise { + if (this.tree) { + return this.tree.reveal(item); + } + } + + private refreshing: boolean = false; + private async doRefresh(elements: ITreeItem[]): Promise { + const tree = this.tree; + if (tree && this.visible) { + this.refreshing = true; + await Promise.all(elements.map(element => tree.updateChildren(element, true, true))); + this.refreshing = false; + this._onDidCompleteRefresh.fire(); + this.updateContentAreas(); + if (this.focused) { + this.focus(false); + } + this.updateCollapseAllToggle(); + } + } + + private updateCollapseAllToggle() { + if (this.showCollapseAllAction) { + this.collapseAllToggleContext.set(!!this.root.children && (this.root.children.length > 0) && + this.root.children.some(value => value.collapsibleState !== TreeItemCollapsibleState.None)); + } + } + + private updateContentAreas(): void { + const isTreeEmpty = !this.root.children || this.root.children.length === 0; + // Hide tree container only when there is a message and tree is empty and not refreshing + if (this._messageValue && isTreeEmpty && !this.refreshing) { + this.treeContainer.classList.add('hide'); + this.domNode.setAttribute('tabindex', '0'); + } else { + this.treeContainer.classList.remove('hide'); + this.domNode.removeAttribute('tabindex'); + } + } +} + +class TreeViewIdentityProvider implements IIdentityProvider { + getId(element: ITreeItem): { toString(): string; } { + return element.handle; + } +} + +class TreeViewDelegate implements IListVirtualDelegate { + + getHeight(element: ITreeItem): number { + return TreeRenderer.ITEM_HEIGHT; + } + + getTemplateId(element: ITreeItem): string { + return TreeRenderer.TREE_TEMPLATE_ID; + } +} + +class TreeDataSource implements IAsyncDataSource { + + constructor( + private treeView: ITreeView, + private withProgress: (task: Promise) => Promise + ) { + } + + hasChildren(element: ITreeItem): boolean { + return !!this.treeView.dataProvider && (element.collapsibleState !== TreeItemCollapsibleState.None); + } + + async getChildren(element: ITreeItem): Promise { + if (this.treeView.dataProvider) { + return this.withProgress(this.treeView.dataProvider.getChildren(element)); + } + return []; + } +} + +// todo@jrieken,sandy make this proper and contributable from extensions +registerThemingParticipant((theme, collector) => { + + const matchBackgroundColor = theme.getColor(listFilterMatchHighlight); + if (matchBackgroundColor) { + collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); + } + const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder); + if (matchBorderColor) { + collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); + } + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message a { color: ${link}; }`); + } + const focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message a:focus { outline: 1px solid ${focusBorderColor}; outline-offset: -1px; }`); + } + const codeBackground = theme.getColor(textCodeBlockBackground); + if (codeBackground) { + collector.addRule(`.tree-explorer-viewlet-tree-view > .message code { background-color: ${codeBackground}; }`); + } +}); + +interface ITreeExplorerTemplateData { + elementDisposable: IDisposable; + container: HTMLElement; + resourceLabel: IResourceLabel; + icon: HTMLElement; + actionBar: ActionBar; +} + +class TreeRenderer extends Disposable implements ITreeRenderer { + static readonly ITEM_HEIGHT = 22; + static readonly TREE_TEMPLATE_ID = 'treeExplorer'; + + private _actionRunner: MultipleSelectionActionRunner | undefined; + private _hoverDelegate: IHoverDelegate; + + constructor( + private treeViewId: string, + private menus: TreeMenus, + private labels: ResourceLabels, + private actionViewItemProvider: IActionViewItemProvider, + private aligner: Aligner, + @IThemeService private readonly themeService: IThemeService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ILabelService private readonly labelService: ILabelService, + @IHoverService private readonly hoverService: IHoverService + ) { + super(); + this._hoverDelegate = { + showHover: (options: IHoverDelegateOptions): IDisposable | undefined => { + return this.hoverService.showHover(options); + } + }; + } + + get templateId(): string { + return TreeRenderer.TREE_TEMPLATE_ID; + } + + set actionRunner(actionRunner: MultipleSelectionActionRunner) { + this._actionRunner = actionRunner; + } + + renderTemplate(container: HTMLElement): ITreeExplorerTemplateData { + container.classList.add('custom-view-tree-node-item'); + + const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); + + const resourceLabel = this.labels.create(container, { supportHighlights: true, hoverDelegate: this._hoverDelegate }); + const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); + const actionBar = new ActionBar(actionsContainer, { + actionViewItemProvider: this.actionViewItemProvider + }); + + return { resourceLabel, icon, actionBar, container, elementDisposable: Disposable.None }; + } + + private getHover(label: string | undefined, resource: URI | null, node: ITreeItem): string | IIconLabelMarkdownString | undefined { + if (!(node instanceof ResolvableTreeItem) || !node.hasResolve) { + if (resource) { + return undefined; + } else if (!node.tooltip) { + return label; + } else if (!isString(node.tooltip)) { + return { markdown: node.tooltip, markdownNotSupportedFallback: resource ? undefined : renderMarkdownAsPlaintext(node.tooltip) }; // Passing undefined as the fallback for a resource falls back to the old native hover + } else { + return node.tooltip; + } + } + + return { + markdown: (): Promise => { + return new Promise(async (resolve) => { + await node.resolve(); + resolve(node.tooltip); + }); + }, + markdownNotSupportedFallback: resource ? undefined : '' // Passing undefined as the fallback for a resource falls back to the old native hover + }; + } + + renderElement(element: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { + templateData.elementDisposable.dispose(); + const node = element.element; + const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; + const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : (resource ? { label: basename(resource) } : undefined); + const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined; + const label = treeItemLabel ? treeItemLabel.label : undefined; + const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => { + if (start < 0) { + start = label.length + start; + } + if (end < 0) { + end = label.length + end; + } + if ((start >= label.length) || (end > label.length)) { + return ({ start: 0, end: 0 }); + } + if (start > end) { + const swap = start; + start = end; + end = swap; + } + return ({ start, end }); + }) : undefined; + const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; + const iconUrl = icon ? URI.revive(icon) : null; + const title = this.getHover(label, resource, node); + + // reset + templateData.actionBar.clear(); + templateData.icon.style.color = ''; + + if (resource || this.isFileKindThemeIcon(node.themeIcon)) { + const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); + const labelResource = resource ? resource : URI.parse('missing:_icon_resource'); + templateData.resourceLabel.setResource({ name: label, description, resource: labelResource }, { + fileKind: this.getFileKind(node), + title, + hideIcon: !!iconUrl, + fileDecorations, + extraClasses: ['custom-view-tree-node-item-resourceLabel'], + matches: matches ? matches : createMatches(element.filterData), + strikethrough: treeItemLabel?.strikethrough + }); + } else { + templateData.resourceLabel.setResource({ name: label, description }, { + title, + hideIcon: true, + extraClasses: ['custom-view-tree-node-item-resourceLabel'], + matches: matches ? matches : createMatches(element.filterData), + strikethrough: treeItemLabel?.strikethrough + }); + } + + if (iconUrl) { + templateData.icon.className = 'custom-view-tree-node-item-icon'; + templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl); + } else { + let iconClass: string | undefined; + if (node.themeIcon && !this.isFileKindThemeIcon(node.themeIcon)) { + iconClass = ThemeIcon.asClassName(node.themeIcon); + if (node.themeIcon.color) { + templateData.icon.style.color = this.themeService.getColorTheme().getColor(node.themeIcon.color.id)?.toString() ?? ''; + } + } + templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : ''; + templateData.icon.style.backgroundImage = ''; + } + + templateData.actionBar.context = { $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; + templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); + if (this._actionRunner) { + templateData.actionBar.actionRunner = this._actionRunner; + } + this.setAlignment(templateData.container, node); + const disposableStore = new DisposableStore(); + templateData.elementDisposable = disposableStore; + disposableStore.add(this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); + } + + private setAlignment(container: HTMLElement, treeItem: ITreeItem) { + container.parentElement!.classList.toggle('align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); + } + + private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean { + if (icon) { + return icon.id === FileThemeIcon.id || icon.id === FolderThemeIcon.id; + } else { + return false; + } + } + + private getFileKind(node: ITreeItem): FileKind { + if (node.themeIcon) { + switch (node.themeIcon.id) { + case FileThemeIcon.id: + return FileKind.FILE; + case FolderThemeIcon.id: + return FileKind.FOLDER; + } + } + return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE; + } + + disposeElement(resource: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { + templateData.elementDisposable.dispose(); + } + + disposeTemplate(templateData: ITreeExplorerTemplateData): void { + templateData.resourceLabel.dispose(); + templateData.actionBar.dispose(); + templateData.elementDisposable.dispose(); + } +} + +class Aligner extends Disposable { + private _tree: WorkbenchAsyncDataTree | undefined; + + constructor(private themeService: IThemeService) { + super(); + } + + set tree(tree: WorkbenchAsyncDataTree) { + this._tree = tree; + } + + public alignIconWithTwisty(treeItem: ITreeItem): boolean { + if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { + return false; + } + if (!this.hasIcon(treeItem)) { + return false; + } + + if (this._tree) { + const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); + if (this.hasIcon(parent)) { + return !!parent.children && parent.children.some(c => c.collapsibleState !== TreeItemCollapsibleState.None && !this.hasIcon(c)); + } + return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); + } else { + return false; + } + } + + private hasIcon(node: ITreeItem): boolean { + const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; + if (icon) { + return true; + } + if (node.resourceUri || node.themeIcon) { + const fileIconTheme = this.themeService.getFileIconTheme(); + const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None; + if (isFolder) { + return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons; + } + return fileIconTheme.hasFileIcons; + } + return false; + } +} + +class MultipleSelectionActionRunner extends ActionRunner { + + constructor(notificationService: INotificationService, private getSelectedResources: (() => ITreeItem[])) { + super(); + this._register(this.onDidRun(e => { + if (e.error) { + notificationService.error(localize('command-error', 'Error running command {1}: {0}. This is likely caused by the extension that contributes {1}.', e.error.message, e.action.id)); + } + })); + } + + runAction(action: IAction, context: TreeViewItemHandleArg): Promise { + const selection = this.getSelectedResources(); + let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; + let actionInSelected: boolean = false; + if (selection.length > 1) { + selectionHandleArgs = selection.map(selected => { + if (selected.handle === context.$treeItemHandle) { + actionInSelected = true; + } + return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }; + }); + } + + if (!actionInSelected) { + selectionHandleArgs = undefined; + } + + return action.run(...[context, selectionHandleArgs]); + } +} + +class TreeMenus extends Disposable implements IDisposable { + private contextKeyService: IContextKeyService | undefined; + + constructor( + private id: string, + @IMenuService private readonly menuService: IMenuService + ) { + super(); + } + + getResourceActions(element: ITreeItem): IAction[] { + return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).primary; + } + + getResourceContextActions(element: ITreeItem): IAction[] { + return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; + } + + public setContextKeyService(service: IContextKeyService) { + this.contextKeyService = service; + } + + private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { + if (!this.contextKeyService) { + return { primary: [], secondary: [] }; + } + const contextKeyService = this.contextKeyService.createScoped(); + contextKeyService.createKey('view', this.id); + contextKeyService.createKey(context.key, context.value); + + const menu = this.menuService.createMenu(menuId, contextKeyService); + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + menu.dispose(); + contextKeyService.dispose(); + + return result; + } +} + +export class CustomTreeView extends TreeView { + + private activated: boolean = false; + + constructor( + id: string, + title: string, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService, + @ICommandService commandService: ICommandService, + @IConfigurationService configurationService: IConfigurationService, + @IProgressService progressService: IProgressService, + @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, + @INotificationService notificationService: INotificationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IContextKeyService contextKeyService: IContextKeyService, + @IHoverService hoverService: IHoverService, + @IExtensionService private readonly extensionService: IExtensionService, + ) { + super(id, title, themeService, instantiationService, commandService, configurationService, progressService, contextMenuService, keybindingService, notificationService, viewDescriptorService, hoverService, contextKeyService); + } + + setVisibility(isVisible: boolean): void { + super.setVisibility(isVisible); + if (this.visible) { + this.activate(); + } + } + + private activate() { + if (!this.activated) { + this.progressService.withProgress({ location: this.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) + .then(() => timeout(2000)) + .then(() => { + this.updateMessage(); + }); + this.activated = true; + } + } +} + diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 4fa93931..410c6724 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -18,15 +18,15 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; +import { IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { PaneView, IPaneViewOptions, IPaneOptions, Pane, IPaneStyles } from 'vs/base/browser/ui/splitview/paneview'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel } from 'vs/workbench/common/views'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel, defaultViewIcon } from 'vs/workbench/common/views'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { assertIsDefined, isString } from 'vs/base/common/types'; +import { assertIsDefined } from 'vs/base/common/types'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -50,6 +50,8 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { Codicon } from 'vs/base/common/codicons'; export interface IPaneColors extends IColorMapping { dropBackground?: ColorIdentifier; @@ -70,6 +72,9 @@ type WelcomeActionClassification = { uri: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; }; +const viewPaneContainerExpandedIcon = registerIcon('view-pane-container-expanded', Codicon.chevronDown, nls.localize('viewPaneContainerExpandedIcon', 'Icon for an expanded view pane container.')); +const viewPaneContainerCollapsedIcon = registerIcon('view-pane-container-collapsed', Codicon.chevronRight, nls.localize('viewPaneContainerCollapsedIcon', 'Icon for a collapsed view pane container.')); + const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); interface IItem { @@ -263,7 +268,10 @@ export abstract class ViewPane extends Pane implements IView { if (changed) { this._onDidChangeBodyVisibility.fire(expanded); } - + if (this.twistiesContainer) { + this.twistiesContainer.classList.remove(...ThemeIcon.asClassNameArray(this.getTwistyIcon(!expanded))); + this.twistiesContainer.classList.add(...ThemeIcon.asClassNameArray(this.getTwistyIcon(expanded))); + } return changed; } @@ -288,7 +296,7 @@ export abstract class ViewPane extends Pane implements IView { protected renderHeader(container: HTMLElement): void { this.headerContainer = container; - this.renderTwisties(container); + this.twistiesContainer = append(container, $(ThemeIcon.asCSSSelector(this.getTwistyIcon(this.isExpanded())))); this.renderHeaderTitle(container, this.title); @@ -316,8 +324,8 @@ export abstract class ViewPane extends Pane implements IView { this.updateActionsVisibility(); } - protected renderTwisties(container: HTMLElement): void { - this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right')); + protected getTwistyIcon(expanded: boolean): ThemeIcon { + return expanded ? viewPaneContainerExpandedIcon : viewPaneContainerCollapsedIcon; } style(styles: IPaneStyles): void { @@ -338,8 +346,8 @@ export abstract class ViewPane extends Pane implements IView { } } - private getIcon(): string | URI { - return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || 'codicon-window'; + private getIcon(): ThemeIcon | URI { + return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || defaultViewIcon; } protected renderHeaderTitle(container: HTMLElement, title: string): void { @@ -357,9 +365,8 @@ export abstract class ViewPane extends Pane implements IView { -webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 16px; `); - } else if (isString(icon)) { - this.iconContainer.classList.add('codicon'); - cssClass = icon; + } else if (ThemeIcon.isThemeIcon(icon)) { + cssClass = ThemeIcon.asClassName(icon); } if (cssClass) { @@ -556,9 +563,7 @@ export abstract class ViewPane extends Pane implements IView { this.bodyContainer.classList.add('welcome'); this.viewWelcomeContainer.innerText = ''; - let buttonIndex = 0; - - for (const { content, preconditions } of contents) { + for (const { content, precondition } of contents) { const lines = content.split('\n'); for (let line of lines) { @@ -581,21 +586,15 @@ export abstract class ViewPane extends Pane implements IView { disposables.add(button); disposables.add(attachButtonStyler(button, this.themeService)); - if (preconditions) { - const precondition = preconditions[buttonIndex]; + if (precondition) { + const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition); + updateEnablement(); - if (precondition) { - const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition); - updateEnablement(); - - const keys = new Set(); - precondition.keys().forEach(key => keys.add(key)); - const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys)); - onDidChangeContext(updateEnablement, null, disposables); - } + const keys = new Set(); + precondition.keys().forEach(key => keys.add(key)); + const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys)); + onDidChangeContext(updateEnablement, null, disposables); } - - buttonIndex++; } else { const p = append(this.viewWelcomeContainer, $('p')); @@ -1319,7 +1318,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { saveState(): void { this.panes.forEach((view) => view.saveState()); - this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE); + this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE, StorageTarget.USER); } private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 80544aab..e7d260b0 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -59,6 +59,8 @@ import { localize } from 'vs/nls'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; class BrowserMain extends Disposable { @@ -198,9 +200,13 @@ class BrowserMain extends Disposable { serviceCollection.set(IFileService, fileService); await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); + // IURIIdentityService + const uriIdentityService = new UriIdentityService(fileService); + serviceCollection.set(IUriIdentityService, uriIdentityService); + // Long running services (workspace, config, storage) const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -330,8 +336,8 @@ class BrowserMain extends Disposable { } } - private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { - const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, logService); + private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, uriIdentityService, logService); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index c04818dc..fdc99470 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -108,6 +108,11 @@ import { isStandalone } from 'vs/base/browser/browser'; ], 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the sizing of editor groups when splitting them.") }, + 'workbench.editor.splitOnDragAndDrop': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('splitOnDragAndDrop', "Controls if editor groups can be split from drag and drop operations by dropping an editor or file on the edges of the editor area.") + }, 'workbench.editor.focusRecentEditorAfterClose': { 'type': 'boolean', 'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."), @@ -120,13 +125,13 @@ import { isStandalone } from 'vs/base/browser/browser'; }, 'workbench.editor.enablePreview': { 'type': 'boolean', - 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are explicitly set to be kept open (e.g. via double click or editing) and show up with an italic font style."), + 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing) and show up with an italic font style."), 'default': true }, 'workbench.editor.enablePreviewFromQuickOpen': { 'type': 'boolean', - 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether editors opened from Quick Open show as preview. Preview editors are reused until they are explicitly set to be kept open (e.g. via double click or editing)."), - 'default': true + 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether editors opened from Quick Open show as preview. Preview editors do not keep open and are reused until explicitly set to be kept open (e.g. via double click or editing)."), + 'default': false }, 'workbench.editor.closeOnFileDelete': { 'type': 'boolean', diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index dce86a91..2172c094 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -17,7 +17,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions'; import { Position, Parts, IWorkbenchLayoutService, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; -import { IStorageService, WillSaveStateReason, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, WillSaveStateReason, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -297,7 +297,7 @@ export class Workbench extends Layout { // local storage and not global storage because it would not make // much sense to synchronize to other machines. if (isNative) { - storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL); + storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL, StorageTarget.MACHINE); } else { window.localStorage.setItem('vscode.editorFontInfo', serializedFontInfoRaw); } @@ -415,8 +415,7 @@ export class Workbench extends Layout { mark('didStartWorkbench'); // Perf reporting (devtools) - performance.mark('workbench-end'); - performance.measure('perf: workbench create & restore', 'workbench-start', 'workbench-end'); + performance.measure('perf: workbench create & restore', 'didLoadWorkbenchMain', 'didStartWorkbench'); } } } diff --git a/src/vs/workbench/common/component.ts b/src/vs/workbench/common/component.ts index cbe3c7d6..f4454acc 100644 --- a/src/vs/workbench/common/component.ts +++ b/src/vs/workbench/common/component.ts @@ -5,7 +5,7 @@ import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; export class Component extends Themable { @@ -35,8 +35,8 @@ export class Component extends Themable { return this.id; } - protected getMemento(scope: StorageScope): MementoObject { - return this.memento.getMemento(scope); + protected getMemento(scope: StorageScope, target: StorageTarget): MementoObject { + return this.memento.getMemento(scope, target); } protected saveState(): void { diff --git a/src/vs/workbench/common/dialogs.ts b/src/vs/workbench/common/dialogs.ts new file mode 100644 index 00000000..5de8eac6 --- /dev/null +++ b/src/vs/workbench/common/dialogs.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IDialog, IDialogResult } from 'vs/platform/dialogs/common/dialogs'; + +export interface IDialogViewItem { + args: IDialog; + close(result?: IDialogResult): void; +} + +export interface IDialogHandle { + item: IDialogViewItem; + result: Promise; +} + +export interface IDialogsModel { + readonly onDidShowDialog: Event; + + readonly dialogs: IDialogViewItem[]; + + show(dialog: IDialog): IDialogHandle; +} + +export class DialogsModel extends Disposable implements IDialogsModel { + readonly dialogs: IDialogViewItem[] = []; + + private readonly _onDidShowDialog = this._register(new Emitter()); + readonly onDidShowDialog = this._onDidShowDialog.event; + + show(dialog: IDialog): IDialogHandle { + let resolver: (value?: IDialogResult) => void; + + const item: IDialogViewItem = { + args: dialog, + close: (result) => { this.dialogs.splice(0, 1); resolver(result); } + }; + + this.dialogs.push(item); + this._onDidShowDialog.fire(); + + return { + item, + result: new Promise(resolve => { resolver = resolve; }) + }; + } +} diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 30a37520..f104149d 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -14,13 +14,13 @@ import { IInstantiationService, IConstructorSignature0, ServicesAccessor, Brande import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITextModel } from 'vs/editor/common/model'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupsOrder, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ICompositeControl, IComposite } from 'vs/workbench/common/composite'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { IPathData } from 'vs/platform/windows/common/windows'; import { coalesce, firstOrDefault } from 'vs/base/common/arrays'; -import { IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; +import { ACTIVE_GROUP, IResourceEditorInputType, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; import { IExtUri } from 'vs/base/common/resources'; @@ -178,7 +178,7 @@ export interface IFileEditorInputFactory { /** * Creates new new editor input capable of showing files. */ - createFileEditorInput(resource: URI, preferredResource: URI | undefined, encoding: string | undefined, mode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; + createFileEditorInput(resource: URI, preferredResource: URI | undefined, preferredName: string | undefined, preferredDescription: string | undefined, preferredEncoding: string | undefined, preferredMode: string | undefined, instantiationService: IInstantiationService): IFileEditorInput; /** * Check if the provided object is a file editor input. @@ -704,6 +704,24 @@ export interface IFileEditorInput extends IEditorInput, IEncodingSupport, IModeS */ setPreferredResource(preferredResource: URI): void; + /** + * Sets the preferred name to use for this file input. + * + * Note: for certain file schemes the input may decide to ignore this + * name and use our standard naming. Specifically for schemes we own, + * we do not let others override the name. + */ + setPreferredName(name: string): void; + + /** + * Sets the preferred description to use for this file input. + * + * Note: for certain file schemes the input may decide to ignore this + * description and use our standard naming. Specifically for schemes we own, + * we do not let others override the description. + */ + setPreferredDescription(description: string): void; + /** * Sets the preferred encoding to use for this file input. */ @@ -734,7 +752,7 @@ export class SideBySideEditorInput extends EditorInput { constructor( protected readonly name: string | undefined, - private readonly description: string | undefined, + protected readonly description: string | undefined, private readonly _secondary: EditorInput, private readonly _primary: EditorInput ) { @@ -1266,6 +1284,7 @@ interface IEditorPartConfiguration { labelFormat?: 'default' | 'short' | 'medium' | 'long'; restoreViewState?: boolean; splitSizing?: 'split' | 'distribute'; + splitOnDragAndDrop?: boolean; limit?: { enabled?: boolean; value?: number; @@ -1421,13 +1440,15 @@ export const enum CloseDirection { export interface IEditorMemento { saveEditorState(group: IEditorGroup, resource: URI, state: T): void; - saveEditorState(group: IEditorGroup, editor: EditorInput, state: T): void; + saveEditorState(group: IEditorGroup, editor: IEditorInput, state: T): void; loadEditorState(group: IEditorGroup, resource: URI): T | undefined; - loadEditorState(group: IEditorGroup, editor: EditorInput): T | undefined; + loadEditorState(group: IEditorGroup, editor: IEditorInput): T | undefined; clearEditorState(resource: URI, group?: IEditorGroup): void; - clearEditorState(editor: EditorInput, group?: IEditorGroup): void; + clearEditorState(editor: IEditorInput, group?: IEditorGroup): void; + + clearEditorStateOnDispose(resource: URI, editor: IEditorInput): void; moveEditorState(source: URI, target: URI, comparer: IExtUri): void; } @@ -1569,3 +1590,42 @@ export function computeEditorAriaLabel(input: IEditorInput, index: number | unde return ariaLabel; } + + +//#region Editor Group Column + +/** + * A way to address editor groups through a column based system + * where `0` is the first column. Will fallback to `SIDE_GROUP` + * in case the column does not exist yet. + */ +export type EditorGroupColumn = number; + +export function viewColumnToEditorGroup(editorGroupService: IEditorGroupsService, viewColumn?: EditorGroupColumn): GroupIdentifier { + if (typeof viewColumn !== 'number' || viewColumn === ACTIVE_GROUP) { + return ACTIVE_GROUP; // prefer active group when position is undefined or passed in as such + } + + const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE); + + let candidateGroup = groups[viewColumn]; + if (candidateGroup) { + return candidateGroup.id; // found direct match + } + + let firstGroup = groups[0]; + if (groups.length === 1 && firstGroup.count === 0) { + return firstGroup.id; // first editor should always open in first group independent from position provided + } + + return SIDE_GROUP; // open to the side if group not found or we are instructed to +} + +export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService, editorGroup: IEditorGroup | GroupIdentifier): EditorGroupColumn { + let group = (typeof editorGroup === 'number') ? editorGroupService.getGroup(editorGroup) : editorGroup; + group = group ?? editorGroupService.activeGroup; + + return editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).indexOf(group); +} + +//#endregion diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 4274f2eb..38051db4 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -3,11 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; +import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; import { localize } from 'vs/nls'; +import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; +import { dirname } from 'vs/base/common/resources'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IFileService } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; /** * The base editor input for the diff editor. It is made up of two editor inputs, the original version @@ -21,10 +26,12 @@ export class DiffEditorInput extends SideBySideEditorInput { constructor( protected name: string | undefined, - description: string | undefined, + protected description: string | undefined, public readonly originalInput: EditorInput, public readonly modifiedInput: EditorInput, - private readonly forceOpenAsBinary?: boolean + private readonly forceOpenAsBinary: boolean | undefined, + @ILabelService private readonly labelService: ILabelService, + @IFileService private readonly fileService: IFileService ) { super(name, description, originalInput, modifiedInput); } @@ -35,12 +42,51 @@ export class DiffEditorInput extends SideBySideEditorInput { getName(): string { if (!this.name) { + + // Craft a name from original and modified input that includes the + // relative path in case both sides have different parents and we + // compare file resources. + const fileResources = this.asFileResources(); + if (fileResources && dirname(fileResources.original).path !== dirname(fileResources.modified).path) { + return `${this.labelService.getUriLabel(fileResources.original, { relative: true })} ↔ ${this.labelService.getUriLabel(fileResources.modified, { relative: true })}`; + } + return localize('sideBySideLabels', "{0} ↔ {1}", this.originalInput.getName(), this.modifiedInput.getName()); } return this.name; } + getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined { + if (typeof this.description !== 'string') { + + // Pass the description of the modified side in case both original + // and modified input have the same parent and we compare file resources. + const fileResources = this.asFileResources(); + if (fileResources && dirname(fileResources.original).path === dirname(fileResources.modified).path) { + return this.modifiedInput.getDescription(verbosity); + } + } + + return this.description; + } + + private asFileResources(): { original: URI, modified: URI } | undefined { + if ( + this.originalInput instanceof AbstractTextResourceEditorInput && + this.modifiedInput instanceof AbstractTextResourceEditorInput && + this.fileService.canHandleResource(this.originalInput.preferredResource) && + this.fileService.canHandleResource(this.modifiedInput.preferredResource) + ) { + return { + original: this.originalInput.preferredResource, + modified: this.modifiedInput.preferredResource + }; + } + + return undefined; + } + async resolve(): Promise { // Create Model - we never reuse our cached model if refresh is true because we cannot diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index ee4f35ba..a72d2652 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -55,6 +55,7 @@ export class ResourceEditorInput extends AbstractTextResourceEditorInput impleme setName(name: string): void { if (this.name !== name) { this.name = name; + this._onDidChangeLabel.fire(); } } diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts index e6d7c4d4..e55b5567 100644 --- a/src/vs/workbench/common/editor/textResourceEditorInput.ts +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -56,7 +56,7 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput implem private updateLabel(): void { // Clear any cached labels from before - this._basename = undefined; + this._name = undefined; this._shortDescription = undefined; this._mediumDescription = undefined; this._longDescription = undefined; @@ -76,17 +76,13 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput implem } } + private _name: string | undefined = undefined; getName(): string { - return this.basename; - } - - private _basename: string | undefined; - private get basename(): string { - if (!this._basename) { - this._basename = this.labelService.getUriBasenameLabel(this._preferredResource); + if (typeof this._name !== 'string') { + this._name = this.labelService.getUriBasenameLabel(this._preferredResource); } - return this._basename; + return this._name; } getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string | undefined { @@ -103,49 +99,55 @@ export abstract class AbstractTextResourceEditorInput extends EditorInput implem private _shortDescription: string | undefined = undefined; private get shortDescription(): string { - if (!this._shortDescription) { + if (typeof this._shortDescription !== 'string') { this._shortDescription = this.labelService.getUriBasenameLabel(dirname(this._preferredResource)); } + return this._shortDescription; } private _mediumDescription: string | undefined = undefined; private get mediumDescription(): string { - if (!this._mediumDescription) { + if (typeof this._mediumDescription !== 'string') { this._mediumDescription = this.labelService.getUriLabel(dirname(this._preferredResource), { relative: true }); } + return this._mediumDescription; } private _longDescription: string | undefined = undefined; private get longDescription(): string { - if (!this._longDescription) { + if (typeof this._longDescription !== 'string') { this._longDescription = this.labelService.getUriLabel(dirname(this._preferredResource)); } + return this._longDescription; } private _shortTitle: string | undefined = undefined; private get shortTitle(): string { - if (!this._shortTitle) { + if (typeof this._shortTitle !== 'string') { this._shortTitle = this.getName(); } + return this._shortTitle; } private _mediumTitle: string | undefined = undefined; private get mediumTitle(): string { - if (!this._mediumTitle) { + if (typeof this._mediumTitle !== 'string') { this._mediumTitle = this.labelService.getUriLabel(this._preferredResource, { relative: true }); } + return this._mediumTitle; } private _longTitle: string | undefined = undefined; private get longTitle(): string { - if (!this._longTitle) { + if (typeof this._longTitle !== 'string') { this._longTitle = this.labelService.getUriLabel(this._preferredResource); } + return this._longTitle; } diff --git a/src/vs/workbench/common/memento.ts b/src/vs/workbench/common/memento.ts index 309b0517..e9854ff8 100644 --- a/src/vs/workbench/common/memento.ts +++ b/src/vs/workbench/common/memento.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { isEmptyObject } from 'vs/base/common/types'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -22,13 +22,13 @@ export class Memento { this.id = Memento.COMMON_PREFIX + id; } - getMemento(scope: StorageScope): MementoObject { + getMemento(scope: StorageScope, target: StorageTarget): MementoObject { // Scope by Workspace if (scope === StorageScope.WORKSPACE) { let workspaceMemento = Memento.workspaceMementos.get(this.id); if (!workspaceMemento) { - workspaceMemento = new ScopedMemento(this.id, scope, this.storageService); + workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); Memento.workspaceMementos.set(this.id, workspaceMemento); } @@ -38,7 +38,7 @@ export class Memento { // Scope Global let globalMemento = Memento.globalMementos.get(this.id); if (!globalMemento) { - globalMemento = new ScopedMemento(this.id, scope, this.storageService); + globalMemento = new ScopedMemento(this.id, scope, target, this.storageService); Memento.globalMementos.set(this.id, globalMemento); } @@ -65,7 +65,7 @@ class ScopedMemento { private readonly mementoObj: MementoObject; - constructor(private id: string, private scope: StorageScope, private storageService: IStorageService) { + constructor(private id: string, private scope: StorageScope, private target: StorageTarget, private storageService: IStorageService) { this.mementoObj = this.load(); } @@ -92,7 +92,7 @@ class ScopedMemento { save(): void { if (!isEmptyObject(this.mementoObj)) { - this.storageService.store(this.id, JSON.stringify(this.mementoObj), this.scope); + this.storageService.store(this.id, JSON.stringify(this.mementoObj), this.scope, this.target); } else { this.storageService.remove(this.id, this.scope); } diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index b9fb58ac..e7df7148 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties } from 'vs/platform/notification/common/notification'; +import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties, IPromptChoiceWithMenu } from 'vs/platform/notification/common/notification'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -695,6 +695,7 @@ export class ChoiceAction extends Action { readonly onDidRun = this._onDidRun.event; private readonly _keepOpen: boolean; + private readonly _menu: ChoiceAction[] | undefined; constructor(id: string, choice: IPromptChoice) { super(id, choice.label, undefined, true, async () => { @@ -707,6 +708,11 @@ export class ChoiceAction extends Action { }); this._keepOpen = !!choice.keepOpen; + this._menu = !choice.isSecondary && (choice).menu ? (choice).menu.map((c, index) => new ChoiceAction(`${id}.${index}`, c)) : undefined; + } + + get menu(): ChoiceAction[] | undefined { + return this._menu; } get keepOpen(): boolean { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index f7451eda..53f29efb 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground, treeIndentGuidesStroke } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground, treeIndentGuidesStroke, errorForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; @@ -401,6 +401,18 @@ export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusB hc: Color.black.transparent(0.3), }, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ERROR_ITEM_BACKGROUND = registerColor('statusBarItem.errorBackground', { + dark: darken(errorForeground, .4), + light: darken(errorForeground, .4), + hc: null, +}, nls.localize('statusBarErrorItemBackground', "Status bar error items background color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); + +export const STATUS_BAR_ERROR_ITEM_FOREGROUND = registerColor('statusBarItem.errorForeground', { + dark: Color.white, + light: Color.white, + hc: Color.white, +}, nls.localize('statusBarErrorItemForeground', "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); + // < --- Activity Bar --- > export const ACTIVITY_BAR_BACKGROUND = registerColor('activityBar.background', { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 24f43b1d..0744cecf 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -25,8 +25,13 @@ import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { mixin } from 'vs/base/common/objects'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; +export const testViewIcon = registerIcon('test-view-icon', Codicon.beaker, localize('testViewIcon', 'View icon of the test view.')); + +export const defaultViewIcon = registerIcon('default-view-icon', Codicon.window, localize('defaultViewIcon', 'Default view icon.')); export namespace Extensions { export const ViewContainersRegistry = 'workbench.registry.view.containers'; @@ -48,7 +53,7 @@ export interface IViewContainerDescriptor { readonly storageId?: string; - readonly icon?: string | URI; + readonly icon?: ThemeIcon | URI; readonly alwaysUseContainerInfo?: boolean; @@ -216,7 +221,7 @@ export interface IViewDescriptor { readonly canMoveView?: boolean; - readonly containerIcon?: string | URI; + readonly containerIcon?: ThemeIcon | URI; readonly containerTitle?: string; @@ -252,7 +257,7 @@ export interface IAddedViewDescriptorState { export interface IViewContainerModel { readonly title: string; - readonly icon: string | URI | undefined; + readonly icon: ThemeIcon | URI | undefined; readonly onDidChangeContainerInfo: Event<{ title?: boolean, icon?: boolean }>; readonly allViewDescriptors: ReadonlyArray; @@ -290,11 +295,7 @@ export interface IViewContentDescriptor { readonly when?: ContextKeyExpression | 'default'; readonly group?: string; readonly order?: number; - - /** - * ordered preconditions for each button in the content - */ - readonly preconditions?: (ContextKeyExpression | undefined)[]; + readonly precondition?: ContextKeyExpression | undefined; } export interface IViewsRegistry { @@ -703,6 +704,27 @@ export class ResolvableTreeItem implements ITreeItem { get hasResolve(): boolean { return this._hasResolve; } + public resetResolve() { + this.resolved = false; + } + public asTreeItem(): ITreeItem { + return { + handle: this.handle, + parentHandle: this.parentHandle, + collapsibleState: this.collapsibleState, + label: this.label, + description: this.description, + icon: this.icon, + iconDark: this.iconDark, + themeIcon: this.themeIcon, + resourceUri: this.resourceUri, + tooltip: this.tooltip, + contextValue: this.contextValue, + command: this.command, + children: this.children, + accessibilityInformation: this.accessibilityInformation + }; + } } export interface ITreeViewDataProvider { diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts index 68c8751e..dfe8325a 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; +import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService'; import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; import { workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index f2e134c7..efb89faf 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -10,7 +10,7 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; +import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService'; import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -33,7 +33,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { HotExitConfiguration } from 'vs/platform/files/common/files'; import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileDialogService, ConfirmResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/electron-browser/workbenchTestServices'; @@ -48,6 +48,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; import { CancellationToken } from 'vs/base/common/cancellation'; import { timeout } from 'vs/base/common/async'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backuprestorer'); const backupHome = path.join(userdataDir, 'Backups'); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts index 96b5e0e7..714e4592 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { groupBy } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { compare } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEditMetadata } from 'vs/editor/common/modes'; import { IProgress } from 'vs/platform/progress/common/progress'; -import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; -import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; export class ResourceNotebookCellEdit extends ResourceEdit { @@ -29,10 +29,11 @@ export class ResourceNotebookCellEdit extends ResourceEdit { export class BulkCellEdits { constructor( - private _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoGroup: UndoRedoGroup, + undoRedoSource: UndoRedoSource | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, private readonly _edits: ResourceNotebookCellEdit[], - @INotebookService private readonly _notebookService: INotebookService, @INotebookEditorModelResolverService private readonly _notebookModelService: INotebookEditorModelResolverService, ) { } @@ -41,6 +42,9 @@ export class BulkCellEdits { const editsByNotebook = groupBy(this._edits, (a, b) => compare(a.resource.toString(), b.resource.toString())); for (let group of editsByNotebook) { + if (this._token.isCancellationRequested) { + break; + } const [first] = group; const ref = await this._notebookModelService.resolve(first.resource); @@ -52,7 +56,6 @@ export class BulkCellEdits { // apply edits const edits = group.map(entry => entry.cellEdit); - this._notebookService.transformEditsOutputs(ref.object.notebook, edits); ref.object.notebook.applyEdits(ref.object.notebook.versionId, edits, true, undefined, () => undefined, this._undoRedoGroup); ref.dispose(); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index defc9283..ffc54667 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -16,7 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { BulkTextEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkTextEdits'; import { BulkFileEdits } from 'vs/workbench/contrib/bulkEdit/browser/bulkFileEdits'; import { BulkCellEdits, ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; -import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; +import { LinkedList } from 'vs/base/common/linkedList'; +import { CancellationToken } from 'vs/base/common/cancellation'; class BulkEdit { @@ -24,7 +26,10 @@ class BulkEdit { private readonly _label: string | undefined, private readonly _editor: ICodeEditor | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, private readonly _edits: ResourceEdit[], + private readonly _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoSource: UndoRedoSource | undefined, @IInstantiationService private readonly _instaService: IInstantiationService, @ILogService private readonly _logService: ILogService, ) { @@ -58,20 +63,24 @@ class BulkEdit { } } - this._progress.report({ total: this._edits.length }); - const progress: IProgress = { report: _ => this._progress.report({ increment: 1 }) }; - - const undoRedoGroup = new UndoRedoGroup(); + // Show infinte progress when there is only 1 item since we do not know how long it takes + const increment = this._edits.length > 1 ? 0 : undefined; + this._progress.report({ increment, total: 100 }); + // Increment by percentage points since progress API expects that + const progress: IProgress = { report: _ => this._progress.report({ increment: 100 / this._edits.length }) }; let index = 0; for (let range of ranges) { + if (this._token.isCancellationRequested) { + break; + } const group = this._edits.slice(index, index + range); if (group[0] instanceof ResourceFileEdit) { - await this._performFileEdits(group, undoRedoGroup, progress); + await this._performFileEdits(group, this._undoRedoGroup, this._undoRedoSource, progress); } else if (group[0] instanceof ResourceTextEdit) { - await this._performTextEdits(group, undoRedoGroup, progress); + await this._performTextEdits(group, this._undoRedoGroup, this._undoRedoSource, progress); } else if (group[0] instanceof ResourceNotebookCellEdit) { - await this._performCellEdits(group, undoRedoGroup, progress); + await this._performCellEdits(group, this._undoRedoGroup, this._undoRedoSource, progress); } else { console.log('UNKNOWN EDIT'); } @@ -79,21 +88,21 @@ class BulkEdit { } } - private async _performFileEdits(edits: ResourceFileEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress) { + private async _performFileEdits(edits: ResourceFileEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress) { this._logService.debug('_performFileEdits', JSON.stringify(edits)); - const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), undoRedoGroup, progress, edits); + const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), undoRedoGroup, undoRedoSource, progress, this._token, edits); await model.apply(); } - private async _performTextEdits(edits: ResourceTextEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress): Promise { + private async _performTextEdits(edits: ResourceTextEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress): Promise { this._logService.debug('_performTextEdits', JSON.stringify(edits)); - const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, undoRedoGroup, progress, edits); + const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, undoRedoGroup, undoRedoSource, progress, this._token, edits); await model.apply(); } - private async _performCellEdits(edits: ResourceNotebookCellEdit[], undoRedoGroup: UndoRedoGroup, progress: IProgress): Promise { + private async _performCellEdits(edits: ResourceNotebookCellEdit[], undoRedoGroup: UndoRedoGroup, undoRedoSource: UndoRedoSource | undefined, progress: IProgress): Promise { this._logService.debug('_performCellEdits', JSON.stringify(edits)); - const model = this._instaService.createInstance(BulkCellEdits, undoRedoGroup, progress, edits); + const model = this._instaService.createInstance(BulkCellEdits, undoRedoGroup, undoRedoSource, progress, this._token, edits); await model.apply(); } } @@ -102,6 +111,7 @@ export class BulkEditService implements IBulkEditService { declare readonly _serviceBrand: undefined; + private readonly _activeUndoRedoGroups = new LinkedList(); private _previewHandler?: IBulkEditPreviewHandler; constructor( @@ -129,7 +139,7 @@ export class BulkEditService implements IBulkEditService { return { ariaSummary: localize('nothing', "Made no edits") }; } - if (this._previewHandler && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) { + if (this._previewHandler && !options?.suppressPreview && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) { edits = await this._previewHandler(edits, options); } @@ -147,11 +157,33 @@ export class BulkEditService implements IBulkEditService { codeEditor = undefined; } + // undo-redo-group: if a group id is passed then try to find it + // in the list of active edits. otherwise (or when not found) + // create a separate undo-redo-group + let undoRedoGroup: UndoRedoGroup | undefined; + let undoRedoGroupRemove = () => { }; + if (typeof options?.undoRedoGroupId === 'number') { + for (let candidate of this._activeUndoRedoGroups) { + if (candidate.id === options.undoRedoGroupId) { + undoRedoGroup = candidate; + break; + } + } + } + if (!undoRedoGroup) { + undoRedoGroup = new UndoRedoGroup(); + undoRedoGroupRemove = this._activeUndoRedoGroups.push(undoRedoGroup); + } + const bulkEdit = this._instaService.createInstance( BulkEdit, options?.quotableLabel || options?.label, - codeEditor, options?.progress ?? Progress.None, - edits + codeEditor, + options?.progress ?? Progress.None, + options?.token ?? CancellationToken.None, + edits, + undoRedoGroup, + options?.undoRedoSource ); try { @@ -162,6 +194,8 @@ export class BulkEditService implements IBulkEditService { // console.log(err); this._logService.error(err); throw err; + } finally { + undoRedoGroupRemove(); } } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index b5843048..f4ac30ca 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -5,21 +5,27 @@ import { WorkspaceFileEditOptions } from 'vs/editor/common/modes'; -import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { IFileService, FileSystemProviderCapabilities, IFileContent } from 'vs/platform/files/common/files'; import { IProgress } from 'vs/platform/progress/common/progress'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; -import { IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoService, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoService, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { VSBuffer } from 'vs/base/common/buffer'; import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; import * as resources from 'vs/base/common/resources'; +import { CancellationToken } from 'vs/base/common/cancellation'; + +interface IFileOperationUndoRedoInfo { + undoRedoGroupId?: number; + isUndoing?: boolean; +} interface IFileOperation { uris: URI[]; - perform(): Promise; + perform(token: CancellationToken): Promise; } class Noop implements IFileOperation { @@ -36,6 +42,7 @@ class RenameOperation implements IFileOperation { readonly newUri: URI, readonly oldUri: URI, readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @IFileService private readonly _fileService: IFileService, ) { } @@ -44,14 +51,14 @@ class RenameOperation implements IFileOperation { return [this.newUri, this.oldUri]; } - async perform(): Promise { + async perform(token: CancellationToken): Promise { // rename if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { return new Noop(); // not overwriting, but ignoring, and the target file exists } - await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite }); - return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService); + await this._workingCopyFileService.move([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token); + return new RenameOperation(this.oldUri, this.newUri, this.options, { isUndoing: true }, this._workingCopyFileService, this._fileService); } toString(): string { @@ -64,11 +71,44 @@ class RenameOperation implements IFileOperation { } } +class CopyOperation implements IFileOperation { + + constructor( + readonly newUri: URI, + readonly oldUri: URI, + readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, + @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, + @IFileService private readonly _fileService: IFileService, + @IInstantiationService private readonly _instaService: IInstantiationService + ) { } + + get uris() { + return [this.newUri, this.oldUri]; + } + + async perform(token: CancellationToken): Promise { + // copy + if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { + return new Noop(); // not overwriting, but ignoring, and the target file exists + } + + const stat = await this._workingCopyFileService.copy([{ source: this.oldUri, target: this.newUri }], { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token); + const folder = this.options.folder || (stat.length === 1 && stat[0].isDirectory); + return this._instaService.createInstance(DeleteOperation, this.newUri, { recursive: true, folder, ...this.options }, { isUndoing: true }, false); + } + + toString(): string { + return `(copy ${this.oldUri} to ${this.newUri})`; + } +} + class CreateOperation implements IFileOperation { constructor( readonly newUri: URI, readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, readonly contents: VSBuffer | undefined, @IFileService private readonly _fileService: IFileService, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @@ -79,17 +119,22 @@ class CreateOperation implements IFileOperation { return [this.newUri]; } - async perform(): Promise { + async perform(token: CancellationToken): Promise { // create file if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) { return new Noop(); // not overwriting, but ignoring, and the target file exists } - await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite }); - return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, true); + if (this.options.folder) { + await this._workingCopyFileService.createFolder(this.newUri, { ...this.undoRedoInfo }, token); + } else { + await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite, ...this.undoRedoInfo }, token); + } + return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, { isUndoing: true }, !this.options.folder && !this.contents); } toString(): string { - return `(create ${resources.basename(this.newUri)} with ${this.contents?.byteLength || 0} bytes)`; + return this.options.folder ? `create ${resources.basename(this.newUri)} folder` + : `(create ${resources.basename(this.newUri)} with ${this.contents?.byteLength || 0} bytes)`; } } @@ -98,6 +143,7 @@ class DeleteOperation implements IFileOperation { constructor( readonly oldUri: URI, readonly options: WorkspaceFileEditOptions, + readonly undoRedoInfo: IFileOperationUndoRedoInfo, private readonly _undoesCreateOperation: boolean, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @IFileService private readonly _fileService: IFileService, @@ -110,7 +156,7 @@ class DeleteOperation implements IFileOperation { return [this.oldUri]; } - async perform(): Promise { + async perform(token: CancellationToken): Promise { // delete file if (!await this._fileService.exists(this.oldUri)) { if (!this.options.ignoreIfNotExists) { @@ -119,18 +165,22 @@ class DeleteOperation implements IFileOperation { return new Noop(); } - let contents: VSBuffer | undefined; - if (!this._undoesCreateOperation) { + let fileContent: IFileContent | undefined; + if (!this._undoesCreateOperation && !this.options.folder) { try { - contents = (await this._fileService.readFile(this.oldUri)).value; + fileContent = await this._fileService.readFile(this.oldUri); } catch (err) { this._logService.critical(err); } } - const useTrash = this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue('files.enableTrash'); - await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive }); - return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, contents); + const useTrash = !this.options.skipTrashBin && this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue('files.enableTrash'); + await this._workingCopyFileService.delete([this.oldUri], { useTrash, recursive: this.options.recursive, ...this.undoRedoInfo }, token); + + if (typeof this.options.maxSize === 'number' && fileContent && (fileContent?.size > this.options.maxSize)) { + return new Noop(); + } + return this._instaService.createInstance(CreateOperation, this.oldUri, this.options, { isUndoing: true }, fileContent?.value); } toString(): string { @@ -162,7 +212,7 @@ class FileUndoRedoElement implements IWorkspaceUndoRedoElement { private async _reverse() { for (let i = 0; i < this.operations.length; i++) { const op = this.operations[i]; - const undo = await op.perform(); + const undo = await op.perform(CancellationToken.None); this.operations[i] = undo; } } @@ -177,7 +227,9 @@ export class BulkFileEdits { constructor( private readonly _label: string, private readonly _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoSource: UndoRedoSource | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, private readonly _edits: ResourceFileEdit[], @IInstantiationService private readonly _instaService: IInstantiationService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, @@ -185,27 +237,35 @@ export class BulkFileEdits { async apply(): Promise { const undoOperations: IFileOperation[] = []; + const undoRedoInfo = { undoRedoGroupId: this._undoRedoGroup.id }; for (const edit of this._edits) { - this._progress.report(undefined); + + if (this._token.isCancellationRequested) { + break; + } const options = edit.options || {}; let op: IFileOperation | undefined; - if (edit.newResource && edit.oldResource) { + if (edit.newResource && edit.oldResource && !options.copy) { // rename - op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options); + op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options, undoRedoInfo); + } else if (edit.newResource && edit.oldResource && options.copy) { + op = this._instaService.createInstance(CopyOperation, edit.newResource, edit.oldResource, options, undoRedoInfo); } else if (!edit.newResource && edit.oldResource) { // delete file - op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options, false); + op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options, undoRedoInfo, false); } else if (edit.newResource && !edit.oldResource) { // create file - op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undefined); + op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undoRedoInfo, undefined); } if (op) { - const undoOp = await op.perform(); + const undoOp = await op.perform(this._token); undoOperations.push(undoOp); } + + this._progress.report(undefined); } - this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations), this._undoRedoGroup); + this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations), this._undoRedoGroup, this._undoRedoSource); } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index 533654d9..852da604 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -14,11 +14,12 @@ import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'v import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IProgress } from 'vs/platform/progress/common/progress'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { IUndoRedoService, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; import { SingleModelEditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; import { ResourceMap } from 'vs/base/common/map'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { CancellationToken } from 'vs/base/common/cancellation'; type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; @@ -39,6 +40,18 @@ class ModelEditTask implements IDisposable { this._modelReference.dispose(); } + isNoOp() { + if (this._edits.length > 0) { + // contains textual edits + return false; + } + if (this._newEol !== undefined && this._newEol !== this.model.getEndOfLineSequence()) { + // contains an eol change that is a real change + return false; + } + return true; + } + addEdit(resourceEdit: ResourceTextEdit): void { this._expectedModelVersionId = resourceEdit.versionId; const { textEdit } = resourceEdit; @@ -122,7 +135,9 @@ export class BulkTextEdits { private readonly _label: string, private readonly _editor: ICodeEditor | undefined, private readonly _undoRedoGroup: UndoRedoGroup, + private readonly _undoRedoSource: UndoRedoSource | undefined, private readonly _progress: IProgress, + private readonly _token: CancellationToken, edits: ResourceTextEdit[], @IEditorWorkerService private readonly _editorWorker: IEditorWorkerService, @IModelService private readonly _modelService: IModelService, @@ -210,6 +225,9 @@ export class BulkTextEdits { this._validateBeforePrepare(); const tasks = await this._createEditsTasks(); + if (this._token.isCancellationRequested) { + return; + } try { const validation = this._validateTasks(tasks); @@ -219,10 +237,12 @@ export class BulkTextEdits { if (tasks.length === 1) { // This edit touches a single model => keep things simple const task = tasks[0]; - const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState()); - this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup); - task.apply(); - singleModelEditStackElement.close(); + if (!task.isNoOp()) { + const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState()); + this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup, this._undoRedoSource); + task.apply(); + singleModelEditStackElement.close(); + } this._progress.report(undefined); } else { // prepare multi model undo element @@ -230,7 +250,7 @@ export class BulkTextEdits { this._label, tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState())) ); - this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup); + this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup, this._undoRedoSource); for (const task of tasks) { task.apply(); this._progress.report(undefined); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts index 139ee6dd..7a757cf0 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts @@ -28,6 +28,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; async function getBulkEditPane(viewsService: IViewsService): Promise { const view = await viewsService.openView(BulkEditPane.ID, true); @@ -169,7 +170,7 @@ registerAction2(class ApplyAction extends Action2 { id: 'refactorPreview.apply', title: { value: localize('apply', "Apply Refactoring"), original: 'Apply Refactoring' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/check' }, + icon: Codicon.check, precondition: ContextKeyExpr.and(BulkEditPreviewContribution.ctxEnabled, BulkEditPane.ctxHasCheckedChanges), menu: [{ id: MenuId.BulkEditTitle, @@ -203,7 +204,7 @@ registerAction2(class DiscardAction extends Action2 { id: 'refactorPreview.discard', title: { value: localize('Discard', "Discard Refactoring"), original: 'Discard Refactoring' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/clear-all' }, + icon: Codicon.clearAll, precondition: BulkEditPreviewContribution.ctxEnabled, menu: [{ id: MenuId.BulkEditTitle, @@ -264,7 +265,7 @@ registerAction2(class GroupByFile extends Action2 { id: 'refactorPreview.groupByFile', title: { value: localize('groupByFile', "Group Changes By File"), original: 'Group Changes By File' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/ungroup-by-ref-type' }, + icon: Codicon.ungroupByRefType, precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile.negate(), BulkEditPreviewContribution.ctxEnabled), menu: [{ id: MenuId.BulkEditTitle, @@ -291,7 +292,7 @@ registerAction2(class GroupByType extends Action2 { id: 'refactorPreview.groupByType', title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/group-by-ref-type' }, + icon: Codicon.groupByRefType, precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPane.ctxGroupByFile, BulkEditPreviewContribution.ctxEnabled), menu: [{ id: MenuId.BulkEditTitle, @@ -318,7 +319,7 @@ registerAction2(class ToggleGrouping extends Action2 { id: 'refactorPreview.toggleGrouping', title: { value: localize('groupByType', "Group Changes By Type"), original: 'Group Changes By Type' }, category: { value: localize('cat', "Refactor Preview"), original: 'Refactor Preview' }, - icon: { id: 'codicon/list-tree' }, + icon: Codicon.listTree, toggled: BulkEditPane.ctxGroupByFile.negate(), precondition: ContextKeyExpr.and(BulkEditPane.ctxHasCategories, BulkEditPreviewContribution.ctxEnabled), menu: [{ @@ -341,6 +342,8 @@ Registry.as(WorkbenchExtensions.Workbench).regi BulkEditPreviewContribution, LifecyclePhase.Ready ); +const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codicon.lightbulb, localize('refactorPreviewViewIcon', 'View icon of the refactor preview view.')); + const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: BulkEditPane.ID, name: localize('panel', "Refactor Preview"), @@ -349,7 +352,7 @@ const container = Registry.as(ViewContainerExtensions.V ViewPaneContainer, [BulkEditPane.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }] ), - icon: Codicon.lightbulb.classNames, + icon: refactorPreviewViewIcon, storageId: BulkEditPane.ID }, ViewContainerLocation.Panel); @@ -358,5 +361,5 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews name: localize('panel', "Refactor Preview"), when: BulkEditPreviewContribution.ctxEnabled, ctorDescriptor: new SyncDescriptor(BulkEditPane), - containerIcon: Codicon.lightbulb.classNames, + containerIcon: refactorPreviewViewIcon, }], container); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index f36f5f0e..ab0172ee 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -12,7 +12,7 @@ import { registerThemingParticipant, IColorTheme, ICssStyleCollector, IThemeServ import { diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { BulkEditPreviewProvider, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -26,7 +26,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; -import { basename } from 'vs/base/common/resources'; +import { basename, dirname } from 'vs/base/common/resources'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -34,7 +34,7 @@ import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import type { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -128,7 +128,7 @@ export class BulkEditPane extends ViewPane { this._tree = >this._instaService.createInstance( WorkbenchAsyncDataTree, this.id, treeContainer, new BulkEditDelegate(), - [new TextEditElementRenderer(), this._instaService.createInstance(FileElementRenderer, resourceLabels), new CategoryElementRenderer()], + [this._instaService.createInstance(TextEditElementRenderer), this._instaService.createInstance(FileElementRenderer, resourceLabels), this._instaService.createInstance(CategoryElementRenderer)], this._treeDataSource, { accessibilityProvider: this._instaService.createInstance(BulkEditAccessibilityProvider), @@ -293,12 +293,12 @@ export class BulkEditPane extends ViewPane { this._setTreeInput(input); // (3) remember preference - this._storageService.store(BulkEditPane._memGroupByFile, this._treeDataSource.groupByFile, StorageScope.GLOBAL); + this._storageService.store(BulkEditPane._memGroupByFile, this._treeDataSource.groupByFile, StorageScope.GLOBAL, StorageTarget.USER); this._ctxGroupByFile.set(this._treeDataSource.groupByFile); } } - private async _openElementAsEditor(e: IOpenEvent): Promise { + private async _openElementAsEditor(e: IOpenEvent): Promise { type Mutable = { -readonly [P in keyof T]: T[P] }; @@ -356,8 +356,9 @@ export class BulkEditPane extends ViewPane { leftResource, rightResource: previewUri, label, + description: this._labelService.getUriLabel(dirname(leftResource), { relative: true }), options - }); + }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts index 4aecbf74..a1b366c4 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts @@ -22,6 +22,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { localize } from 'vs/nls'; import { extUri } from 'vs/base/common/resources'; import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { Codicon } from 'vs/base/common/codicons'; export class CheckedStates { @@ -116,7 +117,7 @@ export class BulkCategory { private static readonly _defaultMetadata = Object.freeze({ label: localize('default', "Other"), - icon: { id: 'codicon/symbol-file' }, + icon: Codicon.symbolFile, needsConfirmation: false }); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts index 4db2acf2..e9dddb2a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts @@ -21,7 +21,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import type { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { basename } from 'vs/base/common/resources'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { compare } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -386,6 +386,8 @@ export class CategoryElementRenderer implements ITreeRenderer, _index: number, template: TextEditElementTemplate): void { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index 32c4c2f8..85c2711c 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -18,12 +18,13 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { PeekContext } from 'vs/editor/contrib/peekView/peekView'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false); const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false); @@ -128,7 +129,7 @@ class CallHierarchyController implements IEditorContribution { this._widget.showLoading(); this._sessionDisposables.add(this._widget.onDidClose(() => { this.endCallHierarchy(); - this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL); + this._storageService.store(CallHierarchyController._StorageDirection, this._widget!.direction, StorageScope.GLOBAL, StorageTarget.USER); })); this._sessionDisposables.add({ dispose() { cts.dispose(true); } }); this._sessionDisposables.add(this._widget); @@ -207,7 +208,7 @@ registerAction2(class extends EditorAction2 { super({ id: 'editor.showIncomingCalls', title: { value: localize('title.incoming', "Show Incoming Calls"), original: 'Show Incoming Calls' }, - icon: registerIcon('callhierarchy-incoming', Codicon.callIncoming), + icon: registerIcon('callhierarchy-incoming', Codicon.callIncoming, localize('showIncomingCallsIcons', 'Icon for incoming calls in the call hierarchy view.')), precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsFrom)), keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -232,7 +233,7 @@ registerAction2(class extends EditorAction2 { super({ id: 'editor.showOutgoingCalls', title: { value: localize('title.outgoing', "Show Outgoing Calls"), original: 'Show Outgoing Calls' }, - icon: registerIcon('callhierarchy-outgoing', Codicon.callOutgoing), + icon: registerIcon('callhierarchy-outgoing', Codicon.callOutgoing, localize('showOutgoingCallsIcon', 'Icon for outgoing calls in the call hierarchy view.')), precondition: ContextKeyExpr.and(_ctxCallHierarchyVisible, _ctxCallHierarchyDirection.isEqualTo(CallHierarchyDirection.CallsTo)), keybinding: { weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index b6866aca..6fcd9f8a 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -28,7 +28,7 @@ import { registerThemingParticipant, themeColorFromId, IThemeService, IColorThem import { IPosition } from 'vs/editor/common/core/position'; import { IAction } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Color } from 'vs/base/common/color'; import { TreeMouseEventTarget, ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { URI } from 'vs/base/common/uri'; @@ -45,7 +45,7 @@ const enum State { class LayoutInfo { static store(info: LayoutInfo, storageService: IStorageService): void { - storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL); + storageService.store('callHierarchyPeekLayout', JSON.stringify(info), StorageScope.GLOBAL, StorageTarget.MACHINE); } static retrieve(storageService: IStorageService): LayoutInfo { @@ -277,7 +277,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { this.dispose(); this._editorService.openEditor({ resource: e.element.item.uri, - options: { selection: e.element.item.selectionRange } + options: { selection: e.element.item.selectionRange, pinned: true } }); } })); @@ -289,7 +289,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { this.dispose(); this._editorService.openEditor({ resource: element.item.uri, - options: { selection: element.item.selectionRange } + options: { selection: element.item.selectionRange, pinned: true } }); } })); diff --git a/src/vs/workbench/contrib/cli/node/cli.contribution.ts b/src/vs/workbench/contrib/cli/node/cli.contribution.ts index 2000693f..30972a41 100644 --- a/src/vs/workbench/contrib/cli/node/cli.contribution.ts +++ b/src/vs/workbench/contrib/cli/node/cli.contribution.ts @@ -8,12 +8,8 @@ import * as path from 'vs/base/common/path'; import * as cp from 'child_process'; import * as pfs from 'vs/base/node/pfs'; import * as extpath from 'vs/base/node/extpath'; -import * as platform from 'vs/base/common/platform'; import { promisify } from 'util'; -import { Action } from 'vs/base/common/actions'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import product from 'vs/platform/product/common/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -21,6 +17,9 @@ import Severity from 'vs/base/common/severity'; import { ILogService } from 'vs/platform/log/common/log'; import { FileAccess } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; function ignore(code: string, value: T): (err: any) => Promise { return err => err.code === code ? Promise.resolve(value) : Promise.reject(err); @@ -39,45 +38,65 @@ function isAvailable(): Promise { return Promise.resolve(pfs.exists(getSource())); } -class InstallAction extends Action { +const category = nls.localize('shellCommand', "Shell Command"); - static readonly ID = 'workbench.action.installCommandLine'; - static readonly LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName); +class InstallAction extends Action2 { - constructor( - id: string, - label: string, - @INotificationService private readonly notificationService: INotificationService, - @IDialogService private readonly dialogService: IDialogService, - @ILogService private readonly logService: ILogService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.installCommandLine', + title: { + value: nls.localize('install', "Install '{0}' command in PATH", product.applicationName), + original: `Shell Command: Install \'${product.applicationName}\' command in PATH` + }, + category, + f1: true, + precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', '')) + }); } - private get target(): string { - return `/usr/local/bin/${this.productService.applicationName}`; - } + run(accessor: ServicesAccessor): Promise { + const productService = accessor.get(IProductService); + const notificationService = accessor.get(INotificationService); + const logService = accessor.get(ILogService); + const dialogService = accessor.get(IDialogService); + const target = `/usr/local/bin/${productService.applicationName}`; - run(): Promise { return isAvailable().then(isAvailable => { if (!isAvailable) { const message = nls.localize('not available', "This command is not available"); - this.notificationService.info(message); + notificationService.info(message); return undefined; } - return this.isInstalled() + return this.isInstalled(target) .then(isInstalled => { if (!isAvailable || isInstalled) { return Promise.resolve(null); } else { - return pfs.unlink(this.target) + return pfs.unlink(target) .then(undefined, ignore('ENOENT', null)) - .then(() => pfs.symlink(getSource(), this.target)) + .then(() => pfs.symlink(getSource(), target)) .then(undefined, err => { if (err.code === 'EACCES' || err.code === 'ENOENT') { - return this.createBinFolderAndSymlinkAsAdmin(); + return new Promise((resolve, reject) => { + const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; + + dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => { + switch (result.choice) { + case 0 /* OK */: + const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + target + '\'\\" with administrator privileges"'; + + promisify(cp.exec)(command, {}) + .then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'.")))) + .then(() => resolve(), reject); + break; + case 1 /* Cancel */: + reject(new Error(nls.localize('aborted', "Aborted"))); + break; + } + }); + }); } return Promise.reject(err); @@ -85,113 +104,84 @@ class InstallAction extends Action { } }) .then(() => { - this.logService.trace('cli#install', this.target); - this.notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", this.productService.applicationName)); + logService.trace('cli#install', target); + notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", productService.applicationName)); }); }); } - private isInstalled(): Promise { - return pfs.lstat(this.target) + private isInstalled(target: string): Promise { + return pfs.lstat(target) .then(stat => stat.isSymbolicLink()) - .then(() => extpath.realpath(this.target)) + .then(() => extpath.realpath(target)) .then(link => link === getSource()) .then(undefined, ignore('ENOENT', false)); } - - private createBinFolderAndSymlinkAsAdmin(): Promise { - return new Promise((resolve, reject) => { - const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - - this.dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => { - switch (result.choice) { - case 0 /* OK */: - const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + this.target + '\'\\" with administrator privileges"'; - - promisify(cp.exec)(command, {}) - .then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'.")))) - .then(() => resolve(), reject); - break; - case 1 /* Cancel */: - reject(new Error(nls.localize('aborted', "Aborted"))); - break; - } - }); - }); - } } -class UninstallAction extends Action { +class UninstallAction extends Action2 { - static readonly ID = 'workbench.action.uninstallCommandLine'; - static readonly LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName); - - constructor( - id: string, - label: string, - @INotificationService private readonly notificationService: INotificationService, - @ILogService private readonly logService: ILogService, - @IDialogService private readonly dialogService: IDialogService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); + constructor() { + super({ + id: 'workbench.action.uninstallCommandLine', + title: { + value: nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName), + original: `Shell Command: Uninstall \'${product.applicationName}\' command from PATH` + }, + category, + f1: true, + precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', '')) + }); } - private get target(): string { - return `/usr/local/bin/${this.productService.applicationName}`; - } + run(accessor: ServicesAccessor): Promise { + const productService = accessor.get(IProductService); + const notificationService = accessor.get(INotificationService); + const logService = accessor.get(ILogService); + const dialogService = accessor.get(IDialogService); + const target = `/usr/local/bin/${productService.applicationName}`; - run(): Promise { return isAvailable().then(isAvailable => { if (!isAvailable) { const message = nls.localize('not available', "This command is not available"); - this.notificationService.info(message); + notificationService.info(message); return undefined; } const uninstall = () => { - return pfs.unlink(this.target) + return pfs.unlink(target) .then(undefined, ignore('ENOENT', null)); }; return uninstall().then(undefined, err => { if (err.code === 'EACCES') { - return this.deleteSymlinkAsAdmin(); + return new Promise(async (resolve, reject) => { + const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; + + const { choice } = await dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }); + switch (choice) { + case 0 /* OK */: + const command = 'osascript -e "do shell script \\"rm \'' + target + '\'\\" with administrator privileges"'; + + promisify(cp.exec)(command, {}) + .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", target)))) + .then(() => resolve(), reject); + break; + case 1 /* Cancel */: + reject(new Error(nls.localize('aborted', "Aborted"))); + break; + } + }); } return Promise.reject(err); }).then(() => { - this.logService.trace('cli#uninstall', this.target); - this.notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", this.productService.applicationName)); + logService.trace('cli#uninstall', target); + notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", productService.applicationName)); }); }); } - - private deleteSymlinkAsAdmin(): Promise { - return new Promise(async (resolve, reject) => { - const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - - const { choice } = await this.dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }); - switch (choice) { - case 0 /* OK */: - const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"'; - - promisify(cp.exec)(command, {}) - .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target)))) - .then(() => resolve(), reject); - break; - case 1 /* Cancel */: - reject(new Error(nls.localize('aborted', "Aborted"))); - break; - } - }); - } } -if (platform.isMacintosh) { - const category = nls.localize('shellCommand', "Shell Command"); - - const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); - workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallAction), `Shell Command: Install \'${product.applicationName}\' command in PATH`, category); - workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(UninstallAction), `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`, category); -} +registerAction2(InstallAction); +registerAction2(UninstallAction); diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index 1779568e..87cbcc7d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -21,7 +21,7 @@ import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOpti import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -120,7 +120,7 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget { if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) { alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'.")); - this._configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER); + this._configurationService.updateValue('editor.accessibilitySupport', 'on'); e.preventDefault(); e.stopPropagation(); diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index d01c06d5..c3ede411 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -50,7 +50,7 @@ class DiffEditorHelperContribution extends Disposable implements IDiffEditorCont [{ label: nls.localize('removeTimeout', "Remove limit"), run: () => { - this._configurationService.updateValue('diffEditor.maxComputationTime', 0, ConfigurationTarget.USER); + this._configurationService.updateValue('diffEditor.maxComputationTime', 0); } }], {} diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts index 9b8cf7a3..cb7b1e97 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -12,7 +12,7 @@ import { Delayer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; -import { SimpleButton, findCloseIcon, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget'; +import { SimpleButton, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -21,6 +21,7 @@ import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/b import { ReplaceInput, IReplaceInputStyles } from 'vs/base/browser/ui/findinput/replaceInput'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -146,7 +147,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this.prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL, - className: findPreviousMatchIcon.classNames, + icon: findPreviousMatchIcon, onTrigger: () => { this.find(true); } @@ -154,7 +155,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this.nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL, - className: findNextMatchIcon.classNames, + icon: findNextMatchIcon, onTrigger: () => { this.find(false); } @@ -162,7 +163,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { const closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL, - className: findCloseIcon.classNames, + icon: widgetClose, onTrigger: () => { this.hide(); } @@ -220,7 +221,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._replaceBtn = this._register(new SimpleButton({ label: NLS_REPLACE_BTN_LABEL, - className: findReplaceIcon.classNames, + icon: findReplaceIcon, onTrigger: () => { this.replaceOne(); } @@ -229,7 +230,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { // Replace all button this._replaceAllBtn = this._register(new SimpleButton({ label: NLS_REPLACE_ALL_BTN_LABEL, - className: findReplaceAllIcon.classNames, + icon: findReplaceAllIcon, onTrigger: () => { this.replaceAll(); } @@ -452,6 +453,6 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index e15e3ee7..6226e8fb 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -12,12 +12,13 @@ import { Delayer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; -import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon, findCloseIcon } from 'vs/editor/contrib/find/findWidget'; +import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon } from 'vs/editor/contrib/find/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput } from 'vs/platform/browser/contextScopedHistoryWidget'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -94,7 +95,7 @@ export abstract class SimpleFindWidget extends Widget { this.prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL, - className: findPreviousMatchIcon.classNames, + icon: findPreviousMatchIcon, onTrigger: () => { this.find(true); } @@ -102,7 +103,7 @@ export abstract class SimpleFindWidget extends Widget { this.nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL, - className: findNextMatchIcon.classNames, + icon: findNextMatchIcon, onTrigger: () => { this.find(false); } @@ -110,7 +111,7 @@ export abstract class SimpleFindWidget extends Widget { const closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL, - className: findCloseIcon.classNames, + icon: widgetClose, onTrigger: () => { this.hide(); } @@ -285,6 +286,6 @@ registerThemingParticipant((theme, collector) => { const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { - collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts b/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts index 8268057a..f7a7ea98 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts @@ -11,7 +11,6 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; /** * Shows a message when opening a large file which has been memory optimized (and features disabled). @@ -24,14 +23,9 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC private readonly _editor: ICodeEditor, @INotificationService private readonly _notificationService: INotificationService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - // opt-in to syncing - const neverShowAgainId = 'editor.contrib.largeFileOptimizationsWarner'; - storageKeysSyncRegistryService.registerStorageKey({ key: neverShowAgainId, version: 1 }); - this._register(this._editor.onDidChangeModel((e) => { const model = this._editor.getModel(); if (!model) { @@ -61,7 +55,7 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC }); } } - ], { neverShowAgain: { id: neverShowAgainId } }); + ], { neverShowAgain: { id: 'editor.contrib.largeFileOptimizationsWarner' } }); } })); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts index c05ec1cf..eb456674 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { IKeyMods, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IEditor } from 'vs/editor/common/editorCommon'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; import { AbstractGotoLineQuickAccessProvider } from 'vs/editor/contrib/quickAccess/gotoLineQuickAccess'; @@ -17,6 +16,7 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProvider { @@ -41,10 +41,12 @@ export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProv return this.editorService.activeTextEditorControl; } - protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { + protected gotoLocation(context: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { // Check for sideBySide use if ((options.keyMods.ctrlCmd || options.forceSideBySide) && this.editorService.activeEditor) { + context.restoreViewState?.(); // since we open to the side, restore view state in this editor + this.editorService.openEditor(this.editorService.activeEditor, { selection: options.range, pinned: options.keyMods.alt || this.configuration.openEditorPinned, @@ -54,7 +56,7 @@ export class GotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProv // Otherwise let parent handle it else { - super.gotoLocation(editor, options); + super.gotoLocation(context, options); } } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts index 5f73a423..bb7642fe 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts @@ -26,6 +26,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess'; export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider { @@ -42,11 +43,12 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess //#region DocumentSymbols (text editor required) - protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable { + protected provideWithTextEditor(context: IQuickAccessTextEditorContext, picker: IQuickPick, token: CancellationToken): IDisposable { if (this.canPickFromTableOfContents()) { return this.doGetTableOfContentsPicks(picker); } - return super.provideWithTextEditor(editor, picker, token); + + return super.provideWithTextEditor(context, picker, token); } private get configuration() { @@ -62,10 +64,12 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess return this.editorService.activeTextEditorControl; } - protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { + protected gotoLocation(context: IQuickAccessTextEditorContext, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void { // Check for sideBySide use if ((options.keyMods.ctrlCmd || options.forceSideBySide) && this.editorService.activeEditor) { + context.restoreViewState?.(); // since we open to the side, restore view state in this editor + this.editorService.openEditor(this.editorService.activeEditor, { selection: options.range, pinned: options.keyMods.alt || this.configuration.openEditorPinned, @@ -75,7 +79,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess // Otherwise let parent handle it else { - super.gotoLocation(editor, options); + super.gotoLocation(context, options); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index f2770354..76ffc0b6 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -287,8 +287,7 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { ? setting : Object.keys(setting).filter(x => setting[x]); - const codeActionsOnSave = settingItems - .map(x => new CodeActionKind(x)); + const codeActionsOnSave = this.createCodeActionsOnSave(settingItems); if (!Array.isArray(setting)) { codeActionsOnSave.sort((a, b) => { @@ -319,6 +318,15 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { await this.applyOnSaveActions(textEditorModel, codeActionsOnSave, excludedActions, progress, token); } + private createCodeActionsOnSave(settingItems: readonly string[]): CodeActionKind[] { + const kinds = settingItems.map(x => new CodeActionKind(x)); + + // Remove subsets + return kinds.filter(kind => { + return kinds.every(otherKind => otherKind.equals(kind) || !otherKind.contains(kind)); + }); + } + private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], progress: IProgress, token: CancellationToken): Promise { const getActionProgress = new class implements IProgress { diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts index ef2018bc..858bf7be 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -42,7 +42,7 @@ export class ToggleColumnSelectionAction extends Action { public async run(): Promise { const oldValue = this._configurationService.getValue('editor.columnSelection'); const codeEditor = this._getCodeEditor(); - await this._configurationService.updateValue('editor.columnSelection', !oldValue, ConfigurationTarget.USER); + await this._configurationService.updateValue('editor.columnSelection', !oldValue); const newValue = this._configurationService.getValue('editor.columnSelection'); if (!codeEditor || codeEditor !== this._getCodeEditor() || oldValue === newValue || !codeEditor.hasModel()) { return; diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts index d40ee948..fbd79e00 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -25,7 +25,7 @@ export class ToggleMinimapAction extends Action { public run(): Promise { const newValue = !this._configurationService.getValue('editor.minimap.enabled'); - return this._configurationService.updateValue('editor.minimap.enabled', newValue, ConfigurationTarget.USER); + return this._configurationService.updateValue('editor.minimap.enabled', newValue); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts index 2c340ac0..36a63179 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import * as platform from 'vs/base/common/platform'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -33,7 +33,7 @@ export class ToggleMultiCursorModifierAction extends Action { const editorConf = this.configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); const newValue: 'ctrlCmd' | 'alt' = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'alt' : 'ctrlCmd'); - return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts index cc439ce4..d530f1a8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -26,7 +26,7 @@ export class ToggleRenderControlCharacterAction extends Action { public run(): Promise { let newRenderControlCharacters = !this._configurationService.getValue('editor.renderControlCharacters'); - return this._configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters, ConfigurationTarget.USER); + return this._configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts index 80a26aee..5a5174b8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; @@ -34,7 +34,7 @@ export class ToggleRenderWhitespaceAction extends Action { newRenderWhitespace = 'none'; } - return this._configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace, ConfigurationTarget.USER); + return this._configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 17acd2da..a0df024b 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -10,33 +10,25 @@ import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { EditorOption, EditorOptions } from 'vs/editor/common/config/editorOptions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { DefaultSettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Codicon } from 'vs/base/common/codicons'; const transientWordWrapState = 'transientWordWrapState'; const isWordWrapMinifiedKey = 'isWordWrapMinified'; const isDominatedByLongLinesKey = 'isDominatedByLongLines'; -const inDiffEditorKey = 'inDiffEditor'; /** * State written/read by the toggle word wrap action and associated with a particular model. */ interface IWordWrapTransientState { - readonly forceWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; - readonly forceWordWrapMinified: boolean; -} - -interface IWordWrapState { - readonly configuredWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded' | undefined; - readonly configuredWordWrapMinified: boolean; - readonly transientState: IWordWrapTransientState | null; + readonly wordWrapOverride: 'on' | 'off'; } /** @@ -49,70 +41,10 @@ export function writeTransientState(model: ITextModel, state: IWordWrapTransient /** * Read (in memory) the word wrap state for a particular model. */ -function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState { +function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState | null { return codeEditorService.getTransientModelProperty(model, transientWordWrapState); } -function readWordWrapState(model: ITextModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState { - const editorConfig = configurationService.getValue(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean }; - let _configuredWordWrap = editorConfig && (typeof editorConfig.wordWrap === 'string' || typeof editorConfig.wordWrap === 'boolean') ? editorConfig.wordWrap : undefined; - - // Compatibility with old true or false values - if (_configuredWordWrap === true) { - _configuredWordWrap = 'on'; - } else if (_configuredWordWrap === false) { - _configuredWordWrap = 'off'; - } - - const _configuredWordWrapMinified = editorConfig && typeof editorConfig.wordWrapMinified === 'boolean' ? editorConfig.wordWrapMinified : undefined; - const _transientState = readTransientState(model, codeEditorService); - return { - configuredWordWrap: _configuredWordWrap, - configuredWordWrapMinified: (typeof _configuredWordWrapMinified === 'boolean' ? _configuredWordWrapMinified : EditorOptions.wordWrapMinified.defaultValue), - transientState: _transientState - }; -} - -function toggleWordWrap(editor: ICodeEditor, state: IWordWrapState): IWordWrapState { - if (state.transientState) { - // toggle off => go to null - return { - configuredWordWrap: state.configuredWordWrap, - configuredWordWrapMinified: state.configuredWordWrapMinified, - transientState: null - }; - } - - let transientState: IWordWrapTransientState; - - const actualWrappingInfo = editor.getOption(EditorOption.wrappingInfo); - if (actualWrappingInfo.isWordWrapMinified) { - // => wrapping due to minified file - transientState = { - forceWordWrap: 'off', - forceWordWrapMinified: false - }; - } else if (state.configuredWordWrap !== 'off') { - // => wrapping is configured to be on (or some variant) - transientState = { - forceWordWrap: 'off', - forceWordWrapMinified: false - }; - } else { - // => wrapping is configured to be off - transientState = { - forceWordWrap: 'on', - forceWordWrapMinified: state.configuredWordWrapMinified - }; - } - - return { - configuredWordWrap: state.configuredWordWrap, - configuredWordWrapMinified: state.configuredWordWrapMinified, - transientState: transientState - }; -} - const TOGGLE_WORD_WRAP_ID = 'editor.action.toggleWordWrap'; class ToggleWordWrapAction extends EditorAction { @@ -138,14 +70,7 @@ class ToggleWordWrapAction extends EditorAction { if (!editor.hasModel()) { return; } - if (editor.getOption(EditorOption.inDiffEditor)) { - // Cannot change wrapping settings inside the diff editor - const notificationService = accessor.get(INotificationService); - notificationService.info(nls.localize('wordWrap.notInDiffEditor', "Cannot toggle word wrap in a diff editor.")); - return; - } - const textResourceConfigurationService = accessor.get(ITextResourceConfigurationService); const codeEditorService = accessor.get(ICodeEditorService); const model = editor.getModel(); @@ -154,12 +79,21 @@ class ToggleWordWrapAction extends EditorAction { } // Read the current state - const currentState = readWordWrapState(model, textResourceConfigurationService, codeEditorService); + const transientState = readTransientState(model, codeEditorService); + // Compute the new state - const newState = toggleWordWrap(editor, currentState); + let newState: IWordWrapTransientState | null; + if (transientState) { + newState = null; + } else { + const actualWrappingInfo = editor.getOption(EditorOption.wrappingInfo); + const wordWrapOverride = (actualWrappingInfo.wrappingColumn === -1 ? 'on' : 'off'); + newState = { wordWrapOverride }; + } + // Write the new state // (this will cause an event and the controller will apply the state) - writeTransientState(model, newState.transientState, codeEditorService); + writeTransientState(model, newState, codeEditorService); } } @@ -168,93 +102,75 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution public static readonly ID = 'editor.contrib.toggleWordWrapController'; constructor( - private readonly editor: ICodeEditor, - @IContextKeyService readonly contextKeyService: IContextKeyService, - @ITextResourceConfigurationService readonly configurationService: ITextResourceConfigurationService, - @ICodeEditorService readonly codeEditorService: ICodeEditorService + private readonly _editor: ICodeEditor, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService ) { super(); - const options = this.editor.getOptions(); + const options = this._editor.getOptions(); const wrappingInfo = options.get(EditorOption.wrappingInfo); - const isWordWrapMinified = this.contextKeyService.createKey(isWordWrapMinifiedKey, wrappingInfo.isWordWrapMinified); - const isDominatedByLongLines = this.contextKeyService.createKey(isDominatedByLongLinesKey, wrappingInfo.isDominatedByLongLines); - const inDiffEditor = this.contextKeyService.createKey(inDiffEditorKey, options.get(EditorOption.inDiffEditor)); + const isWordWrapMinified = this._contextKeyService.createKey(isWordWrapMinifiedKey, wrappingInfo.isWordWrapMinified); + const isDominatedByLongLines = this._contextKeyService.createKey(isDominatedByLongLinesKey, wrappingInfo.isDominatedByLongLines); let currentlyApplyingEditorConfig = false; - this._register(editor.onDidChangeConfiguration((e) => { - if (!e.hasChanged(EditorOption.wrappingInfo) && !e.hasChanged(EditorOption.inDiffEditor)) { + this._register(_editor.onDidChangeConfiguration((e) => { + if (!e.hasChanged(EditorOption.wrappingInfo)) { return; } - const options = this.editor.getOptions(); + const options = this._editor.getOptions(); const wrappingInfo = options.get(EditorOption.wrappingInfo); isWordWrapMinified.set(wrappingInfo.isWordWrapMinified); isDominatedByLongLines.set(wrappingInfo.isDominatedByLongLines); - inDiffEditor.set(options.get(EditorOption.inDiffEditor)); if (!currentlyApplyingEditorConfig) { // I am not the cause of the word wrap getting changed ensureWordWrapSettings(); } })); - this._register(editor.onDidChangeModel((e) => { + this._register(_editor.onDidChangeModel((e) => { ensureWordWrapSettings(); })); - this._register(codeEditorService.onDidChangeTransientModelProperty(() => { + this._register(_codeEditorService.onDidChangeTransientModelProperty(() => { ensureWordWrapSettings(); })); const ensureWordWrapSettings = () => { - if (this.editor.getContribution(DefaultSettingsEditorContribution.ID)) { + if (this._editor.getContribution(DefaultSettingsEditorContribution.ID)) { // in the settings editor... return; } - if (this.editor.isSimpleWidget) { + if (this._editor.isSimpleWidget) { // in a simple widget... return; } // Ensure correct word wrap settings - const newModel = this.editor.getModel(); + const newModel = this._editor.getModel(); if (!newModel) { return; } - if (this.editor.getOption(EditorOption.inDiffEditor)) { - return; - } - if (!canToggleWordWrap(newModel.uri)) { return; } - // Read current configured values and toggle state - const desiredState = readWordWrapState(newModel, this.configurationService, this.codeEditorService); + const transientState = readTransientState(newModel, this._codeEditorService); // Apply the state try { currentlyApplyingEditorConfig = true; - this._applyWordWrapState(desiredState); + this._applyWordWrapState(transientState); } finally { currentlyApplyingEditorConfig = false; } }; } - private _applyWordWrapState(state: IWordWrapState): void { - if (state.transientState) { - // toggle is on - this.editor.updateOptions({ - wordWrap: state.transientState.forceWordWrap, - wordWrapMinified: state.transientState.forceWordWrapMinified - }); - return; - } - - // toggle is off - this.editor.updateOptions({ - wordWrap: state.configuredWordWrap, - wordWrapMinified: state.configuredWordWrapMinified + private _applyWordWrapState(state: IWordWrapTransientState | null): void { + const wordWrapOverride2 = state ? state.wordWrapOverride : 'inherit'; + this._editor.updateOptions({ + wordWrapOverride2: wordWrapOverride2 }); } } @@ -275,14 +191,11 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_WORD_WRAP_ID, title: nls.localize('unwrapMinified', "Disable wrapping for this file"), - icon: { - id: 'codicon/word-wrap' - } + icon: Codicon.wordWrap }, group: 'navigation', order: 1, when: ContextKeyExpr.and( - ContextKeyExpr.not(inDiffEditorKey), ContextKeyExpr.has(isDominatedByLongLinesKey), ContextKeyExpr.has(isWordWrapMinifiedKey) ) @@ -291,14 +204,12 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_WORD_WRAP_ID, title: nls.localize('wrapMinified', "Enable wrapping for this file"), - icon: { - id: 'codicon/word-wrap' - } + icon: Codicon.wordWrap }, group: 'navigation', order: 1, when: ContextKeyExpr.and( - ContextKeyExpr.not(inDiffEditorKey), + EditorContextKeys.inDiffEditor.negate(), ContextKeyExpr.has(isDominatedByLongLinesKey), ContextKeyExpr.not(isWordWrapMinifiedKey) ) diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts index c764c2c4..7718e505 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/startDebugTextMate.ts @@ -70,7 +70,8 @@ class StartDebugTextMate extends Action { }; await this._hostService.openWindow([{ fileUri: URI.file(pathInTemp) }], { forceNewWindow: true }); const textEditorPane = await this._editorService.openEditor({ - resource: model.uri + resource: model.uri, + options: { pinned: true } }); if (!textEditorPane) { return; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts index 08763754..d544a687 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/codeEditor.contribution.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import './displayChangeRemeasureFonts'; import './inputClipboardActions'; import './selectionClipboard'; import './sleepResumeRepaintMinimap'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts new file mode 100644 index 00000000..efcdd898 --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/displayChangeRemeasureFonts.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { IDisplayMainService } from 'vs/platform/display/common/displayMainService'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; + +class DisplayChangeRemeasureFonts extends Disposable implements IWorkbenchContribution { + + constructor( + @IMainProcessService mainProcessService: IMainProcessService + ) { + super(); + const displayMainService = createChannelSender(mainProcessService.getChannel('display')); + displayMainService.onDidDisplayChanged(() => { + clearAllFontInfos(); + }); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DisplayChangeRemeasureFonts, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 249fd9b1..a059627b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -34,6 +34,7 @@ import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commen import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { Codicon } from 'vs/base/common/codicons'; export class CommentNode extends Disposable { private _domNode: HTMLElement; @@ -156,7 +157,7 @@ export class CommentNode extends Disposable { { actionViewItemProvider: action => this.actionViewItemProvider(action as Action), actionRunner: this.actionRunner, - classNames: ['toolbar-toggle-pickReactions', 'codicon', 'codicon-reactions'], + classNames: ['toolbar-toggle-pickReactions', ...Codicon.reactions.classNamesArray], anchorAlignmentProvider: () => AnchorAlignment.RIGHT } ); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 9e66d94a..ffab7e07 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -6,7 +6,6 @@ import * as dom from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action, IAction } from 'vs/base/common/actions'; -import * as arrays from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -32,7 +31,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget'; import { CommentMenus } from 'vs/workbench/contrib/comments/browser/commentMenus'; @@ -48,9 +47,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { PANEL_BORDER } from 'vs/workbench/common/theme'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { Codicon } from 'vs/base/common/codicons'; + + +const collapseIcon = registerIcon('review-comment-collapse', Codicon.chevronUp, nls.localize('collapseIcon', 'Icon to collapse a review comment.')); export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; -const COLLAPSE_ACTION_CLASS = 'expand-review-action codicon-chevron-up'; +const COLLAPSE_ACTION_CLASS = 'expand-review-action ' + ThemeIcon.asClassName(collapseIcon); const COMMENT_SCHEME = 'comment'; @@ -712,10 +716,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget label = this._commentThread.label; if (label === undefined) { - if (this._commentThread.comments && this._commentThread.comments.length) { - const participantsList = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', '); - label = nls.localize('commentThreadParticipants', "Participants: {0}", participantsList); - } else { + if (!(this._commentThread.comments && this._commentThread.comments.length)) { label = nls.localize('startThread', "Start discussion"); } } @@ -884,13 +885,18 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } private _applyTheme(theme: IColorTheme) { - const borderColor = theme.getColor(peekViewBorder) || Color.transparent; + const borderColor = theme.getColor(peekViewBorder); this.style({ - arrowColor: borderColor, - frameColor: borderColor + arrowColor: borderColor || Color.transparent, + frameColor: borderColor || Color.transparent }); const content: string[] = []; + + if (borderColor) { + content.push(`.monaco-editor .review-widget > .body { border-top: 1px solid ${borderColor} }`); + } + const linkColor = theme.getColor(textLinkForeground); if (linkColor) { content.push(`.monaco-editor .review-widget .body .comment-body a { color: ${linkColor} }`); @@ -945,7 +951,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget const fontInfo = this.editor.getOption(EditorOption.fontInfo); content.push(`.monaco-editor .review-widget .body code { - font-family: ${fontInfo.fontFamily}; + font-family: '${fontInfo.fontFamily}'; font-size: ${fontInfo.fontSize}px; font-weight: ${fontInfo.fontWeight}; }`); diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index 8d77ef60..5ed011a2 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -34,7 +34,6 @@ export class CommentsPanel extends ViewPane { private tree!: CommentsList; private treeContainer!: HTMLElement; private messageBoxContainer!: HTMLElement; - private messageBox!: HTMLElement; private commentsModel!: CommentsModel; private collapseAllAction?: IAction; @@ -85,6 +84,18 @@ export class CommentsPanel extends ViewPane { this.renderComments(); } + public focus(): void { + if (this.tree && this.tree.getHTMLElement() === document.activeElement) { + return; + } + + if (!this.commentsModel.hasCommentThreads() && this.messageBoxContainer) { + this.messageBoxContainer.focus(); + } else if (this.tree) { + this.tree.domFocus(); + } + } + private applyStyles(styleElement: HTMLStyleElement) { const content: string[] = []; @@ -114,8 +125,8 @@ export class CommentsPanel extends ViewPane { private async renderComments(): Promise { this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads()); - await this.tree.setInput(this.commentsModel); this.renderMessage(); + await this.tree.setInput(this.commentsModel); } public getActions(): IAction[] { @@ -138,12 +149,11 @@ export class CommentsPanel extends ViewPane { private createMessageBox(parent: HTMLElement): void { this.messageBoxContainer = dom.append(parent, dom.$('.message-box-container')); - this.messageBox = dom.append(this.messageBoxContainer, dom.$('span')); - this.messageBox.setAttribute('tabindex', '0'); + this.messageBoxContainer.setAttribute('tabIndex', '0'); } private renderMessage(): void { - this.messageBox.textContent = this.commentsModel.getMessage(); + this.messageBoxContainer.textContent = this.commentsModel.getMessage(); this.messageBoxContainer.classList.toggle('hidden', this.commentsModel.hasCommentThreads()); } @@ -231,18 +241,23 @@ export class CommentsPanel extends ViewPane { return true; } - private refresh(): void { + private async refresh(): Promise { if (this.isVisible()) { if (this.collapseAllAction) { this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads(); } this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads()); - this.tree.updateChildren().then(() => { - this.renderMessage(); - }, (e) => { - console.log(e); - }); + this.renderMessage(); + await this.tree.updateChildren(); + + if (this.tree.getSelection().length === 0 && this.commentsModel.hasCommentThreads()) { + const firstComment = this.commentsModel.resourceCommentThreads[0].commentThreads[0]; + if (firstComment) { + this.tree.setFocus([firstComment]); + this.tree.setSelection([firstComment]); + } + } } } diff --git a/src/vs/workbench/contrib/comments/browser/media/panel.css b/src/vs/workbench/contrib/comments/browser/media/panel.css index 1633bd41..b931b6cd 100644 --- a/src/vs/workbench/contrib/comments/browser/media/panel.css +++ b/src/vs/workbench/contrib/comments/browser/media/panel.css @@ -48,10 +48,7 @@ .comments-panel .comments-panel-container .message-box-container { line-height: 22px; padding-left: 20px; -} - -.comments-panel .comments-panel-container .message-box-container span:focus { - outline: none; + height: inherit; } .comments-panel .comments-panel-container .tree-container .count-badge-wrapper { @@ -61,4 +58,4 @@ .comments-panel .comments-panel-container .tree-container .comment-container { line-height: 22px; margin-right: 5px; -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/comments/common/commentModel.ts b/src/vs/workbench/contrib/comments/common/commentModel.ts index 0dc8a239..e56c2267 100644 --- a/src/vs/workbench/contrib/comments/common/commentModel.ts +++ b/src/vs/workbench/contrib/comments/common/commentModel.ts @@ -134,7 +134,7 @@ export class CommentsModel { public getMessage(): string { if (!this.resourceCommentThreads.length) { - return localize('noComments', "There are no comments on this review."); + return localize('noComments', "There are no comments in this workspace yet."); } else { return ''; } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 3bcaaacd..c57aa6dd 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce, distinct } from 'vs/base/common/arrays'; +import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -173,7 +174,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ : undefined, detail: editorDescriptor.providerDisplayName, buttons: resourceExt ? [{ - iconClass: 'codicon-settings-gear', + iconClass: Codicon.settingsGear.classNames, tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) }] : undefined })); @@ -447,6 +448,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo constructor( @IEditorService private readonly editorService: IEditorService, @ICustomEditorService private readonly customEditorService: ICustomEditorService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -651,7 +653,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo if (modifiedOverride || originalOverride) { return { override: (async () => { - const input = new DiffEditorInput(editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); + const input = this.instantiationService.createInstance(DiffEditorInput, editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput, true); return this.editorService.openEditor(input, { ...options, override: false }, group); })(), }; diff --git a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts index d61f9874..98fcdb9b 100644 --- a/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts +++ b/src/vs/workbench/contrib/customEditor/common/contributedCustomEditors.ts @@ -8,12 +8,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Memento } from 'vs/workbench/common/memento'; import { CustomEditorDescriptor, CustomEditorInfo, CustomEditorPriority } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { customEditorsExtensionPoint, ICustomEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/common/extensionPoint'; -import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { DEFAULT_EDITOR_ID } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; const builtinProviderDisplayName = nls.localize('builtinProviderDisplayName', "Built-in"); @@ -40,7 +40,7 @@ export class ContributedCustomEditors extends Disposable { this._memento = new Memento(ContributedCustomEditors.CUSTOM_EDITORS_STORAGE_ID, storageService); - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); for (const info of (mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] || []) as CustomEditorDescriptor[]) { this.add(new CustomEditorInfo(info)); } @@ -68,7 +68,7 @@ export class ContributedCustomEditors extends Disposable { } } - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); mementoObject[ContributedCustomEditors.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._editors.values()); this._memento.saveMemento(); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index bd7d28ee..966dc83e 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -22,7 +22,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { generateUuid } from 'vs/base/common/uuid'; import { memoize } from 'vs/base/common/decorators'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -32,9 +32,10 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { isSafari } from 'vs/base/browser/browser'; -import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -46,7 +47,7 @@ interface IBreakpointDecoration { } const breakpointHelperDecoration: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-hint', + glyphMarginClassName: ThemeIcon.asClassName(icons.debugBreakpointHint), stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }; @@ -72,7 +73,7 @@ export function createBreakpointDecorations(model: ITextModel, breakpoints: Read } function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, state: State, breakpointsActivated: boolean, showBreakpointsInOverviewRuler: boolean): IModelDecorationOptions { - const { className, message } = getBreakpointMessageAndClassName(state, breakpointsActivated, breakpoint, undefined); + const { icon, message } = getBreakpointMessageAndIcon(state, breakpointsActivated, breakpoint, undefined); let glyphMarginHoverMessage: MarkdownString | undefined; if (message) { @@ -94,7 +95,7 @@ function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoi const renderInline = breakpoint.column && (breakpoint.column > model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber)); return { - glyphMarginClassName: `${className}`, + glyphMarginClassName: ThemeIcon.asClassName(icon), glyphMarginHoverMessage, stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, beforeContentClassName: renderInline ? `debug-breakpoint-placeholder` : undefined, @@ -174,7 +175,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi private registerListeners(): void { this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => { - if (!this.debugService.getConfigurationManager().hasDebuggers()) { + if (!this.debugService.getAdapterManager().hasDebuggers()) { return; } @@ -183,7 +184,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { return; } - const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model); + const canSetBreakpoints = this.debugService.canSetBreakpointsIn(model); const lineNumber = e.target.position.lineNumber; const uri = model.uri; @@ -252,13 +253,13 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi * 2. When users click on line numbers, the breakpoint hint displays immediately, however it doesn't create the breakpoint unless users click on the left gutter. On a touch screen, it's hard to click on that small area. */ this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => { - if (!this.debugService.getConfigurationManager().hasDebuggers()) { + if (!this.debugService.getAdapterManager().hasDebuggers()) { return; } let showBreakpointHintAtLineNumber = -1; const model = this.editor.getModel(); - if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) && + if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.canSetBreakpointsIn(model) && this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { const data = e.target.detail as IMarginData; if (!data.isAfterLines) { @@ -453,9 +454,9 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi // Candidate decoration has a breakpoint attached when a breakpoint is already at that location and we did not yet set a decoration there // In practice this happens for the first breakpoint that was set on a line // We could have also rendered this first decoration as part of desiredBreakpointDecorations however at that moment we have no location information - const cssClass = candidate.breakpoint ? getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint, this.labelService).className : 'codicon-debug-breakpoint-disabled'; + const icon = candidate.breakpoint ? getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), candidate.breakpoint, this.labelService).icon : icons.debugBreakpointDisabled; const contextMenuActions = () => this.getContextMenuActions(candidate.breakpoint ? [candidate.breakpoint] : [], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn); - const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, cssClass, candidate.breakpoint, this.debugService, this.contextMenuService, contextMenuActions); + const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, ThemeIcon.asClassName(icon), candidate.breakpoint, this.debugService, this.contextMenuService, contextMenuActions); return { decorationId, @@ -575,9 +576,8 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable { private create(cssClass: string | null | undefined): void { this.domNode = $('.inline-breakpoint-widget'); - this.domNode.classList.add('codicon'); if (cssClass) { - this.domNode.classList.add(cssClass); + this.domNode.classList.add(...cssClass.split(' ')); } this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, async e => { if (this.breakpoint) { @@ -645,15 +645,15 @@ registerThemingParticipant((theme, collector) => { const debugIconBreakpointColor = theme.getColor(debugIconBreakpointForeground); if (debugIconBreakpointColor) { collector.addRule(` - .monaco-workbench .codicon-debug-breakpoint, - .monaco-workbench .codicon-debug-breakpoint-conditional, - .monaco-workbench .codicon-debug-breakpoint-log, - .monaco-workbench .codicon-debug-breakpoint-function, - .monaco-workbench .codicon-debug-breakpoint-data, - .monaco-workbench .codicon-debug-breakpoint-unsupported, - .monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']), - .monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe-focused::after, - .monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe::after { + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointConditional)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointLog)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointFunction)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointData)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointUnsupported)}, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpointHint)}:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']), + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}${ThemeIcon.asCSSSelector(icons.debugStackframeFocused)}::after, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugBreakpoint)}${ThemeIcon.asCSSSelector(icons.debugStackframe)}::after { color: ${debugIconBreakpointColor} !important; } `); @@ -680,7 +680,7 @@ registerThemingParticipant((theme, collector) => { const debugIconBreakpointCurrentStackframeForegroundColor = theme.getColor(debugIconBreakpointCurrentStackframeForeground); if (debugIconBreakpointCurrentStackframeForegroundColor) { collector.addRule(` - .monaco-workbench .codicon-debug-stackframe, + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframe)}, .monaco-editor .debug-top-stack-frame-column::before { color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important; } @@ -690,7 +690,7 @@ registerThemingParticipant((theme, collector) => { const debugIconBreakpointStackframeFocusedColor = theme.getColor(debugIconBreakpointStackframeForeground); if (debugIconBreakpointStackframeFocusedColor) { collector.addRule(` - .monaco-workbench .codicon-debug-stackframe-focused { + .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframeFocused)} { color: ${debugIconBreakpointStackframeFocusedColor} !important; } `); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 97636b20..7fa72c43 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -13,7 +13,7 @@ import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAl import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Constants } from 'vs/base/common/uint'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list'; @@ -37,6 +37,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -93,6 +94,7 @@ export class BreakpointsView extends ViewPane { this.list = >this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [ this.instantiationService.createInstance(BreakpointsRenderer), new ExceptionBreakpointsRenderer(this.debugService), + new ExceptionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService), this.instantiationService.createInstance(FunctionBreakpointsRenderer), this.instantiationService.createInstance(DataBreakpointsRenderer), new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService, this.labelService) @@ -122,7 +124,7 @@ export class BreakpointsView extends ViewPane { const resourceNavigator = this._register(new ListResourceNavigator(this.list, { configurationService: this.configurationService })); this._register(resourceNavigator.onDidOpen(async e => { - if (e.element === null) { + if (!e.element) { return; } @@ -130,14 +132,12 @@ export class BreakpointsView extends ViewPane { return; } - const element = this.list.element(e.element); - - if (element instanceof Breakpoint) { - openBreakpointSource(element, e.sideBySide, e.editorOptions.preserveFocus || false, this.debugService, this.editorService); + if (e.element instanceof Breakpoint) { + openBreakpointSource(e.element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService); } - if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) { + if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.debugService.getViewModel().getSelectedBreakpoint()) { // double click - this.debugService.getViewModel().setSelectedFunctionBreakpoint(element); + this.debugService.getViewModel().setSelectedBreakpoint(e.element); this.onBreakpointsChange(); } })); @@ -188,38 +188,48 @@ export class BreakpointsView extends ViewPane { const actions: IAction[] = []; const element = e.element; - const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint"); - if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) { - actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => { - if (element instanceof Breakpoint) { - const editor = await openBreakpointSource(element, false, false, this.debugService, this.editorService); - if (editor) { - const codeEditor = editor.getControl(); - if (isCodeEditor(codeEditor)) { - codeEditor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column); - } - } - } else { - this.debugService.getViewModel().setSelectedFunctionBreakpoint(element); + if (element instanceof ExceptionBreakpoint) { + if (element.supportsCondition) { + actions.push(new Action('workbench.action.debug.editExceptionBreakpointCondition', nls.localize('editCondition', "Edit Condition"), '', true, async () => { + this.debugService.getViewModel().setSelectedBreakpoint(element); this.onBreakpointsChange(); - } - })); + })); + } + } else { + const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint"); + if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) { + actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => { + if (element instanceof Breakpoint) { + const editor = await openBreakpointSource(element, false, false, true, this.debugService, this.editorService); + if (editor) { + const codeEditor = editor.getControl(); + if (isCodeEditor(codeEditor)) { + codeEditor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column); + } + } + } else { + this.debugService.getViewModel().setSelectedBreakpoint(element); + this.onBreakpointsChange(); + } + })); + actions.push(new Separator()); + } + + + actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService)); + + if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length >= 1) { + actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); + actions.push(new Separator()); + + actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); + actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); + } + actions.push(new Separator()); + actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService)); } - actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService)); - - if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) { - actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - actions.push(new Separator()); - - actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - } - - actions.push(new Separator()); - actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService)); - this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions, @@ -285,7 +295,7 @@ class BreakpointsDelegate implements IListVirtualDelegate { return BreakpointsRenderer.ID; } if (element instanceof FunctionBreakpoint) { - const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint(); + const selected = this.debugService.getViewModel().getSelectedBreakpoint(); if (!element.name || (selected && selected.getId() === element.getId())) { return FunctionBreakpointInputRenderer.ID; } @@ -293,6 +303,10 @@ class BreakpointsDelegate implements IListVirtualDelegate { return FunctionBreakpointsRenderer.ID; } if (element instanceof ExceptionBreakpoint) { + const selected = this.debugService.getViewModel().getSelectedBreakpoint(); + if (selected && selected.getId() === element.getId()) { + return ExceptionBreakpointInputRenderer.ID; + } return ExceptionBreakpointsRenderer.ID; } if (element instanceof DataBreakpoint) { @@ -320,7 +334,11 @@ interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData { filePath: HTMLElement; } -interface IInputTemplateData { +interface IExceptionBreakpointTemplateData extends IBaseBreakpointTemplateData { + condition: HTMLElement; +} + +interface IFunctionBreakpointInputTemplateData { inputBox: InputBox; checkbox: HTMLInputElement; icon: HTMLElement; @@ -329,6 +347,14 @@ interface IInputTemplateData { toDispose: IDisposable[]; } +interface IExceptionBreakpointInputTemplateData { + inputBox: InputBox; + checkbox: HTMLInputElement; + breakpoint: IExceptionBreakpoint; + reactedOnEvent: boolean; + toDispose: IDisposable[]; +} + class BreakpointsRenderer implements IListRenderer { constructor( @@ -379,8 +405,8 @@ class BreakpointsRenderer implements IListRenderer { +class ExceptionBreakpointsRenderer implements IListRenderer { constructor( private debugService: IDebugService @@ -408,8 +434,8 @@ class ExceptionBreakpointsRenderer implements IListRenderer { +class FunctionBreakpointInputRenderer implements IListRenderer { constructor( private debugService: IDebugService, @@ -567,8 +596,8 @@ class FunctionBreakpointInputRenderer implements IListRenderer { if (!template.reactedOnEvent) { template.reactedOnEvent = true; - this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined); + this.debugService.getViewModel().setSelectedBreakpoint(undefined); if (inputBox.value && (renamed || template.breakpoint.name)) { this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name); } else { @@ -619,12 +648,12 @@ class FunctionBreakpointInputRenderer implements IListRenderer { + + constructor( + private debugService: IDebugService, + private contextViewService: IContextViewService, + private themeService: IThemeService + ) { + // noop + } + + static readonly ID = 'exceptionbreakpointinput'; + + get templateId() { + return ExceptionBreakpointInputRenderer.ID; + } + + renderTemplate(container: HTMLElement): IExceptionBreakpointInputTemplateData { + const template: IExceptionBreakpointInputTemplateData = Object.create(null); + + const breakpoint = dom.append(container, $('.breakpoint')); + breakpoint.classList.add('exception'); + template.checkbox = createCheckbox(); + + dom.append(breakpoint, template.checkbox); + const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer')); + const inputBox = new InputBox(inputBoxContainer, this.contextViewService, { + placeholder: nls.localize('exceptionBreakpointPlaceholder', "Break when expression evaluates to true"), + ariaLabel: nls.localize('exceptionBreakpointAriaLabel', "Type exception breakpoint condition") + }); + const styler = attachInputBoxStyler(inputBox, this.themeService); + const toDispose: IDisposable[] = [inputBox, styler]; + + const wrapUp = (success: boolean) => { + if (!template.reactedOnEvent) { + template.reactedOnEvent = true; + this.debugService.getViewModel().setSelectedBreakpoint(undefined); + let newCondition = template.breakpoint.condition; + if (success) { + newCondition = inputBox.value !== '' ? inputBox.value : undefined; + } + this.debugService.setExceptionBreakpointCondition(template.breakpoint, newCondition); + } + }; + + toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { + const isEscape = e.equals(KeyCode.Escape); + const isEnter = e.equals(KeyCode.Enter); + if (isEscape || isEnter) { + e.preventDefault(); + e.stopPropagation(); + wrapUp(isEnter); + } + })); + toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { + // Need to react with a timeout on the blur event due to possible concurent splices #56443 + setTimeout(() => { + wrapUp(true); + }); + })); + + template.inputBox = inputBox; + template.toDispose = toDispose; + return template; + } + + renderElement(exceptionBreakpoint: ExceptionBreakpoint, _index: number, data: IExceptionBreakpointInputTemplateData): void { + data.breakpoint = exceptionBreakpoint; + data.reactedOnEvent = false; + data.checkbox.checked = exceptionBreakpoint.enabled; + data.checkbox.disabled = true; + data.inputBox.value = exceptionBreakpoint.condition || ''; + setTimeout(() => { + data.inputBox.focus(); + data.inputBox.select(); + }, 0); + } + + disposeTemplate(templateData: IExceptionBreakpointInputTemplateData): void { dispose(templateData.toDispose); } } @@ -664,14 +775,14 @@ class BreakpointsAccessibilityProvider implements IListAccessibilityProvider { +export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, pinned: boolean, debugService: IDebugService, editorService: IEditorService): Promise { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { return Promise.resolve(undefined); } @@ -695,17 +806,17 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea selection, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, - pinned: !preserveFocus + pinned } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } -export function getBreakpointMessageAndClassName(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, className: string } { +export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: IBreakpoint | IFunctionBreakpoint | IDataBreakpoint, labelService?: ILabelService): { message?: string, icon: ThemeIcon } { const debugActive = state === State.Running || state === State.Stopped; if (!breakpoint.enabled || !breakpointsActivated) { return { - className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-disabled' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-disabled' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-disabled' : 'codicon-debug-breakpoint-disabled', + icon: breakpoint instanceof DataBreakpoint ? icons.debugBreakpointDataDisabled : breakpoint instanceof FunctionBreakpoint ? icons.debugBreakpointFunctionDisabled : breakpoint.logMessage ? icons.debugBreakpointLogDisabled : icons.debugBreakpointDisabled, message: breakpoint.logMessage ? nls.localize('disabledLogpoint', "Disabled Logpoint") : nls.localize('disabledBreakpoint', "Disabled Breakpoint"), }; } @@ -715,7 +826,7 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva }; if (debugActive && !breakpoint.verified) { return { - className: breakpoint instanceof DataBreakpoint ? 'codicon-debug-breakpoint-data-unverified' : breakpoint instanceof FunctionBreakpoint ? 'codicon-debug-breakpoint-function-unverified' : breakpoint.logMessage ? 'codicon-debug-breakpoint-log-unverified' : 'codicon-debug-breakpoint-unverified', + icon: breakpoint instanceof DataBreakpoint ? icons.debugBreakpointDataUnverified : breakpoint instanceof FunctionBreakpoint ? icons.debugBreakpointFunctionUnverified : breakpoint.logMessage ? icons.debugBreakpointLogUnverified : icons.debugBreakpointUnverified, message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : (breakpoint.logMessage ? nls.localize('unverifiedLogpoint', "Unverified Logpoint") : nls.localize('unverifiedBreakopint', "Unverified Breakpoint")), }; } @@ -723,13 +834,13 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva if (breakpoint instanceof FunctionBreakpoint) { if (!breakpoint.supported) { return { - className: 'codicon-debug-breakpoint-function-unverified', + icon: icons.debugBreakpointFunctionUnverified, message: nls.localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"), }; } return { - className: 'codicon-debug-breakpoint-function', + icon: icons.debugBreakpointFunction, message: breakpoint.message || nls.localize('functionBreakpoint', "Function Breakpoint") }; } @@ -737,13 +848,13 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva if (breakpoint instanceof DataBreakpoint) { if (!breakpoint.supported) { return { - className: 'codicon-debug-breakpoint-data-unverified', + icon: icons.debugBreakpointDataUnverified, message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"), }; } return { - className: 'codicon-debug-breakpoint-data', + icon: icons.debugBreakpointData, message: breakpoint.message || nls.localize('dataBreakpoint', "Data Breakpoint") }; } @@ -753,7 +864,7 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva if (!breakpoint.supported) { return { - className: 'codicon-debug-breakpoint-unsupported', + icon: icons.debugBreakpointUnsupported, message: nls.localize('breakpointUnsupported', "Breakpoints of this type are not supported by the debugger"), }; } @@ -762,21 +873,21 @@ export function getBreakpointMessageAndClassName(state: State, breakpointsActiva messages.push(nls.localize('logMessage', "Log Message: {0}", breakpoint.logMessage)); } if (breakpoint.condition) { - messages.push(nls.localize('expression', "Expression: {0}", breakpoint.condition)); + messages.push(nls.localize('expression', "Expression condition: {0}", breakpoint.condition)); } if (breakpoint.hitCondition) { messages.push(nls.localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition)); } return { - className: breakpoint.logMessage ? 'codicon-debug-breakpoint-log' : 'codicon-debug-breakpoint-conditional', + icon: breakpoint.logMessage ? icons.debugBreakpointLog : icons.debugBreakpointConditional, message: appendMessage(messages.join('\n')) }; } const message = ('message' in breakpoint && breakpoint.message) ? breakpoint.message : breakpoint instanceof Breakpoint && labelService ? labelService.getUriLabel(breakpoint.uri) : nls.localize('breakpoint', "Breakpoint"); return { - className: 'codicon-debug-breakpoint', + icon: icons.debugBreakpoint, message }; } diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index ec6bdd2a..9fd5d78b 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -5,9 +5,9 @@ import { Constants } from 'vs/base/common/uint'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; +import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/model'; import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { Event } from 'vs/base/common/event'; @@ -16,17 +16,28 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { distinct } from 'vs/base/common/arrays'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; +const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); +const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; // we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement. const TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-stackframe', - stickiness + glyphMarginClassName: ThemeIcon.asClassName(debugStackframe), + stickiness, + overviewRuler: { + position: OverviewRulerLane.Full, + color: themeColorFromId(topStackFrameColor) + } }; const FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-stackframe-focused', - stickiness + glyphMarginClassName: ThemeIcon.asClassName(debugStackframeFocused), + stickiness, + overviewRuler: { + position: OverviewRulerLane.Full, + color: themeColorFromId(focusedStackFrameColor) + } }; const TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = { isWholeLine: true, @@ -152,6 +163,3 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`); } }); - -const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); -const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index c7d8774c..70851479 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -36,7 +36,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; @@ -46,6 +46,7 @@ import { posix } from 'vs/base/common/path'; import { ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree'; import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -206,7 +207,7 @@ export class CallStackView extends ViewPane { getActions(): IAction[] { if (this.stateMessage.hidden) { - return [new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all')]; + return [new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(icons.debugCollapseAll))]; } return []; @@ -501,7 +502,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer { + const action = new Action('debug.callStack.restartFrame', nls.localize('restartFrame', "Restart Frame"), ThemeIcon.asClassName(icons.debugRestartFrame), true, async () => { try { await stackFrame.restart(); } catch (e) { @@ -918,7 +919,7 @@ class CallStackDataSource implements IAsyncDataSource callStack.length && callStack.length > 1) { + if (!thread.reachedEndOfCallStack && thread.stoppedDetails) { callStack = callStack.concat([new ThreadAndSessionIds(thread.session.getId(), thread.threadId)]); } @@ -994,7 +995,7 @@ class StopAction extends Action { private readonly session: IDebugSession, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action codicon-debug-stop'); + super(`action.${STOP_ID}`, STOP_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStop)); } public run(): Promise { @@ -1008,7 +1009,7 @@ class DisconnectAction extends Action { private readonly session: IDebugSession, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${DISCONNECT_ID}`, DISCONNECT_LABEL, 'debug-action codicon-debug-disconnect'); + super(`action.${DISCONNECT_ID}`, DISCONNECT_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugDisconnect)); } public run(): Promise { @@ -1022,7 +1023,7 @@ class RestartAction extends Action { private readonly session: IDebugSession, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${RESTART_SESSION_ID}`, RESTART_LABEL, 'debug-action codicon-debug-restart'); + super(`action.${RESTART_SESSION_ID}`, RESTART_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugRestart)); } public run(): Promise { @@ -1036,7 +1037,7 @@ class StepOverAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STEP_OVER_ID}`, STEP_OVER_LABEL, 'debug-action codicon-debug-step-over', thread.stopped); + super(`action.${STEP_OVER_ID}`, STEP_OVER_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepOver), thread.stopped); } public run(): Promise { @@ -1050,7 +1051,7 @@ class StepIntoAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STEP_INTO_ID}`, STEP_INTO_LABEL, 'debug-action codicon-debug-step-into', thread.stopped); + super(`action.${STEP_INTO_ID}`, STEP_INTO_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepInto), thread.stopped); } public run(): Promise { @@ -1064,7 +1065,7 @@ class StepOutAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${STEP_OUT_ID}`, STEP_OUT_LABEL, 'debug-action codicon-debug-step-out', thread.stopped); + super(`action.${STEP_OUT_ID}`, STEP_OUT_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugStepOut), thread.stopped); } public run(): Promise { @@ -1078,7 +1079,7 @@ class PauseAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${PAUSE_ID}`, PAUSE_LABEL, 'debug-action codicon-debug-pause', !thread.stopped); + super(`action.${PAUSE_ID}`, PAUSE_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugPause), !thread.stopped); } public run(): Promise { @@ -1092,7 +1093,7 @@ class ContinueAction extends Action { private readonly thread: IThread, @ICommandService private readonly commandService: ICommandService ) { - super(`action.${CONTINUE_ID}`, CONTINUE_LABEL, 'debug-action codicon-debug-continue', thread.stopped); + super(`action.${CONTINUE_ID}`, CONTINUE_LABEL, 'debug-action ' + ThemeIcon.asClassName(icons.debugContinue), thread.stopped); } public run(): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index a26cea18..9438ac11 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -49,10 +49,10 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { StartDebugQuickAccessProvider } from 'vs/workbench/contrib/debug/browser/debugQuickAccess'; import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/debugProgress'; import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle'; -import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution'; import { FileAccess } from 'vs/base/common/network'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); const debugCategory = nls.localize('debugCategory', "Debug"); @@ -153,16 +153,16 @@ function registerCommandsAndActions(): void { }); }; - registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, { id: 'codicon/debug-continue' }, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, { id: 'codicon/debug-pause' }, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'), CONTEXT_DEBUG_STATE.isEqualTo('running')); - registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, { id: 'codicon/debug-stop' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); - registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, { id: 'codicon/debug-disconnect' }, CONTEXT_FOCUSED_SESSION_IS_ATTACH); - registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, { id: 'codicon/debug-step-over' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, { id: 'codicon/debug-step-into' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, { id: 'codicon/debug-step-out' }, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, { id: 'codicon/debug-restart' }); - registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, { id: 'codicon/debug-step-back' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); - registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, { id: 'codicon/debug-reverse-continue' }, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(CONTINUE_ID, CONTINUE_LABEL, 10, icons.debugContinue, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(PAUSE_ID, PAUSE_LABEL, 10, icons.debugPause, CONTEXT_DEBUG_STATE.notEqualsTo('stopped'), CONTEXT_DEBUG_STATE.isEqualTo('running')); + registerDebugToolBarItem(STOP_ID, STOP_LABEL, 70, icons.debugStop, CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated()); + registerDebugToolBarItem(DISCONNECT_ID, DISCONNECT_LABEL, 70, icons.debugDisconnect, CONTEXT_FOCUSED_SESSION_IS_ATTACH); + registerDebugToolBarItem(STEP_OVER_ID, STEP_OVER_LABEL, 20, icons.debugStepOver, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(STEP_INTO_ID, STEP_INTO_LABEL, 30, icons.debugStepInto, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(STEP_OUT_ID, STEP_OUT_LABEL, 40, icons.debugStepOut, undefined, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(RESTART_SESSION_ID, RESTART_LABEL, 60, icons.debugRestart); + registerDebugToolBarItem(STEP_BACK_ID, nls.localize('stepBackDebug', "Step Back"), 50, icons.debugStepBack, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); + registerDebugToolBarItem(REVERSE_CONTINUE_ID, nls.localize('reverseContinue', "Reverse"), 60, icons.debugReverseContinue, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); // Debug callstack context menu const registerDebugViewMenuItem = (menuId: MenuId, id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation') => { @@ -460,7 +460,7 @@ function registerDebugPanel(): void { const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: DEBUG_PANEL_ID, name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - icon: Codicon.debugConsole.classNames, + icon: icons.debugConsoleViewIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: DEBUG_PANEL_ID, focusCommand: { id: OpenDebugConsoleAction.ID }, @@ -471,7 +471,7 @@ function registerDebugPanel(): void { Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ id: REPL_VIEW_ID, name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), - containerIcon: Codicon.debugConsole.classNames, + containerIcon: icons.debugConsoleViewIcon, canToggleVisibility: false, canMoveView: true, when: CONTEXT_DEBUGGERS_AVAILABLE, @@ -481,12 +481,13 @@ function registerDebugPanel(): void { registry.registerWorkbenchAction(SyncActionDescriptor.from(OpenDebugConsoleAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }), 'View: Debug Console', CATEGORIES.View.value, CONTEXT_DEBUGGERS_AVAILABLE); } + function registerDebugView(): void { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: nls.localize('run', "Run"), ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer), - icon: Codicon.debugAlt.classNames, + icon: icons.runViewIcon, alwaysUseContainerInfo: true, order: 2 }, ViewContainerLocation.Sidebar); @@ -494,12 +495,12 @@ function registerDebugView(): void { // Register default debug views const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); - viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); - viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); - viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); - viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); - viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); - viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); + viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: icons.variablesViewIcon, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: icons.watchViewIcon, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: icons.callStackViewIcon, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer); + viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: icons.breakpointsViewIcon, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); + viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); + viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); } function registerConfiguration(): void { diff --git a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts index 47356271..d3cef4bb 100644 --- a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts +++ b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts @@ -7,13 +7,13 @@ import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { RGBA, Color } from 'vs/base/common/color'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; /** * @param text The content to stylize. * @returns An {@link HTMLSpanElement} that contains the potentially stylized text. */ -export function handleANSIOutput(text: string, linkDetector: LinkDetector, themeService: IThemeService, debugSession: IDebugSession): HTMLSpanElement { +export function handleANSIOutput(text: string, linkDetector: LinkDetector, themeService: IThemeService, workspaceFolder: IWorkspaceFolder | undefined): HTMLSpanElement { const root: HTMLSpanElement = document.createElement('span'); const textLength: number = text.length; @@ -54,7 +54,7 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector, theme if (sequenceFound) { // Flush buffer with previous styles. - appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, debugSession, customFgColor, customBgColor); + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor); buffer = ''; @@ -100,7 +100,7 @@ export function handleANSIOutput(text: string, linkDetector: LinkDetector, theme // Flush remaining text buffer if not empty. if (buffer) { - appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, debugSession, customFgColor, customBgColor); + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor); } return root; @@ -268,7 +268,7 @@ export function appendStylizedStringToContainer( stringContent: string, cssClasses: string[], linkDetector: LinkDetector, - debugSession: IDebugSession, + workspaceFolder: IWorkspaceFolder | undefined, customTextColor?: RGBA, customBackgroundColor?: RGBA ): void { @@ -276,7 +276,7 @@ export function appendStylizedStringToContainer( return; } - const container = linkDetector.linkify(stringContent, true, debugSession.root); + const container = linkDetector.linkify(stringContent, true, workspaceFolder); container.className = cssClasses.join(' '); if (customTextColor) { diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index f7fcea50..3ab90797 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -12,7 +12,7 @@ import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selec import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDebugService, IDebugSession, IDebugConfiguration, IConfig, ILaunch } from 'vs/workbench/contrib/debug/common/debug'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { selectBorder, selectBackground } from 'vs/platform/theme/common/colorRegistry'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -20,6 +20,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { debugStart } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -68,7 +69,7 @@ export class StartDebugActionViewItem implements IActionViewItem { render(container: HTMLElement): void { this.container = container; container.classList.add('start-debug-action-item'); - this.start = dom.append(container, $('.codicon.codicon-debug-start')); + this.start = dom.append(container, $(ThemeIcon.asCSSSelector(debugStart))); this.start.title = this.action.label; this.start.setAttribute('role', 'button'); this.start.tabIndex = 0; @@ -188,23 +189,35 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - if (this.options.length === 0) { - this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); - } else { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); - } - - this.providers.forEach(p => { - if (p.type === manager.selectedConfiguration.type) { + // Only take 3 elements from the recent dynamic configurations to not clutter the dropdown + manager.getRecentDynamicConfigurations().slice(0, 3).forEach(({ name, type }) => { + if (type === manager.selectedConfiguration.type && manager.selectedConfiguration.name === name) { this.selected = this.options.length; } + this.options.push({ + label: name, + handler: async () => { + await manager.selectConfiguration(undefined, name, undefined, { type }); + return true; + } + }); + }); + + if (this.options.length === 0) { + this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); + } + + this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); + disabledIdxs.push(this.options.length - 1); + + this.providers.forEach(p => { this.options.push({ - label: `${p.label}...`, handler: async () => { + label: `${p.label}...`, + handler: async () => { const picked = await p.pick(); if (picked) { - await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, p.type); + await manager.selectConfiguration(picked.launch, picked.config.name, picked.config, { type: p.type }); return true; } return false; @@ -212,11 +225,6 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - if (this.providers.length > 0) { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); - } - manager.getLaunches().filter(l => !l.hidden).forEach(l => { const label = inWorkspace ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); this.options.push({ diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index 3f3e09c9..3c947210 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -14,6 +14,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { deepClone } from 'vs/base/common/objects'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export abstract class AbstractDebugAction extends Action { @@ -64,7 +66,7 @@ export class ConfigureAction extends AbstractDebugAction { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IQuickInputService private readonly quickInputService: IQuickInputService ) { - super(id, label, 'debug-action codicon codicon-gear', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.debugConfigure), debugService, keybindingService); this._register(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass())); this.updateClass(); } @@ -79,7 +81,7 @@ export class ConfigureAction extends AbstractDebugAction { private updateClass(): void { const configurationManager = this.debugService.getConfigurationManager(); - this.class = configurationManager.selectedConfiguration.name ? 'debug-action codicon codicon-gear' : 'debug-action codicon codicon-gear notification'; + this.class = configurationManager.selectedConfiguration.name ? 'debug-action' + ThemeIcon.asClassName(icons.debugConfigure) : 'debug-action ' + ThemeIcon.asClassName(icons.debugConfigure) + ' notification'; } async run(): Promise { @@ -132,7 +134,8 @@ export class StartAction extends AbstractDebugAction { } async run(): Promise { - let { launch, name, config } = this.debugService.getConfigurationManager().selectedConfiguration; + let { launch, name, getConfig } = this.debugService.getConfigurationManager().selectedConfiguration; + const config = await getConfig(); const clonedConfig = deepClone(config); return this.debugService.startDebugging(launch, clonedConfig || name, { noDebug: this.isNoDebug() }); } @@ -147,10 +150,10 @@ export class StartAction extends AbstractDebugAction { if (debugService.state === State.Initializing) { return false; } - let { name, config } = debugService.getConfigurationManager().selectedConfiguration; - let nameToStart = name || config?.name; + let { name, launch } = debugService.getConfigurationManager().selectedConfiguration; + let nameToStart = name; - if (sessions.some(s => s.configuration.name === nameToStart)) { + if (sessions.some(s => s.configuration.name === nameToStart && s.root === launch?.workspace)) { // There is already a debug session running and we do not have any launch configuration selected return false; } @@ -209,7 +212,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { static readonly LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-close-all', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.breakpointsRemoveAll), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } @@ -267,7 +270,7 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction { static readonly DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-activate-breakpoints', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.breakpointsActivate), debugService, keybindingService); this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => { @@ -310,7 +313,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { static readonly LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-add', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsAddFuncBreakpoint), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } @@ -319,7 +322,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { } protected isEnabled(_: State): boolean { - return !this.debugService.getViewModel().getSelectedFunctionBreakpoint() + return !this.debugService.getViewModel().getSelectedBreakpoint() && this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name); } } @@ -329,7 +332,7 @@ export class AddWatchExpressionAction extends AbstractDebugAction { static readonly LABEL = nls.localize('addWatchExpression', "Add Expression"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-add', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsAdd), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); this._register(this.debugService.getViewModel().onDidSelectExpression(() => this.updateEnablement())); } @@ -349,7 +352,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction { static readonly LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions"); constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action codicon-close-all', debugService, keybindingService); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(icons.watchExpressionsRemoveAll), debugService, keybindingService); this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); } diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts new file mode 100644 index 00000000..249f1e69 --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import * as strings from 'vs/base/common/strings'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { ITextModel } from 'vs/editor/common/model'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IDebugConfiguration, IConfig, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, IDebugAdapterFactory, CONTEXT_DEBUGGERS_AVAILABLE, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug'; +import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export class AdapterManager implements IAdapterManager { + + private debuggers: Debugger[]; + private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[]; + private debugAdapterFactories = new Map(); + private debuggersAvailable: IContextKey; + private readonly _onDidRegisterDebugger = new Emitter(); + private readonly _onDidDebuggersExtPointRead = new Emitter(); + private breakpointModeIdsSet = new Set(); + + constructor( + @IEditorService private readonly editorService: IEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ICommandService private readonly commandService: ICommandService, + @IExtensionService private readonly extensionService: IExtensionService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this.adapterDescriptorFactories = []; + this.debuggers = []; + this.registerListeners(); + this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); + } + + private registerListeners(): void { + debuggersExtPoint.setHandler((extensions, delta) => { + delta.added.forEach(added => { + added.value.forEach(rawAdapter => { + if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) { + added.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'.")); + } + if (rawAdapter.enableBreakpointsFor && rawAdapter.enableBreakpointsFor.languageIds) { + rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => { + this.breakpointModeIdsSet.add(modeId); + }); + } + + if (rawAdapter.type !== '*') { + const existing = this.getDebugger(rawAdapter.type); + if (existing) { + existing.merge(rawAdapter, added.description); + } else { + this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, added.description)); + } + } + }); + }); + + // take care of all wildcard contributions + extensions.forEach(extension => { + extension.value.forEach(rawAdapter => { + if (rawAdapter.type === '*') { + this.debuggers.forEach(dbg => dbg.merge(rawAdapter, extension.description)); + } + }); + }); + + delta.removed.forEach(removed => { + const removedTypes = removed.value.map(rawAdapter => rawAdapter.type); + this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1); + }); + + // update the schema to include all attributes, snippets and types from extensions. + this.debuggers.forEach(adapter => { + const items = (launchSchema.properties!['configurations'].items); + const schemaAttributes = adapter.getSchemaAttributes(); + if (schemaAttributes && items.oneOf) { + items.oneOf.push(...schemaAttributes); + } + const configurationSnippets = adapter.configurationSnippets; + if (configurationSnippets && items.defaultSnippets) { + items.defaultSnippets.push(...configurationSnippets); + } + }); + + this._onDidDebuggersExtPointRead.fire(); + }); + + breakpointsExtPoint.setHandler((extensions, delta) => { + delta.removed.forEach(removed => { + removed.value.forEach(breakpoints => this.breakpointModeIdsSet.delete(breakpoints.language)); + }); + delta.added.forEach(added => { + added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language)); + }); + }); + } + + registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { + debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); + this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); + this._onDidRegisterDebugger.fire(); + + return { + dispose: () => { + debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType)); + } + }; + } + + hasDebuggers(): boolean { + return this.debugAdapterFactories.size > 0; + } + + createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined { + let factory = this.debugAdapterFactories.get(session.configuration.type); + if (factory) { + return factory.createDebugAdapter(session); + } + return undefined; + } + + substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise { + let factory = this.debugAdapterFactories.get(debugType); + if (factory) { + return factory.substituteVariables(folder, config); + } + return Promise.resolve(config); + } + + runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise { + let factory = this.debugAdapterFactories.get(debugType); + if (factory) { + return factory.runInTerminal(args); + } + return Promise.resolve(void 0); + } + + registerDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): IDisposable { + this.adapterDescriptorFactories.push(debugAdapterProvider); + return { + dispose: () => { + this.unregisterDebugAdapterDescriptorFactory(debugAdapterProvider); + } + }; + } + + unregisterDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): void { + const ix = this.adapterDescriptorFactories.indexOf(debugAdapterProvider); + if (ix >= 0) { + this.adapterDescriptorFactories.splice(ix, 1); + } + } + + getDebugAdapterDescriptor(session: IDebugSession): Promise { + const config = session.configuration; + const providers = this.adapterDescriptorFactories.filter(p => p.type === config.type && p.createDebugAdapterDescriptor); + if (providers.length === 1) { + return providers[0].createDebugAdapterDescriptor(session); + } else { + // TODO@AW handle n > 1 case + } + return Promise.resolve(undefined); + } + + getDebuggerLabel(type: string): string | undefined { + const dbgr = this.getDebugger(type); + if (dbgr) { + return dbgr.label; + } + + return undefined; + } + + get onDidRegisterDebugger(): Event { + return this._onDidRegisterDebugger.event; + } + + get onDidDebuggersExtPointRead(): Event { + return this._onDidDebuggersExtPointRead.event; + } + + canSetBreakpointsIn(model: ITextModel): boolean { + const modeId = model.getLanguageIdentifier().language; + if (!modeId || modeId === 'jsonc' || modeId === 'log') { + // do not allow breakpoints in our settings files and output + return false; + } + if (this.configurationService.getValue('debug').allowBreakpointsEverywhere) { + return true; + } + + return this.breakpointModeIdsSet.has(modeId); + } + + getDebugger(type: string): Debugger | undefined { + return this.debuggers.find(dbg => strings.equalsIgnoreCase(dbg.type, type)); + } + + isDebuggerInterestedInLanguage(language: string): boolean { + return !!this.debuggers.find(a => language && a.languages && a.languages.indexOf(language) >= 0); + } + + async guessDebugger(type?: string): Promise { + if (type) { + const adapter = this.getDebugger(type); + return Promise.resolve(adapter); + } + + const activeTextEditorControl = this.editorService.activeTextEditorControl; + let candidates: Debugger[] | undefined; + if (isCodeEditor(activeTextEditorControl)) { + const model = activeTextEditorControl.getModel(); + const language = model ? model.getLanguageIdentifier().language : undefined; + const adapters = this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0); + if (adapters.length === 1) { + return adapters[0]; + } + if (adapters.length > 1) { + candidates = adapters; + } + } + + if (!candidates) { + await this.activateDebuggers('onDebugInitialConfigurations'); + candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider()); + } + + candidates.sort((first, second) => first.label.localeCompare(second.label)); + const picks = candidates.map(c => ({ label: c.label, debugger: c })); + return this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: nls.localize('more', "More..."), debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") }) + .then(picked => { + if (picked && picked.debugger) { + return picked.debugger; + } + if (picked) { + this.commandService.executeCommand('debug.installAdditionalDebuggers'); + } + return undefined; + }); + } + + async activateDebuggers(activationEvent: string, debugType?: string): Promise { + const promises: Promise[] = [ + this.extensionService.activateByEvent(activationEvent), + this.extensionService.activateByEvent('onDebug') + ]; + if (debugType) { + promises.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`)); + } + await Promise.all(promises); + } +} diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 098a3eeb..47aaedfb 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -113,9 +113,9 @@ function isSessionContext(obj: any): obj is CallStackContext { export function registerCommands(): void { - // These commands are used in call stack context menu, call stack inline actions, command pallete, debug toolbar, mac native touch bar + // These commands are used in call stack context menu, call stack inline actions, command palette, debug toolbar, mac native touch bar // When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id - // Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command pallete) we do not pass any id and just take whatever is the focussed thread + // Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command palette) we do not pass any id and just take whatever is the focussed thread // Same for stackFrame commands and session commands. CommandsRegistry.registerCommand({ id: COPY_STACK_TRACE_ID, @@ -521,7 +521,7 @@ export function registerCommands(): void { const control = editorService.activeTextEditorControl; if (isCodeEditor(control)) { const position = control.getPosition(); - if (position && control.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(control.getModel())) { + if (position && control.hasModel() && debugService.canSetBreakpointsIn(control.getModel())) { const modelUri = control.getModel().uri; const breakpointAlreadySet = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri }) .some(bp => (bp.sessionAgnosticData.column === position.column || (!bp.column && position.column <= 1))); @@ -564,7 +564,7 @@ export function registerCommands(): void { if (list instanceof List) { const focus = list.getFocusedElements(); if (focus.length && focus[0] instanceof Breakpoint) { - return openBreakpointSource(focus[0], true, false, accessor.get(IDebugService), accessor.get(IEditorService)); + return openBreakpointSource(focus[0], true, false, true, accessor.get(IDebugService), accessor.get(IEditorService)); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 041c97e2..1357826c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -6,30 +6,25 @@ import * as nls from 'vs/nls'; import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import * as json from 'vs/base/common/json'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { ITextModel } from 'vs/editor/common/model'; import { IEditorPane } from 'vs/workbench/common/editor'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IConfigPresentation, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; -import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; +import { IDebugConfigurationProvider, ICompound, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, CONTEXT_DEBUG_CONFIGURATION_TYPE, IConfigPresentation } from 'vs/workbench/contrib/debug/common/debug'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; -import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas'; +import { launchSchema } from 'vs/workbench/contrib/debug/common/debugSchemas'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -37,10 +32,13 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { withUndefinedAsNull } from 'vs/base/common/types'; import { sequence } from 'vs/base/common/async'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { flatten } from 'vs/base/common/arrays'; +import { flatten, distinct } from 'vs/base/common/arrays'; import { getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager'; +import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -49,33 +47,27 @@ const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; const DEBUG_SELECTED_ROOT = 'debug.selectedroot'; // Debug type is only stored if a dynamic configuration is used for better restore const DEBUG_SELECTED_TYPE = 'debug.selectedtype'; +const DEBUG_RECENT_DYNAMIC_CONFIGURATIONS = 'debug.recentdynamicconfigurations'; interface IDynamicPickItem { label: string, launch: ILaunch, config: IConfig } export class ConfigurationManager implements IConfigurationManager { - private debuggers: Debugger[]; - private breakpointModeIdsSet = new Set(); private launches!: ILaunch[]; private selectedName: string | undefined; private selectedLaunch: ILaunch | undefined; - private selectedConfig: IConfig | undefined; + private getSelectedConfig: () => Promise = () => Promise.resolve(undefined); private selectedType: string | undefined; private toDispose: IDisposable[]; private readonly _onDidSelectConfigurationName = new Emitter(); private configProviders: IDebugConfigurationProvider[]; - private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[]; - private debugAdapterFactories = new Map(); private debugConfigurationTypeContext: IContextKey; - private debuggersAvailable: IContextKey; - private readonly _onDidRegisterDebugger = new Emitter(); constructor( + private readonly adapterManager: AdapterManager, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ICommandService private readonly commandService: ICommandService, @IStorageService private readonly storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IHistoryService private readonly historyService: IHistoryService, @@ -83,8 +75,6 @@ export class ConfigurationManager implements IConfigurationManager { @IContextKeyService contextKeyService: IContextKeyService ) { this.configProviders = []; - this.adapterDescriptorFactories = []; - this.debuggers = []; this.toDispose = []; this.initLaunches(); this.registerListeners(); @@ -93,111 +83,13 @@ export class ConfigurationManager implements IConfigurationManager { const previousSelectedLaunch = this.launches.find(l => l.uri.toString() === previousSelectedRoot); const previousSelectedName = this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); - this.debuggersAvailable = CONTEXT_DEBUGGERS_AVAILABLE.bindTo(contextKeyService); if (previousSelectedLaunch && previousSelectedLaunch.getConfigurationNames().length) { - this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, previousSelectedType); + this.selectConfiguration(previousSelectedLaunch, previousSelectedName, undefined, { type: previousSelectedType }); } else if (this.launches.length > 0) { - this.selectConfiguration(undefined, previousSelectedName, undefined, previousSelectedType); + this.selectConfiguration(undefined, previousSelectedName, undefined, { type: previousSelectedType }); } } - // debuggers - - registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { - debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); - this.debuggersAvailable.set(this.debugAdapterFactories.size > 0); - this._onDidRegisterDebugger.fire(); - - return { - dispose: () => { - debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType)); - } - }; - } - - hasDebuggers(): boolean { - return this.debugAdapterFactories.size > 0; - } - - createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined { - let factory = this.debugAdapterFactories.get(session.configuration.type); - if (factory) { - return factory.createDebugAdapter(session); - } - return undefined; - } - - substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise { - let factory = this.debugAdapterFactories.get(debugType); - if (factory) { - return factory.substituteVariables(folder, config); - } - return Promise.resolve(config); - } - - runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise { - let factory = this.debugAdapterFactories.get(debugType); - if (factory) { - return factory.runInTerminal(args); - } - return Promise.resolve(void 0); - } - - // debug adapter - - registerDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): IDisposable { - this.adapterDescriptorFactories.push(debugAdapterProvider); - return { - dispose: () => { - this.unregisterDebugAdapterDescriptorFactory(debugAdapterProvider); - } - }; - } - - unregisterDebugAdapterDescriptorFactory(debugAdapterProvider: IDebugAdapterDescriptorFactory): void { - const ix = this.adapterDescriptorFactories.indexOf(debugAdapterProvider); - if (ix >= 0) { - this.adapterDescriptorFactories.splice(ix, 1); - } - } - - getDebugAdapterDescriptor(session: IDebugSession): Promise { - - const config = session.configuration; - - // first try legacy proposed API: DebugConfigurationProvider.debugAdapterExecutable - const providers0 = this.configProviders.filter(p => p.type === config.type && p.debugAdapterExecutable); - if (providers0.length === 1 && providers0[0].debugAdapterExecutable) { - return providers0[0].debugAdapterExecutable(session.root ? session.root.uri : undefined); - } else { - // TODO@AW handle n > 1 case - } - - // new API - const providers = this.adapterDescriptorFactories.filter(p => p.type === config.type && p.createDebugAdapterDescriptor); - if (providers.length === 1) { - return providers[0].createDebugAdapterDescriptor(session); - } else { - // TODO@AW handle n > 1 case - } - return Promise.resolve(undefined); - } - - getDebuggerLabel(type: string): string | undefined { - const dbgr = this.getDebugger(type); - if (dbgr) { - return dbgr.label; - } - - return undefined; - } - - get onDidRegisterDebugger(): Event { - return this._onDidRegisterDebugger.event; - } - - // debug configurations - registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable { this.configProviders.push(debugConfigurationProvider); return { @@ -298,7 +190,7 @@ export class ConfigurationManager implements IConfigurationManager { return debugDynamicExtensionsTypes.map(type => { return { - label: this.getDebuggerLabel(type)!, + label: this.adapterManager.getDebuggerLabel(type)!, getProvider: async () => { await this.activateDebuggers(onDebugDynamicConfigurationsName, type); return this.configProviders.find(p => p.type === type && p.triggerKind === DebugConfigurationProviderTriggerKind.Dynamic && p.provideDebugConfigurations); @@ -313,7 +205,6 @@ export class ConfigurationManager implements IConfigurationManager { input.placeholder = nls.localize('selectConfiguration', "Select Launch Configuration"); input.show(); - let chosenDidCancel = false; const chosenPromise = new Promise(resolve => { disposables.add(input.onDidAccept(() => resolve(input.activeItems[0]))); disposables.add(input.onDidTriggerItemButton(async (context) => { @@ -324,7 +215,6 @@ export class ConfigurationManager implements IConfigurationManager { await (launch as Launch).writeConfiguration(config); await this.selectConfiguration(launch, config.name); })); - disposables.add(input.onDidHide(() => { chosenDidCancel = true; resolve(undefined); })); }); const token = new CancellationTokenSource(); @@ -337,7 +227,7 @@ export class ConfigurationManager implements IConfigurationManager { description: launch.name, config, buttons: [{ - iconClass: 'codicon-gear', + iconClass: ThemeIcon.asClassName(debugConfigure), tooltip: nls.localize('editLaunchConfig', "Edit Debug Configuration in launch.json") }], launch @@ -348,16 +238,9 @@ export class ConfigurationManager implements IConfigurationManager { const nestedPicks = await Promise.all(picks); const items = flatten(nestedPicks); - let chosen: IDynamicPickItem | undefined; - - // If there's exactly one item to choose from, pick it automatically - if (items.length === 1 && !chosenDidCancel) { - chosen = items[0]; - } else { - input.items = items; - input.busy = false; - chosen = await chosenPromise; - } + input.items = items; + input.busy = false; + const chosen = await chosenPromise; disposables.dispose(); @@ -387,69 +270,11 @@ export class ConfigurationManager implements IConfigurationManager { return getVisibleAndSorted(all); } + getRecentDynamicConfigurations(): { name: string, type: string }[] { + return JSON.parse(this.storageService.get(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, StorageScope.WORKSPACE, '[]')); + } + private registerListeners(): void { - debuggersExtPoint.setHandler((extensions, delta) => { - delta.added.forEach(added => { - added.value.forEach(rawAdapter => { - if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) { - added.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'.")); - } - if (rawAdapter.enableBreakpointsFor) { - rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => { - this.breakpointModeIdsSet.add(modeId); - }); - } - - if (rawAdapter.type !== '*') { - const existing = this.getDebugger(rawAdapter.type); - if (existing) { - existing.merge(rawAdapter, added.description); - } else { - this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, added.description)); - } - } - }); - }); - - // take care of all wildcard contributions - extensions.forEach(extension => { - extension.value.forEach(rawAdapter => { - if (rawAdapter.type === '*') { - this.debuggers.forEach(dbg => dbg.merge(rawAdapter, extension.description)); - } - }); - }); - - delta.removed.forEach(removed => { - const removedTypes = removed.value.map(rawAdapter => rawAdapter.type); - this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1); - }); - - // update the schema to include all attributes, snippets and types from extensions. - this.debuggers.forEach(adapter => { - const items = (launchSchema.properties!['configurations'].items); - const schemaAttributes = adapter.getSchemaAttributes(); - if (schemaAttributes && items.oneOf) { - items.oneOf.push(...schemaAttributes); - } - const configurationSnippets = adapter.configurationSnippets; - if (configurationSnippets && items.defaultSnippets) { - items.defaultSnippets.push(...configurationSnippets); - } - }); - - this.setCompoundSchemaValues(); - }); - - breakpointsExtPoint.setHandler((extensions, delta) => { - delta.removed.forEach(removed => { - removed.value.forEach(breakpoints => this.breakpointModeIdsSet.delete(breakpoints.language)); - }); - delta.added.forEach(added => { - added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language)); - }); - }); - this.toDispose.push(Event.any(this.contextService.onDidChangeWorkspaceFolders, this.contextService.onDidChangeWorkbenchState)(() => { this.initLaunches(); this.selectConfiguration(undefined); @@ -462,14 +287,17 @@ export class ConfigurationManager implements IConfigurationManager { this.setCompoundSchemaValues(); } })); + this.toDispose.push(this.adapterManager.onDidDebuggersExtPointRead(() => { + this.setCompoundSchemaValues(); + })); } private initLaunches(): void { - this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder)); + this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, this.adapterManager, folder)); if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this)); + this.launches.push(this.instantiationService.createInstance(WorkspaceLaunch, this, this.adapterManager)); } - this.launches.push(this.instantiationService.createInstance(UserLaunch, this)); + this.launches.push(this.instantiationService.createInstance(UserLaunch, this, this.adapterManager)); if (this.selectedLaunch && this.launches.indexOf(this.selectedLaunch) === -1) { this.selectConfiguration(undefined); @@ -501,11 +329,11 @@ export class ConfigurationManager implements IConfigurationManager { return this.launches.find(l => l.workspace && this.uriIdentityService.extUri.isEqual(l.workspace.uri, workspaceUri)); } - get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, config: IConfig | undefined, type: string | undefined } { + get selectedConfiguration(): { launch: ILaunch | undefined, name: string | undefined, getConfig: () => Promise, type: string | undefined } { return { launch: this.selectedLaunch, name: this.selectedName, - config: this.selectedConfig, + getConfig: this.getSelectedConfig, type: this.selectedType }; } @@ -522,7 +350,7 @@ export class ConfigurationManager implements IConfigurationManager { return undefined; } - async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise { + async selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfig?: { type?: string }): Promise { if (typeof launch === 'undefined') { const rootUri = this.historyService.getLastActiveWorkspaceRoot(); launch = this.getLaunch(rootUri); @@ -536,40 +364,56 @@ export class ConfigurationManager implements IConfigurationManager { this.selectedLaunch = launch; if (this.selectedLaunch) { - this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_SELECTED_ROOT, this.selectedLaunch.uri.toString(), StorageScope.WORKSPACE, StorageTarget.MACHINE); } else { this.storageService.remove(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); } const names = launch ? launch.getConfigurationNames() : []; - if ((name && names.indexOf(name) >= 0) || config) { + this.getSelectedConfig = () => Promise.resolve(config); + let type = config?.type; + if (name && names.indexOf(name) >= 0) { this.setSelectedLaunchName(name); - } else if (!this.selectedName || names.indexOf(this.selectedName) === -1) { - // We could not find the previously used name. We should get all dynamic configurations from providers + } else if (dynamicConfig && dynamicConfig.type) { + // We could not find the previously used name and config is not passed. We should get all dynamic configurations from providers // And potentially auto select the previously used dynamic configuration #96293 - const providers = (await this.getDynamicProviders()).filter(p => p.type === type); - const activatedProviders = await Promise.all(providers.map(p => p.getProvider())); - const provider = activatedProviders.find(p => p && p.type === type); - let nameToSet = names.length ? names[0] : undefined; - if (provider && launch && launch.workspace) { - const token = new CancellationTokenSource(); - const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token); - const dynamicConfig = dynamicConfigs.find(c => c.name === name); - if (dynamicConfig) { - config = dynamicConfig; - nameToSet = name; - } - } + type = dynamicConfig.type; + if (!config) { + const providers = (await this.getDynamicProviders()).filter(p => p.type === type); + this.getSelectedConfig = async () => { + const activatedProviders = await Promise.all(providers.map(p => p.getProvider())); + const provider = activatedProviders.find(p => p && p.type === type); + if (provider && launch && launch.workspace) { + const token = new CancellationTokenSource(); + const dynamicConfigs = await provider.provideDebugConfigurations!(launch.workspace.uri, token.token); + const dynamicConfig = dynamicConfigs.find(c => c.name === name); + if (dynamicConfig) { + return dynamicConfig; + } + } + return undefined; + }; + } + this.setSelectedLaunchName(name); + + let recentDynamicProviders = this.getRecentDynamicConfigurations(); + if (name && dynamicConfig.type) { + // We need to store the recently used dynamic configurations to be able to show them in UI #110009 + recentDynamicProviders.unshift({ name, type: dynamicConfig.type }); + recentDynamicProviders = distinct(recentDynamicProviders, t => `${t.name} : ${t.type}`); + this.storageService.store(DEBUG_RECENT_DYNAMIC_CONFIGURATIONS, JSON.stringify(recentDynamicProviders), StorageScope.WORKSPACE, StorageTarget.USER); + } + } else if (!this.selectedName || names.indexOf(this.selectedName) === -1) { + // We could not find the configuration to select, pick the first one, or reset the selection if there is no launch configuration + const nameToSet = names.length ? names[0] : undefined; this.setSelectedLaunchName(nameToSet); } - this.selectedConfig = config; - this.selectedType = type || this.selectedConfig?.type; - this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE); - const configForType = this.selectedConfig || (this.selectedLaunch && this.selectedName ? this.selectedLaunch.getConfiguration(this.selectedName) : undefined); - if (configForType) { - this.debugConfigurationTypeContext.set(configForType.type); + this.selectedType = dynamicConfig?.type || config?.type; + this.storageService.store(DEBUG_SELECTED_TYPE, this.selectedType, StorageScope.WORKSPACE, StorageTarget.MACHINE); + if (type) { + this.debugConfigurationTypeContext.set(type); } else { this.debugConfigurationTypeContext.reset(); } @@ -579,66 +423,6 @@ export class ConfigurationManager implements IConfigurationManager { } } - canSetBreakpointsIn(model: ITextModel): boolean { - const modeId = model.getLanguageIdentifier().language; - if (!modeId || modeId === 'jsonc' || modeId === 'log') { - // do not allow breakpoints in our settings files and output - return false; - } - if (this.configurationService.getValue('debug').allowBreakpointsEverywhere) { - return true; - } - - return this.breakpointModeIdsSet.has(modeId); - } - - getDebugger(type: string): Debugger | undefined { - return this.debuggers.find(dbg => strings.equalsIgnoreCase(dbg.type, type)); - } - - isDebuggerInterestedInLanguage(language: string): boolean { - return !!this.debuggers.find(a => language && a.languages && a.languages.indexOf(language) >= 0); - } - - async guessDebugger(type?: string): Promise { - if (type) { - const adapter = this.getDebugger(type); - return Promise.resolve(adapter); - } - - const activeTextEditorControl = this.editorService.activeTextEditorControl; - let candidates: Debugger[] | undefined; - if (isCodeEditor(activeTextEditorControl)) { - const model = activeTextEditorControl.getModel(); - const language = model ? model.getLanguageIdentifier().language : undefined; - const adapters = this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0); - if (adapters.length === 1) { - return adapters[0]; - } - if (adapters.length > 1) { - candidates = adapters; - } - } - - if (!candidates) { - await this.activateDebuggers('onDebugInitialConfigurations'); - candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider()); - } - - candidates.sort((first, second) => first.label.localeCompare(second.label)); - const picks = candidates.map(c => ({ label: c.label, debugger: c })); - return this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: nls.localize('more', "More..."), debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") }) - .then(picked => { - if (picked && picked.debugger) { - return picked.debugger; - } - if (picked) { - this.commandService.executeCommand('debug.installAdditionalDebuggers'); - } - return undefined; - }); - } - async activateDebuggers(activationEvent: string, debugType?: string): Promise { const promises: Promise[] = [ this.extensionService.activateByEvent(activationEvent), @@ -654,7 +438,7 @@ export class ConfigurationManager implements IConfigurationManager { this.selectedName = selectedName; if (this.selectedName) { - this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE); + this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE, StorageTarget.MACHINE); } else { this.storageService.remove(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE); } @@ -668,8 +452,10 @@ export class ConfigurationManager implements IConfigurationManager { abstract class AbstractLaunch { protected abstract getConfig(): IGlobalConfig | undefined; - constructor(protected configurationManager: ConfigurationManager) { - } + constructor( + protected configurationManager: ConfigurationManager, + private readonly adapterManager: AdapterManager + ) { } getCompound(name: string): ICompound | undefined { const config = this.getConfig(); @@ -722,7 +508,7 @@ abstract class AbstractLaunch { async getInitialConfigurationContent(folderUri?: uri, type?: string, token?: CancellationToken): Promise { let content = ''; - const adapter = await this.configurationManager.guessDebugger(type); + const adapter = await this.adapterManager.guessDebugger(type); if (adapter) { const initialConfigs = await this.configurationManager.provideDebugConfigurations(folderUri, adapter.type, token || CancellationToken.None); content = await adapter.getInitialConfigurationContent(initialConfigs); @@ -739,13 +525,14 @@ class Launch extends AbstractLaunch implements ILaunch { constructor( configurationManager: ConfigurationManager, + adapterManager: AdapterManager, public workspace: IWorkspaceFolder, @IFileService private readonly fileService: IFileService, @ITextFileService private readonly textFileService: ITextFileService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService ) { - super(configurationManager); + super(configurationManager, adapterManager); } get uri(): uri { @@ -822,11 +609,12 @@ class Launch extends AbstractLaunch implements ILaunch { class WorkspaceLaunch extends AbstractLaunch implements ILaunch { constructor( configurationManager: ConfigurationManager, + adapterManager: AdapterManager, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService ) { - super(configurationManager); + super(configurationManager, adapterManager); } get workspace(): undefined { @@ -873,10 +661,11 @@ class UserLaunch extends AbstractLaunch implements ILaunch { constructor( configurationManager: ConfigurationManager, + adapterManager: AdapterManager, @IConfigurationService private readonly configurationService: IConfigurationService, @IPreferencesService private readonly preferencesService: IPreferencesService ) { - super(configurationManager); + super(configurationManager, adapterManager); } get workspace(): undefined { @@ -900,7 +689,7 @@ class UserLaunch extends AbstractLaunch implements ILaunch { } async openConfigFile(preserveFocus: boolean): Promise<{ editor: IEditorPane | null, created: boolean }> { - const editor = await this.preferencesService.openGlobalSettings(true, { preserveFocus }); + const editor = await this.preferencesService.openGlobalSettings(true, { preserveFocus, revealSetting: { key: 'launch' } }); return ({ editor: withUndefinedAsNull(editor), created: false diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 1d5b88bb..d8ba1d55 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, REPL_VIEW_ID, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, WATCH_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_EXCEPTION_WIDGET_VISIBLE } from 'vs/workbench/contrib/debug/common/debug'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/contrib/debug/browser/breakpointsView'; @@ -20,6 +20,10 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { Action } from 'vs/base/common/actions'; import { getDomNodePagePosition } from 'vs/base/browser/dom'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { Position } from 'vs/editor/common/core/position'; +import { URI } from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { raceTimeout } from 'vs/base/common/async'; export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { @@ -41,7 +45,7 @@ class ToggleBreakpointAction extends EditorAction { if (editor.hasModel()) { const debugService = accessor.get(IDebugService); const modelUri = editor.getModel().uri; - const canSet = debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel()); + const canSet = debugService.canSetBreakpointsIn(editor.getModel()); // Does not account for multi line selections, Set to remove multiple cursor on the same line const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))]; @@ -75,7 +79,7 @@ class ConditionalBreakpointAction extends EditorAction { const debugService = accessor.get(IDebugService); const position = editor.getPosition(); - if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) { editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined, BreakpointWidgetContext.CONDITION); } } @@ -97,7 +101,7 @@ class LogPointAction extends EditorAction { const debugService = accessor.get(IDebugService); const position = editor.getPosition(); - if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) { editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE); } } @@ -123,14 +127,37 @@ export class RunToCursorAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); - const uriIdentityService = accessor.get(IUriIdentityService); const focusedSession = debugService.getViewModel().focusedSession; if (debugService.state !== State.Stopped || !focusedSession) { return; } - let breakpointToRemove: IBreakpoint; - const oneTimeListener = focusedSession.onDidChangeState(() => { + const position = editor.getPosition(); + if (!(editor.hasModel() && position)) { + return; + } + + const uri = editor.getModel().uri; + const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length); + + let breakpointToRemove: IBreakpoint | undefined; + let threadToContinue = debugService.getViewModel().focusedThread; + if (!bpExists) { + const addResult = await this.addBreakpoints(accessor, uri, position); + if (addResult.thread) { + threadToContinue = addResult.thread; + } + + if (addResult.breakpoint) { + breakpointToRemove = addResult.breakpoint; + } + } + + if (!threadToContinue) { + return; + } + + const oneTimeListener = threadToContinue.session.onDidChangeState(() => { const state = focusedSession.state; if (state === State.Stopped || state === State.Inactive) { if (breakpointToRemove) { @@ -140,27 +167,90 @@ export class RunToCursorAction extends EditorAction { } }); - const position = editor.getPosition(); - if (editor.hasModel() && position) { - const uri = editor.getModel().uri; - const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length); - if (!bpExists) { - let column = 0; - const focusedStackFrame = debugService.getViewModel().focusedStackFrame; - if (focusedStackFrame && uriIdentityService.extUri.isEqual(focusedStackFrame.source.uri, uri) && focusedStackFrame.range.startLineNumber === position.lineNumber) { - // If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line - // otherwise set it at the precise column #102199 - column = position.column; - } + await threadToContinue.continue(); + } - const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column }], false); - if (breakpoints && breakpoints.length) { - breakpointToRemove = breakpoints[0]; + private async addBreakpoints(accessor: ServicesAccessor, uri: URI, position: Position) { + const debugService = accessor.get(IDebugService); + const debugModel = debugService.getModel(); + const viewModel = debugService.getViewModel(); + const uriIdentityService = accessor.get(IUriIdentityService); + + let column = 0; + const focusedStackFrame = viewModel.focusedStackFrame; + if (focusedStackFrame && uriIdentityService.extUri.isEqual(focusedStackFrame.source.uri, uri) && focusedStackFrame.range.startLineNumber === position.lineNumber) { + // If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint at column 0 on the line + // otherwise set it at the precise column #102199 + column = position.column; + } + + const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column }], false); + const breakpoint = breakpoints?.[0]; + if (!breakpoint) { + return { breakpoint: undefined, thread: viewModel.focusedThread }; + } + + // If the breakpoint was not initially verified, wait up to 2s for it to become so. + // Inherently racey if multiple sessions can verify async, but not solvable... + if (!breakpoint.verified) { + let listener: IDisposable; + await raceTimeout(new Promise(resolve => { + listener = debugModel.onDidChangeBreakpoints(() => { + if (breakpoint.verified) { + resolve(); + } + }); + }), 2000); + listener!.dispose(); + } + + // Look at paused threads for sessions that verified this bp. Prefer, in order: + const enum Score { + /** The focused thread */ + Focused, + /** Any other stopped thread of a session that verified the bp */ + Verified, + /** Any thread that verified and paused in the same file */ + VerifiedAndPausedInFile, + /** The focused thread if it verified the breakpoint */ + VerifiedAndFocused, + } + + let bestThread = viewModel.focusedThread; + let bestScore = Score.Focused; + for (const sessionId of breakpoint.sessionsThatVerified) { + const session = debugModel.getSession(sessionId); + if (!session) { + continue; + } + + const threads = session.getAllThreads().filter(t => t.stopped); + if (bestScore < Score.VerifiedAndFocused) { + if (viewModel.focusedThread && threads.includes(viewModel.focusedThread)) { + bestThread = viewModel.focusedThread; + bestScore = Score.VerifiedAndFocused; } } - await debugService.getViewModel().focusedThread!.continue(); + if (bestScore < Score.VerifiedAndPausedInFile) { + const pausedInThisFile = threads.find(t => { + const top = t.getTopStackFrame(); + return top && uriIdentityService.extUri.isEqual(top.source.uri, uri); + }); + + if (pausedInThisFile) { + bestThread = pausedInThisFile; + bestScore = Score.VerifiedAndPausedInFile; + } + } + + if (bestScore < Score.Verified) { + bestThread = threads[0]; + bestScore = Score.VerifiedAndPausedInFile; + } } + + return { thread: bestThread, breakpoint }; } } @@ -336,7 +426,7 @@ class GoToBreakpointAction extends EditorAction { } if (moveBreakpoint) { - return openBreakpointSource(moveBreakpoint, false, true, debugService, editorService); + return openBreakpointSource(moveBreakpoint, false, true, false, debugService, editorService); } } } @@ -364,6 +454,27 @@ class GoToPreviousBreakpointAction extends GoToBreakpointAction { } } +class CloseExceptionWidgetAction extends EditorAction { + + constructor() { + super({ + id: 'editor.debug.action.closeExceptionWidget', + label: nls.localize('closeExceptionWidget', "Close Exception Widget"), + alias: 'Close Exception Widget', + precondition: CONTEXT_EXCEPTION_WIDGET_VISIBLE, + kbOpts: { + primary: KeyCode.Escape, + weight: KeybindingWeight.EditorContrib + } + }); + } + + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const contribution = editor.getContribution(EDITOR_CONTRIBUTION_ID); + contribution.closeExceptionWidget(); + } +} + export function registerEditorActions(): void { registerEditorAction(ToggleBreakpointAction); registerEditorAction(ConditionalBreakpointAction); @@ -375,4 +486,5 @@ export function registerEditorActions(): void { registerEditorAction(ShowDebugHoverAction); registerEditorAction(GoToNextBreakpointAction); registerEditorAction(GoToPreviousBreakpointAction); + registerEditorAction(CloseExceptionWidgetAction); } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 4e39c57d..dbd53aa5 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -21,7 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugEditorContribution, IDebugService, State, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugEditorContribution, IDebugService, State, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, IDebugSession, CONTEXT_EXCEPTION_WIDGET_VISIBLE } from 'vs/workbench/contrib/debug/common/debug'; import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget'; import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; import { Position } from 'vs/editor/common/core/position'; @@ -39,6 +39,7 @@ import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Event } from 'vs/base/common/event'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; const LAUNCH_JSON_REGEX = /\.vscode\/launch\.json$/; const INLINE_VALUE_DECORATION_KEY = 'inlinevaluedecoration'; @@ -173,6 +174,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { private hoverWidget: DebugHoverWidget; private hoverRange: Range | null = null; private mouseDown = false; + private exceptionWidgetVisible: IContextKey; private static readonly MEMOIZER = createMemoizer(); private exceptionWidget: ExceptionWidget | undefined; @@ -189,13 +191,15 @@ export class DebugEditorContribution implements IDebugEditorContribution { @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, @IHostService private readonly hostService: IHostService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IContextKeyService contextKeyService: IContextKeyService ) { this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor); this.toDispose = []; this.registerListeners(); this.updateConfigurationWidgetVisibility(); this.codeEditorService.registerDecorationType(INLINE_VALUE_DECORATION_KEY, {}); + this.exceptionWidgetVisible = CONTEXT_EXCEPTION_WIDGET_VISIBLE.bindTo(contextKeyService); this.toggleExceptionWidget(); } @@ -355,7 +359,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { } private hideHoverWidget(): void { - if (!this.hideHoverScheduler.isScheduled() && this.hoverWidget.isVisible()) { + if (!this.hideHoverScheduler.isScheduled() && this.hoverWidget.willBeVisible()) { this.hideHoverScheduler.schedule(); } this.showHoverScheduler.cancel(); @@ -441,13 +445,20 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, exceptionInfo, debugSession); this.exceptionWidget.show({ lineNumber, column }, 0); + this.exceptionWidget.focus(); this.editor.revealLine(lineNumber); + this.exceptionWidgetVisible.set(true); } - private closeExceptionWidget(): void { + closeExceptionWidget(): void { if (this.exceptionWidget) { + const shouldFocusEditor = this.exceptionWidget.hasfocus(); this.exceptionWidget.dispose(); this.exceptionWidget = undefined; + this.exceptionWidgetVisible.set(false); + if (shouldFocusEditor) { + this.editor.focus(); + } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 4e877402..d828f5bf 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -31,7 +31,8 @@ import { coalesce } from 'vs/base/common/arrays'; import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; import { EvaluatableExpressionProviderRegistry } from 'vs/editor/common/modes'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { isMacintosh } from 'vs/base/common/platform'; const $ = dom.$; @@ -70,6 +71,7 @@ export class DebugHoverWidget implements IContentWidget { allowEditorOverflow = true; private _isVisible: boolean; + private showCancellationSource?: CancellationTokenSource; private domNode!: HTMLElement; private tree!: AsyncDataTree; private showAtPosition: Position | null; @@ -102,6 +104,8 @@ export class DebugHoverWidget implements IContentWidget { this.complexValueTitle = dom.append(this.complexValueContainer, $('.title')); this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree')); this.treeContainer.setAttribute('role', 'tree'); + const tip = dom.append(this.complexValueContainer, $('.tip')); + tip.textContent = nls.localize('quickTip', 'Hold {0} key to switch to editor language hover', isMacintosh ? 'Option' : 'Alt'); const dataSource = new DebugHoverDataSource(); this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'DebugHover', this.treeContainer, new DebugHoverDelegate(), [this.instantiationService.createInstance(VariablesRenderer)], @@ -161,13 +165,17 @@ export class DebugHoverWidget implements IContentWidget { } isHovered(): boolean { - return this.domNode.matches(':hover'); + return !!this.domNode?.matches(':hover'); } isVisible(): boolean { return this._isVisible; } + willBeVisible(): boolean { + return !!this.showCancellationSource; + } + getId(): string { return DebugHoverWidget.ID; } @@ -177,6 +185,8 @@ export class DebugHoverWidget implements IContentWidget { } async showAt(range: Range, focus: boolean): Promise { + this.showCancellationSource?.cancel(); + const cancellationSource = this.showCancellationSource = new CancellationTokenSource(); const session = this.debugService.getViewModel().focusedSession; if (!session || !this.editor.hasModel()) { @@ -193,7 +203,7 @@ export class DebugHoverWidget implements IContentWidget { const supports = EvaluatableExpressionProviderRegistry.ordered(model); const promises = supports.map(support => { - return Promise.resolve(support.provideEvaluatableExpression(model, pos, CancellationToken.None)).then(expression => { + return Promise.resolve(support.provideEvaluatableExpression(model, pos, cancellationSource.token)).then(expression => { return expression; }, err => { //onUnexpectedExternalError(err); @@ -236,7 +246,7 @@ export class DebugHoverWidget implements IContentWidget { } } - if (!expression || (expression instanceof Expression && !expression.available)) { + if (cancellationSource.token.isCancellationRequested || !expression || (expression instanceof Expression && !expression.available)) { this.hide(); return; } @@ -315,6 +325,11 @@ export class DebugHoverWidget implements IContentWidget { hide(): void { + if (this.showCancellationSource) { + this.showCancellationSource.cancel(); + this.showCancellationSource = undefined; + } + if (!this._isVisible) { return; } diff --git a/src/vs/workbench/contrib/debug/browser/debugIcons.ts b/src/vs/workbench/contrib/debug/browser/debugIcons.ts new file mode 100644 index 00000000..b2203e38 --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/debugIcons.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const debugConsoleViewIcon = registerIcon('debug-console-view-icon', Codicon.debugConsole, localize('debugConsoleViewIcon', 'View icon of the debug console view.')); +export const runViewIcon = registerIcon('run-view-icon', Codicon.debugAlt, localize('runViewIcon', 'View icon of the run view.')); +export const variablesViewIcon = registerIcon('variables-view-icon', Codicon.debugAlt, localize('variablesViewIcon', 'View icon of the variables view.')); +export const watchViewIcon = registerIcon('watch-view-icon', Codicon.debugAlt, localize('watchViewIcon', 'View icon of the watch view.')); +export const callStackViewIcon = registerIcon('callstack-view-icon', Codicon.debugAlt, localize('callStackViewIcon', 'View icon of the call stack view.')); +export const breakpointsViewIcon = registerIcon('breakpoints-view-icon', Codicon.debugAlt, localize('breakpointsViewIcon', 'View icon of the breakpoints view.')); +export const loadedScriptsViewIcon = registerIcon('loaded-scripts-view-icon', Codicon.debugAlt, localize('loadedScriptsViewIcon', 'View icon of the loaded scripts view.')); + +export const debugBreakpoint = registerIcon('debug-breakpoint', Codicon.debugBreakpoint, localize('debugBreakpoint', 'Icon for breakpoints.')); +export const debugBreakpointDisabled = registerIcon('debug-breakpoint-disabled', Codicon.debugBreakpointDisabled, localize('debugBreakpointDisabled', 'Icon for disabled breakpoints.')); +export const debugBreakpointUnverified = registerIcon('debug-breakpoint-unverified', Codicon.debugBreakpointUnverified, localize('debugBreakpointUnverified', 'Icon for unverified breakpoints.')); +export const debugBreakpointHint = registerIcon('debug-hint', Codicon.debugHint, localize('debugBreakpointHint', 'Icon for breakpoint hints shown on hover in editor glyph margin.')); +export const debugBreakpointFunction = registerIcon('debug-breakpoint-function', Codicon.debugBreakpointFunction, localize('debugBreakpointFunction', 'Icon for function breakpoints.')); +export const debugBreakpointFunctionUnverified = registerIcon('debug-breakpoint-function-unverified', Codicon.debugBreakpointFunctionUnverified, localize('debugBreakpointFunctionUnverified', 'Icon for unverified function breakpoints.')); +export const debugBreakpointFunctionDisabled = registerIcon('debug-breakpoint-function-disabled', Codicon.debugBreakpointFunctionDisabled, localize('debugBreakpointFunctionDisabled', 'Icon for disabled function breakpoints.')); + +export const debugBreakpointUnsupported = registerIcon('debug-breakpoint-unsupported', Codicon.debugBreakpointUnsupported, localize('debugBreakpointUnsupported', 'Icon for unsupported breakpoints.')); + +export const debugBreakpointConditionalUnverified = registerIcon('debug-breakpoint-conditional-unverified', Codicon.debugBreakpointConditionalUnverified, localize('debugBreakpointConditionalUnverified', 'Icon for unverified conditional breakpoints.')); +export const debugBreakpointConditional = registerIcon('debug-breakpoint-conditional', Codicon.debugBreakpointConditional, localize('debugBreakpointConditional', 'Icon for conditional breakpoints.')); +export const debugBreakpointConditionalDisabled = registerIcon('debug-breakpoint-conditional-disabled', Codicon.debugBreakpointConditionalDisabled, localize('debugBreakpointConditionalDisabled', 'Icon for disabled conditional breakpoints.')); +export const debugBreakpointDataUnverified = registerIcon('debug-breakpoint-data-unverified', Codicon.debugBreakpointDataUnverified, localize('debugBreakpointDataUnverified', 'Icon for unverified data breakpoints.')); +export const debugBreakpointData = registerIcon('debug-breakpoint-data', Codicon.debugBreakpointData, localize('debugBreakpointData', 'Icon for data breakpoints.')); +export const debugBreakpointDataDisabled = registerIcon('debug-breakpoint-data-disabled', Codicon.debugBreakpointDataDisabled, localize('debugBreakpointDataDisabled', 'Icon for disabled data breakpoints.')); +export const debugBreakpointLogUnverified = registerIcon('debug-breakpoint-log-unverified', Codicon.debugBreakpointLogUnverified, localize('debugBreakpointLogUnverified', 'Icon for unverified log breakpoints.')); +export const debugBreakpointLog = registerIcon('debug-breakpoint-log', Codicon.debugBreakpointLog, localize('debugBreakpointLog', 'Icon for log breakpoints.')); +export const debugBreakpointLogDisabled = registerIcon('debug-breakpoint-log-disabled', Codicon.debugBreakpointLogDisabled, localize('debugBreakpointLogDisabled', 'Icon for disabled log breakpoint.')); + +export const debugStackframe = registerIcon('debug-stackframe', Codicon.debugStackframe, localize('debugStackframe', 'Icon for a stackframe shown in the editor glyph margin.')); +export const debugStackframeFocused = registerIcon('debug-stackframe-focused', Codicon.debugStackframeFocused, localize('debugStackframeFocused', 'Icon for a focused stackframe shown in the editor glyph margin.')); + +export const debugGripper = registerIcon('debug-gripper', Codicon.gripper, localize('debugGripper', 'Icon for the debug bar gripper.')); + +export const debugRestartFrame = registerIcon('debug-restart-frame', Codicon.debugRestartFrame, localize('debugRestartFrame', 'Icon for the debug restart frame action.')); + +export const debugStop = registerIcon('debug-stop', Codicon.debugStop, localize('debugStop', 'Icon for the debug stop action.')); +export const debugDisconnect = registerIcon('debug-disconnect', Codicon.debugDisconnect, localize('debugDisconnect', 'Icon for the debug disconnect action.')); +export const debugRestart = registerIcon('debug-restart', Codicon.debugRestart, localize('debugRestart', 'Icon for the debug restart action.')); +export const debugStepOver = registerIcon('debug-step-over', Codicon.debugStepOver, localize('debugStepOver', 'Icon for the debug step over action.')); +export const debugStepInto = registerIcon('debug-step-into', Codicon.debugStepInto, localize('debugStepInto', 'Icon for the debug step into action.')); +export const debugStepOut = registerIcon('debug-step-out', Codicon.debugStepOut, localize('debugStepOut', 'Icon for the debug step out action.')); +export const debugStepBack = registerIcon('debug-step-back', Codicon.debugStepBack, localize('debugStepBack', 'Icon for the debug step back action.')); +export const debugPause = registerIcon('debug-pause', Codicon.debugPause, localize('debugPause', 'Icon for the debug pause action.')); +export const debugContinue = registerIcon('debug-continue', Codicon.debugContinue, localize('debugContinue', 'Icon for the debug continue action.')); +export const debugReverseContinue = registerIcon('debug-reverse-continue', Codicon.debugReverseContinue, localize('debugReverseContinue', 'Icon for the debug reverse continue action.')); + +export const debugStart = registerIcon('debug-start', Codicon.debugStart, localize('debugStart', 'Icon for the debug start action.')); +export const debugConfigure = registerIcon('debug-configure', Codicon.gear, localize('debugConfigure', 'Icon for the debug configure action.')); +export const debugConsole = registerIcon('debug-console', Codicon.gear, localize('debugConsole', 'Icon for the debug console open action.')); + +export const debugCollapseAll = registerIcon('debug-collapse-all', Codicon.collapseAll, localize('debugCollapseAll', 'Icon for the collapse all action in the debug views.')); +export const callstackViewSession = registerIcon('callstack-view-session', Codicon.bug, localize('callstackViewSession', 'Icon for the session icon in the call stack view.')); +export const debugConsoleClearAll = registerIcon('debug-console-clear-all', Codicon.clearAll, localize('debugConsoleClearAll', 'Icon for the clear all action in the debug console.')); +export const watchExpressionsRemoveAll = registerIcon('watch-expressions-remove-all', Codicon.closeAll, localize('watchExpressionsRemoveAll', 'Icon for the remove all action in the watch view.')); +export const watchExpressionsAdd = registerIcon('watch-expressions-add', Codicon.add, localize('watchExpressionsAdd', 'Icon for the add action in the watch view.')); +export const watchExpressionsAddFuncBreakpoint = registerIcon('watch-expressions-add-function-breakpoint', Codicon.add, localize('watchExpressionsAddFuncBreakpoint', 'Icon for the add function breakpoint action in the watch view.')); + +export const breakpointsRemoveAll = registerIcon('breakpoints-remove-all', Codicon.closeAll, localize('breakpointsRemoveAll', 'Icon for the remove all action in the breakpoints view.')); +export const breakpointsActivate = registerIcon('breakpoints-activate', Codicon.activateBreakpoints, localize('breakpointsActivate', 'Icon for the activate action in the breakpoints view.')); + +export const debugConsoleEvaluationInput = registerIcon('debug-console-evaluation-input', Codicon.arrowSmallRight, localize('debugConsoleEvaluationInput', 'Icon for the debug evaluation input marker.')); +export const debugConsoleEvaluationPrompt = registerIcon('debug-console-evaluation-prompt', Codicon.chevronRight, localize('debugConsoleEvaluationPrompt', 'Icon for the debug evaluation prompt.')); diff --git a/src/vs/workbench/contrib/debug/browser/debugProgress.ts b/src/vs/workbench/contrib/debug/browser/debugProgress.ts index 6a8ef039..67c97535 100644 --- a/src/vs/workbench/contrib/debug/browser/debugProgress.ts +++ b/src/vs/workbench/contrib/debug/browser/debugProgress.ts @@ -39,7 +39,7 @@ export class DebugProgressContribution implements IWorkbenchContribution { if (viewsService.isViewContainerVisible(VIEWLET_ID)) { progressService.withProgress({ location: VIEWLET_ID }, () => promise); } - const source = debugService.getConfigurationManager().getDebuggerLabel(session.configuration.type); + const source = debugService.getAdapterManager().getDebuggerLabel(session.configuration.type); progressService.withProgress({ location: ProgressLocation.Notification, title: progressStartEvent.body.title, diff --git a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts index b8aac795..2b6f530c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts +++ b/src/vs/workbench/contrib/debug/browser/debugQuickAccess.ts @@ -13,6 +13,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { matchesFuzzy } from 'vs/base/common/filters'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; +import { debugConfigure } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { @@ -55,7 +57,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { @@ -64,7 +66,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { - await this.debugService.getConfigurationManager().selectConfiguration(config.launch, config.name); + await configManager.selectConfiguration(config.launch, config.name); try { await this.debugService.startDebugging(config.launch); } catch (error) { @@ -86,6 +88,26 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { + const highlights = matchesFuzzy(filter, name, true); + if (highlights) { + picks.push({ + label: name, + highlights: { label: highlights }, + accept: async () => { + await configManager.selectConfiguration(undefined, name, undefined, { type }); + try { + const { launch, getConfig } = configManager.selectedConfiguration; + const config = await getConfig(); + await this.debugService.startDebugging(launch, config); + } catch (error) { + this.notificationService.error(error); + } + } + }); + } + }); + dynamicProviders.forEach(provider => { picks.push({ label: `$(folder) ${provider.label}...`, @@ -93,6 +115,7 @@ export class StartDebugQuickAccessProvider extends PickerQuickAccessProvider { const pick = await provider.pick(); if (pick) { + await configManager.selectConfiguration(pick.launch, pick.config.name, pick.config, { type: pick.config.type }); this.debugService.startDebugging(pick.launch, pick.config); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 6f2078d0..e05a471f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -32,7 +32,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { deepClone, equals } from 'vs/base/common/objects'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IStackFrame, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, IGlobalConfig, CALLSTACK_VIEW_ID, IAdapterManager, IExceptionBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils'; import { isErrorWithActions } from 'vs/base/common/errorsWithActions'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -49,6 +49,8 @@ import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompou import { ICommandService } from 'vs/platform/commands/common/commands'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { AdapterManager } from 'vs/workbench/contrib/debug/browser/debugAdapterManager'; +import { ITextModel } from 'vs/editor/common/model'; export class DebugService implements IDebugService { declare readonly _serviceBrand: undefined; @@ -63,6 +65,7 @@ export class DebugService implements IDebugService { private telemetry: DebugTelemetry; private taskRunner: DebugTaskRunner; private configurationManager: ConfigurationManager; + private adapterManager: AdapterManager; private toDispose: IDisposable[]; private debugType!: IContextKey; private debugState!: IContextKey; @@ -105,7 +108,8 @@ export class DebugService implements IDebugService { this._onWillNewSession = new Emitter(); this._onDidEndSession = new Emitter(); - this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); + this.adapterManager = this.instantiationService.createInstance(AdapterManager); + this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.adapterManager); this.toDispose.push(this.configurationManager); contextKeyService.bufferChangeEvents(() => { @@ -113,7 +117,7 @@ export class DebugService implements IDebugService { this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService); - this.debugUx.set((this.configurationManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); + this.debugUx.set((this.adapterManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); }); @@ -162,8 +166,8 @@ export class DebugService implements IDebugService { this.toDispose.push(this.viewModel.onDidFocusSession(() => { this.onStateChange(); })); - this.toDispose.push(Event.any(this.configurationManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => { - this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.configurationManager.hasDebuggers())) ? 'default' : 'simple'); + this.toDispose.push(Event.any(this.adapterManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => { + this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.adapterManager.hasDebuggers())) ? 'default' : 'simple'); })); this.toDispose.push(this.model.onDidChangeCallStack(() => { const numberOfSessions = this.model.getSessions().filter(s => !s.parentSession).length; @@ -192,6 +196,10 @@ export class DebugService implements IDebugService { return this.configurationManager; } + getAdapterManager(): IAdapterManager { + return this.adapterManager; + } + sourceIsNotAvailable(uri: uri): void { this.model.sourceIsNotAvailable(uri); } @@ -245,7 +253,7 @@ export class DebugService implements IDebugService { this.debugState.set(getStateLabel(state)); this.inDebugMode.set(state !== State.Inactive); // Only show the simple ux if debug is not yet started and if no launch.json exists - this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.configurationManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple'); + this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.adapterManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple'); }); this.previousState = state; this._onDidChangeState.fire(state); @@ -297,7 +305,7 @@ export class DebugService implements IDebugService { const sessions = this.model.getSessions(); const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName); - if (sessions.some(s => s.configuration.name === configOrName && (!launch || !launch.workspace || !s.root || this.uriIdentityService.extUri.isEqual(s.root.uri, launch.workspace.uri)))) { + if (sessions.some(s => (s.configuration.name === configOrName && s.root === launch.workspace) && (!launch || !launch.workspace || !s.root || this.uriIdentityService.extUri.isEqual(s.root.uri, launch.workspace.uri)))) { throw new Error(alreadyRunningMessage); } if (compound && compound.configurations && sessions.some(p => compound!.configurations.indexOf(p.configuration.name) !== -1)) { @@ -397,7 +405,7 @@ export class DebugService implements IDebugService { const unresolvedConfig = deepClone(config); if (!type) { - const guess = await this.configurationManager.guessDebugger(); + const guess = await this.adapterManager.guessDebugger(); if (guess) { type = guess.type; } @@ -437,7 +445,7 @@ export class DebugService implements IDebugService { } resolvedConfig = cfg; - if (!this.configurationManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) { + if (!this.adapterManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) { let message: string; if (configByProviders.request !== 'attach' && configByProviders.request !== 'launch') { message = configByProviders.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', configByProviders.request) @@ -549,7 +557,7 @@ export class DebugService implements IDebugService { } private async launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise { - const dbgr = this.configurationManager.getDebugger(session.configuration.type); + const dbgr = this.adapterManager.getDebugger(session.configuration.type); try { await session.initialize(dbgr!); await session.launchOrAttach(session.configuration); @@ -754,7 +762,7 @@ export class DebugService implements IDebugService { } private async substituteVariables(launch: ILaunch | undefined, config: IConfig): Promise { - const dbg = this.configurationManager.getDebugger(config.type); + const dbg = this.adapterManager.getDebugger(config.type); if (dbg) { let folder: IWorkspaceFolder | undefined = undefined; if (launch && launch.workspace) { @@ -842,6 +850,10 @@ export class DebugService implements IDebugService { //---- breakpoints + canSetBreakpointsIn(model: ITextModel): boolean { + return this.adapterManager.canSetBreakpointsIn(model); + } + async enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise { if (breakpoint) { this.model.setEnablement(breakpoint, enable); @@ -906,7 +918,7 @@ export class DebugService implements IDebugService { addFunctionBreakpoint(name?: string, id?: string): void { const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id); - this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint); + this.viewModel.setSelectedBreakpoint(newFunctionBreakpoint); } async renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { @@ -934,6 +946,12 @@ export class DebugService implements IDebugService { await this.sendDataBreakpoints(); } + async setExceptionBreakpointCondition(exceptionBreakpoint: IExceptionBreakpoint, condition: string | undefined): Promise { + this.model.setExceptionBreakpointCondition(exceptionBreakpoint, condition); + this.debugStorage.storeBreakpoints(this.model); + await this.sendExceptionBreakpoints(); + } + async sendAllBreakpoints(session?: IDebugSession): Promise { await Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))); await this.sendFunctionBreakpoints(session); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 663ff710..9b8c75af 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -394,7 +394,18 @@ export class DebugSession implements IDebugSession { } if (this.raw.readyForBreakpoints) { - await this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) }); + const args: DebugProtocol.SetExceptionBreakpointsArguments = this.capabilities.supportsExceptionFilterOptions ? { + filters: [], + filterOptions: exbpts.map(exb => { + if (exb.condition) { + return { filterId: exb.filter, condition: exb.condition }; + } + + return { filterId: exb.filter }; + }) + } : { filters: exbpts.map(exb => exb.filter) }; + + await this.raw.setExceptionBreakpoints(args); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 93635f7f..8f62ac8d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -16,9 +16,9 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { registerThemingParticipant, IThemeService, Themable } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -29,6 +29,7 @@ import { IMenu, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from ' import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; const DEBUG_TOOLBAR_POSITION_KEY = 'debug.actionswidgetposition'; const DEBUG_TOOLBAR_Y_KEY = 'debug.actionswidgety'; @@ -64,7 +65,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); this.$el.style.top = `${layoutService.offset?.top ?? 0}px`; - this.dragArea = dom.append(this.$el, dom.$('div.drag-area.codicon.codicon-gripper')); + this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container')); this.debugToolBarMenu = menuService.createMenu(MenuId.DebugToolBar, contextKeyService); @@ -169,7 +170,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { const left = dom.getComputedStyle(this.$el).left; if (left) { const position = parseFloat(left) / window.innerWidth; - this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.GLOBAL); + this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -180,7 +181,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el.style.backgroundColor = this.getColor(debugToolBarBackground) || ''; const widgetShadowColor = this.getColor(widgetShadow); - this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : ''; + this.$el.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : ''; const contrastBorderColor = this.getColor(contrastBorder); const borderColor = this.getColor(debugToolBarBorder); @@ -220,7 +221,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { if ((y < titleAreaHeight / 2) || (y > titleAreaHeight + titleAreaHeight / 2)) { const moveToTop = y < titleAreaHeight; this.setYCoordinate(moveToTop ? 0 : titleAreaHeight); - this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.GLOBAL); + this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -351,51 +352,51 @@ registerThemingParticipant((theme, collector) => { const debugIconStartColor = theme.getColor(debugIconStartForeground); if (debugIconStartColor) { - collector.addRule(`.monaco-workbench .codicon-debug-start { color: ${debugIconStartColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStart)} { color: ${debugIconStartColor} !important; }`); } const debugIconPauseColor = theme.getColor(debugIconPauseForeground); if (debugIconPauseColor) { - collector.addRule(`.monaco-workbench .codicon-debug-pause { color: ${debugIconPauseColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugPause)} { color: ${debugIconPauseColor} !important; }`); } const debugIconStopColor = theme.getColor(debugIconStopForeground); if (debugIconStopColor) { - collector.addRule(`.monaco-workbench .codicon-debug-stop, .monaco-workbench .debug-view-content .codicon-record { color: ${debugIconStopColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStop)} { color: ${debugIconStopColor} !important; }`); } const debugIconDisconnectColor = theme.getColor(debugIconDisconnectForeground); if (debugIconDisconnectColor) { - collector.addRule(`.monaco-workbench .codicon-debug-disconnect { color: ${debugIconDisconnectColor} !important; }`); + collector.addRule(`.monaco-workbench .debug-view-content ${ThemeIcon.asCSSSelector(icons.debugDisconnect)}, .monaco-workbench .debug-toolbar ${ThemeIcon.asCSSSelector(icons.debugDisconnect)} { color: ${debugIconDisconnectColor} !important; }`); } const debugIconRestartColor = theme.getColor(debugIconRestartForeground); if (debugIconRestartColor) { - collector.addRule(`.monaco-workbench .codicon-debug-restart, .monaco-workbench .codicon-debug-restart-frame { color: ${debugIconRestartColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugRestart)}, .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugRestartFrame)} { color: ${debugIconRestartColor} !important; }`); } const debugIconStepOverColor = theme.getColor(debugIconStepOverForeground); if (debugIconStepOverColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-over { color: ${debugIconStepOverColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepOver)} { color: ${debugIconStepOverColor} !important; }`); } const debugIconStepIntoColor = theme.getColor(debugIconStepIntoForeground); if (debugIconStepIntoColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-into { color: ${debugIconStepIntoColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepInto)} { color: ${debugIconStepIntoColor} !important; }`); } const debugIconStepOutColor = theme.getColor(debugIconStepOutForeground); if (debugIconStepOutColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-out { color: ${debugIconStepOutColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepOut)} { color: ${debugIconStepOutColor} !important; }`); } const debugIconContinueColor = theme.getColor(debugIconContinueForeground); if (debugIconContinueColor) { - collector.addRule(`.monaco-workbench .codicon-debug-continue,.monaco-workbench .codicon-debug-reverse-continue { color: ${debugIconContinueColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugContinue)}, .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugReverseContinue)} { color: ${debugIconContinueColor} !important; }`); } const debugIconStepBackColor = theme.getColor(debugIconStepBackForeground); if (debugIconStepBackColor) { - collector.addRule(`.monaco-workbench .codicon-debug-step-back { color: ${debugIconStepBackColor} !important; }`); + collector.addRule(`.monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStepBack)} { color: ${debugIconStepBackColor} !important; }`); } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index f13479c4..91187dd3 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -15,7 +15,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -33,6 +33,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { debugConsole } from 'vs/workbench/contrib/debug/browser/debugIcons'; export class DebugViewPaneContainer extends ViewPaneContainer { @@ -247,7 +248,7 @@ export class OpenDebugConsoleAction extends ToggleViewAction { @IContextKeyService contextKeyService: IContextKeyService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService ) { - super(id, label, REPL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService, 'codicon-debug-console'); + super(id, label, REPL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService, ThemeIcon.asClassName(debugConsole)); } } diff --git a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts index 93f702fa..67a12c48 100644 --- a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts @@ -8,14 +8,17 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IExceptionInfo, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IExceptionInfo, IDebugSession, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID } from 'vs/workbench/contrib/debug/common/debug'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; const $ = dom.$; // theming @@ -25,18 +28,19 @@ export const debugExceptionWidgetBackground = registerColor('debugExceptionWidge export class ExceptionWidget extends ZoneWidget { - private _backgroundColor?: Color; + private backgroundColor: Color | undefined; - constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private debugSession: IDebugSession | undefined, + constructor( + editor: ICodeEditor, + private exceptionInfo: IExceptionInfo, + private debugSession: IDebugSession | undefined, @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { - super(editor, { showFrame: true, showArrow: true, frameWidth: 1, className: 'exception-widget-container' }); + super(editor, { showFrame: true, showArrow: true, isAccessible: true, frameWidth: 1, className: 'exception-widget-container' }); - this._backgroundColor = Color.white; - - this._applyTheme(themeService.getColorTheme()); - this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); + this.applyTheme(themeService.getColorTheme()); + this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme.bind(this))); this.create(); const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50); @@ -44,8 +48,8 @@ export class ExceptionWidget extends ZoneWidget { this._disposables.add(onDidLayoutChangeScheduler); } - private _applyTheme(theme: IColorTheme): void { - this._backgroundColor = theme.getColor(debugExceptionWidgetBackground); + private applyTheme(theme: IColorTheme): void { + this.backgroundColor = theme.getColor(debugExceptionWidgetBackground); const frameColor = theme.getColor(debugExceptionWidgetBorder); this.style({ arrowColor: frameColor, @@ -55,7 +59,7 @@ export class ExceptionWidget extends ZoneWidget { protected _applyStyles(): void { if (this.container) { - this.container.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : ''; + this.container.style.backgroundColor = this.backgroundColor ? this.backgroundColor.toString() : ''; } super._applyStyles(); } @@ -66,14 +70,27 @@ export class ExceptionWidget extends ZoneWidget { const fontInfo = this.editor.getOption(EditorOption.fontInfo); container.style.fontSize = `${fontInfo.fontSize}px`; container.style.lineHeight = `${fontInfo.lineHeight}px`; + container.tabIndex = 0; + const title = $('.title'); + const label = $('.label'); + dom.append(title, label); + const actions = $('.actions'); + dom.append(title, actions); + label.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.'); + let ariaLabel = label.textContent; + + const actionBar = new ActionBar(actions); + actionBar.push(new Action('editor.closeExceptionWidget', nls.localize('close', "Close"), ThemeIcon.asClassName(widgetClose), true, async () => { + const contribution = this.editor.getContribution(EDITOR_CONTRIBUTION_ID); + contribution.closeExceptionWidget(); + }), { label: false, icon: true }); - let title = $('.title'); - title.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.'); dom.append(container, title); if (this.exceptionInfo.description) { let description = $('.description'); description.textContent = this.exceptionInfo.description; + ariaLabel += ', ' + this.exceptionInfo.description; dom.append(container, description); } @@ -83,7 +100,9 @@ export class ExceptionWidget extends ZoneWidget { const linkedStackTrace = linkDetector.linkify(this.exceptionInfo.details.stackTrace, true, this.debugSession ? this.debugSession.root : undefined); stackTrace.appendChild(linkedStackTrace); dom.append(container, stackTrace); + ariaLabel += ', ' + this.exceptionInfo.details.stackTrace; } + container.setAttribute('aria-label', ariaLabel); } protected _doLayout(_heightInPixel: number | undefined, _widthInPixel: number | undefined): void { @@ -96,4 +115,13 @@ export class ExceptionWidget extends ZoneWidget { this._relayout(computedLinesNumber); } + + focus(): void { + // Focus into the container for accessibility purposes so the exception and stack trace gets read + this.container?.focus(); + } + + hasfocus(): boolean { + return dom.isAncestor(document.activeElement, this.container); + } } diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index e115fe0b..198da910 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -16,7 +16,7 @@ import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from ' import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { tildify } from 'vs/base/common/labels'; +import { normalizeDriveLetter, tildify } from 'vs/base/common/labels'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { ltrim } from 'vs/base/common/strings'; @@ -350,7 +350,9 @@ class SessionTreeItem extends BaseTreeItem { } else { // on unix try to tildify absolute paths path = normalize(path); - if (!isWindows) { + if (isWindows) { + path = normalizeDriveLetter(path); + } else { path = tildify(path, (await this._pathService.userHome()).fsPath); } } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugHover.css b/src/vs/workbench/contrib/debug/browser/media/debugHover.css index 6c48d940..8f5ae2c9 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugHover.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugHover.css @@ -19,7 +19,8 @@ max-width: 700px; } -.monaco-editor .debug-hover-widget .complex-value .title { +.monaco-editor .debug-hover-widget .complex-value .title, +.monaco-editor .debug-hover-widget .complex-value .tip { padding-left: 15px; padding-right: 2px; font-size: 11px; @@ -29,9 +30,17 @@ height: 18px; overflow: hidden; white-space: pre; +} + +.monaco-editor .debug-hover-widget .complex-value .title { border-bottom: 1px solid rgba(128, 128, 128, 0.35); } +.monaco-editor .debug-hover-widget .complex-value .tip { + border-top: 1px solid rgba(128, 128, 128, 0.35); + opacity: 0.5; +} + .monaco-editor .debug-hover-widget .debug-hover-tree { line-height: 18px; cursor: pointer; diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index e0b6acb3..47cafc6a 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -84,6 +84,7 @@ /* Make icons and text the same color as the list foreground on focus selection */ .debug-pane .monaco-list:focus .monaco-list-row.selected .state.label, +.debug-pane .monaco-list:focus .monaco-list-row.selected .load-all, .debug-pane .monaco-list:focus .monaco-list-row.selected.focused .state.label, .debug-pane .monaco-list:focus .monaco-list-row.selected .codicon, .debug-pane .monaco-list:focus .monaco-list-row.selected.focused .codicon { @@ -317,10 +318,10 @@ justify-content: center; } -.debug-pane .debug-breakpoints .breakpoint > .file-path { +.debug-pane .debug-breakpoints .breakpoint > .file-path, +.debug-pane .debug-breakpoints .breakpoint.exception > .condition { opacity: 0.7; - font-size: 0.9em; - margin-left: 0.8em; + margin-left: 0.9em; flex: 1; text-overflow: ellipsis; overflow: hidden; diff --git a/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css b/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css index df149d3b..f9d7846d 100644 --- a/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css +++ b/src/vs/workbench/contrib/debug/browser/media/exceptionWidget.css @@ -15,9 +15,17 @@ } .monaco-editor .zone-widget .zone-widget-container.exception-widget .title { + display: flex; +} + +.monaco-editor .zone-widget .zone-widget-container.exception-widget .title .label { font-weight: bold; } +.monaco-editor .zone-widget .zone-widget-container.exception-widget .title .actions { + flex: 1; +} + .monaco-editor .zone-widget .zone-widget-container.exception-widget .description, .monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace { font-family: var(--monaco-monospace-font); @@ -27,7 +35,7 @@ margin-top: 0.5em; } -.monaco-editor .zone-widget .zone-widget-container.exception-widget a { +.monaco-editor .zone-widget .zone-widget-container.exception-widget .stack-trace a { text-decoration: underline; cursor: pointer; } diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index b2a47407..04de3a21 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -38,6 +38,15 @@ margin-right: 4px; } +.monaco-workbench .repl .repl-tree .output.expression.value-and-source .count-badge-wrapper { + margin-right: 4px; +} + +/* Allow the badge to be a bit shorter so it does not look cut off */ +.monaco-workbench .repl .repl-tree .output.expression.value-and-source .count-badge-wrapper .monaco-count-badge { + min-height: 16px; +} + .monaco-workbench .repl .repl-tree .monaco-tl-contents .arrow { position:absolute; left: 2px; diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index dce39124..a7003659 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -620,10 +620,10 @@ export class RawDebugSession implements IDisposable { } } - let env: IProcessEnvironment = {}; - if (vscodeArgs.env) { + let env: IProcessEnvironment = processEnv; + if (vscodeArgs.env && Object.keys(vscodeArgs.env).length > 0) { // merge environment variables into a copy of the process.env - env = objects.mixin(processEnv, vscodeArgs.env); + env = objects.mixin(objects.deepClone(processEnv), vscodeArgs.env); // and delete some if necessary Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]); } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index cbc9401e..74467fa8 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -19,8 +19,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { memoize } from 'vs/base/common/decorators'; import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -59,10 +59,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; import { ReplFilter, ReplFilterState, ReplFilterActionViewItem } from 'vs/workbench/contrib/debug/browser/replFilter'; +import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; const HISTORY_STORAGE_KEY = 'debug.repl.history'; +const FILTER_HISTORY_STORAGE_KEY = 'debug.repl.filterHistory'; const DECORATION_KEY = 'replinputdecoration'; const FILTER_ACTION_ID = `workbench.actions.treeView.repl.filter`; @@ -303,7 +305,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { if (this.styleElement) { const debugConsole = this.configurationService.getValue('debug').console; const fontSize = debugConsole.fontSize; - const fontFamily = debugConsole.fontFamily === 'default' ? 'var(--monaco-monospace-font)' : debugConsole.fontFamily; + const fontFamily = debugConsole.fontFamily === 'default' ? 'var(--monaco-monospace-font)' : `'${debugConsole.fontFamily}'`; const lineHeight = debugConsole.lineHeight ? `${debugConsole.lineHeight}px` : '1.4em'; const backgroundColor = this.themeService.getColorTheme().getColor(this.getBackgroundColor()); @@ -462,7 +464,9 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { const session = (this.tree ? this.tree.getInput() : undefined) ?? this.debugService.getViewModel().focusedSession; return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction, session); } else if (action.id === FILTER_ACTION_ID) { - this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState); + const filterHistory = JSON.parse(this.storageService.get(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')) as string[]; + this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, + localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState, filterHistory); return this.filterActionViewItem; } @@ -592,7 +596,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private createReplInput(container: HTMLElement): void { this.replInputContainer = dom.append(container, $('.repl-input-wrapper')); - dom.append(this.replInputContainer, $('.repl-input-chevron.codicon.codicon-chevron-right')); + dom.append(this.replInputContainer, $('.repl-input-chevron' + ThemeIcon.asCSSSelector(debugConsoleEvaluationPrompt))); const { scopedContextKeyService, historyNavigationEnablement } = createAndBindHistoryNavigationWidgetScopedContextKeyService(this.contextKeyService, { target: this.replInputContainer, historyNavigator: this }); this.historyNavigationEnablement = historyNavigationEnablement; @@ -708,10 +712,18 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { saveState(): void { const replHistory = this.history.getHistory(); if (replHistory.length) { - this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); + this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); } + if (this.filterActionViewItem) { + const filterHistory = this.filterActionViewItem.getHistory(); + if (filterHistory.length) { + this.storageService.store(FILTER_HISTORY_STORAGE_KEY, JSON.stringify(filterHistory), StorageScope.WORKSPACE, StorageTarget.USER); + } else { + this.storageService.remove(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); + } + } super.saveState(); } @@ -845,7 +857,7 @@ export class ClearReplAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'debug-action codicon-clear-all'); + super(id, label, 'debug-action ' + ThemeIcon.asClassName(debugConsoleClearAll)); } async run(): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 143d555a..3cb5e45d 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -136,6 +136,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { action: IAction, private placeholder: string, private filters: ReplFilterState, + private history: string[], @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService private readonly themeService: IThemeService, @IContextViewService private readonly contextViewService: IContextViewService) { @@ -159,6 +160,10 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { this.filterInputBox.focus(); } + getHistory(): string[] { + return this.filterInputBox.getHistory(); + } + private clearFilterText(): void { this.filterInputBox.value = ''; } @@ -166,7 +171,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { private createInput(container: HTMLElement): void { this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, { placeholder: this.placeholder, - history: [] + history: this.history })); this._register(attachInputBoxStyler(this.filterInputBox, this.themeService)); this.filterInputBox.value = this.filters.filterText; diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index 4fbb33eb..0d432417 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -21,8 +21,11 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IReplElementSource, IDebugService, IExpression, IReplElement, IDebugConfiguration, IDebugSession, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { localize } from 'vs/nls'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; +import { debugConsoleEvaluationInput } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; @@ -40,10 +43,13 @@ interface IReplEvaluationResultTemplateData { interface ISimpleReplElementTemplateData { container: HTMLElement; + count: CountBadge; + countContainer: HTMLElement; value: HTMLElement; source: HTMLElement; getReplElementSource(): IReplElementSource | undefined; toDispose: IDisposable[]; + elementListener: IDisposable; } interface IRawObjectReplTemplateData { @@ -62,7 +68,7 @@ export class ReplEvaluationInputsRenderer implements ITreeRenderer { +export class ReplEvaluationResultsRenderer implements ITreeRenderer { static readonly ID = 'replEvaluationResult'; get templateId(): string { @@ -117,7 +123,7 @@ export class ReplEvaluationResultsRenderer implements ITreeRenderer, index: number, templateData: IReplEvaluationResultTemplateData): void { + renderElement(element: ITreeNode, index: number, templateData: IReplEvaluationResultTemplateData): void { const expression = element.element; renderExpressionValue(expression, templateData.value, { showHover: false, @@ -151,9 +157,12 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer { e.preventDefault(); e.stopPropagation(); @@ -172,11 +181,13 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer, index: number, templateData: ISimpleReplElementTemplateData): void { + this.setElementCount(element, templateData); + templateData.elementListener = element.onDidChangeCount(() => this.setElementCount(element, templateData)); // value dom.clearNode(templateData.value); // Reset classes to clear ansi decorations since templates are reused templateData.value.className = 'value'; - const result = handleANSIOutput(element.value, this.linkDetector, this.themeService, element.session); + const result = handleANSIOutput(element.value, this.linkDetector, this.themeService, element.session.root); templateData.value.appendChild(result); templateData.value.classList.add((element.severity === severity.Warning) ? 'warn' : (element.severity === severity.Error) ? 'error' : (element.severity === severity.Ignore) ? 'ignore' : 'info'); @@ -185,9 +196,22 @@ export class ReplSimpleElementsRenderer implements ITreeRenderer element.sourceData; } + private setElementCount(element: SimpleReplElement, templateData: ISimpleReplElementTemplateData): void { + if (element.count >= 2) { + templateData.count.setCount(element.count); + templateData.countContainer.hidden = false; + } else { + templateData.countContainer.hidden = true; + } + } + disposeTemplate(templateData: ISimpleReplElementTemplateData): void { dispose(templateData.toDispose); } + + disposeElement(_element: ITreeNode, _index: number, templateData: ISimpleReplElementTemplateData): void { + templateData.elementListener.dispose(); + } } export class ReplVariablesRenderer extends AbstractExpressionsRenderer { @@ -296,14 +320,14 @@ export class ReplDelegate extends CachedListVirtualDelegate { if (element instanceof Variable && element.name) { return ReplVariablesRenderer.ID; } - if (element instanceof ReplEvaluationResult) { + if (element instanceof ReplEvaluationResult || (element instanceof Variable && !element.name)) { + // Variable with no name is a top level variable which should be rendered like a repl element #17404 return ReplEvaluationResultsRenderer.ID; } if (element instanceof ReplEvaluationInput) { return ReplEvaluationInputsRenderer.ID; } - if (element instanceof SimpleReplElement || (element instanceof Variable && !element.name)) { - // Variable with no name is a top level variable which should be rendered like a repl element #17404 + if (element instanceof SimpleReplElement) { return ReplSimpleElementsRenderer.ID; } if (element instanceof ReplGroup) { @@ -359,13 +383,13 @@ export class ReplAccessibilityProvider implements IListAccessibilityProvider 1 ? localize('occurred', ", occured {0} times", element.count) : ''); } if (element instanceof RawObjectReplElement) { return localize('replRawObjectAriaLabel', "Debug console variable {0}, value {1}", element.name, element.value); } if (element instanceof ReplGroup) { - return localize('replGroup', "Debug console group {0}, read eval print loop, debug", element.name); + return localize('replGroup', "Debug console group {0}", element.name); } return ''; diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 9a4560c0..7c8acdc4 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -30,12 +30,13 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { dispose } from 'vs/base/common/lifecycle'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { debugCollapseAll } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; let forgetScopes = true; @@ -179,7 +180,7 @@ export class VariablesView extends ViewPane { } getActions(): IAction[] { - return [new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all')]; + return [new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(debugCollapseAll))]; } layoutBody(width: number, height: number): void { @@ -207,8 +208,8 @@ export class VariablesView extends ViewPane { this.variableEvaluateName.set(!!variable.evaluateName); this.breakWhenValueChangesSupported.reset(); if (session && session.capabilities.supportsDataBreakpoints) { - const response = await session.dataBreakpointInfo(variable.name, variable.parent.reference); - const dataBreakpointId = response?.dataId; + dataBreakpointInfoResponse = await session.dataBreakpointInfo(variable.name, variable.parent.reference); + const dataBreakpointId = dataBreakpointInfoResponse?.dataId; this.breakWhenValueChangesSupported.set(!!dataBreakpointId); } diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index ac56c479..2e18414c 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -30,8 +30,9 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { dispose } from 'vs/base/common/lifecycle'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { debugCollapseAll } from 'vs/workbench/contrib/debug/browser/debugIcons'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; let ignoreViewUpdates = false; @@ -160,7 +161,7 @@ export class WatchExpressionsView extends ViewPane { getActions(): IAction[] { return [ new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService), - new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all'), + new CollapseAction(() => this.tree, true, 'explorer-action ' + ThemeIcon.asClassName(debugCollapseAll)), new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService) ]; } diff --git a/src/vs/workbench/contrib/debug/browser/welcomeView.ts b/src/vs/workbench/contrib/debug/browser/welcomeView.ts index 37f5d797..c88b28e8 100644 --- a/src/vs/workbench/contrib/debug/browser/welcomeView.ts +++ b/src/vs/workbench/contrib/debug/browser/welcomeView.ts @@ -22,7 +22,7 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { isMacintosh } from 'vs/base/common/platform'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -65,10 +65,10 @@ export class WelcomeView extends ViewPane { if (isCodeEditor(editorControl)) { const model = editorControl.getModel(); const language = model ? model.getLanguageIdentifier().language : undefined; - if (language && this.debugService.getConfigurationManager().isDebuggerInterestedInLanguage(language)) { + if (language && this.debugService.getAdapterManager().isDebuggerInterestedInLanguage(language)) { this.debugStartLanguageContext.set(language); this.debuggerInterestedContext.set(true); - storageSevice.store(debugStartLanguageKey, language, StorageScope.WORKSPACE); + storageSevice.store(debugStartLanguageKey, language, StorageScope.WORKSPACE, StorageTarget.MACHINE); return; } } @@ -88,7 +88,7 @@ export class WelcomeView extends ViewPane { setContextKey(); })); - this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey)); + this._register(this.debugService.getAdapterManager().onDidRegisterDebugger(setContextKey)); this._register(this.onDidChangeBodyVisibility(visible => { if (visible) { setContextKey(); @@ -117,7 +117,6 @@ let debugKeybindingLabel = ''; viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'runAndDebugAction', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID), - preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR], when: CONTEXT_DEBUGGERS_AVAILABLE, group: ViewContentGroups.Debug }); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 782bb5af..8f171554 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -64,6 +64,7 @@ export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey('debugSetVariableSupported', false); export const CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED = new RawContextKey('breakWhenValueChangesSupported', false); export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey('variableEvaluateNamePresent', false); +export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey('exceptionWidgetVisible', false); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint'; @@ -379,6 +380,7 @@ export interface IBaseBreakpoint extends IEnablement { readonly logMessage?: string; readonly verified: boolean; readonly supported: boolean; + readonly sessionsThatVerified: string[]; getIdFromAdapter(sessionId: string): number | undefined; } @@ -400,6 +402,7 @@ export interface IFunctionBreakpoint extends IBaseBreakpoint { export interface IExceptionBreakpoint extends IEnablement { readonly filter: string; readonly label: string; + readonly condition: string | undefined; } export interface IDataBreakpoint extends IBaseBreakpoint { @@ -434,9 +437,9 @@ export interface IViewModel extends ITreeElement { readonly focusedStackFrame: IStackFrame | undefined; getSelectedExpression(): IExpression | undefined; - getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined; + getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined; setSelectedExpression(expression: IExpression | undefined): void; - setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void; + setSelectedBreakpoint(functionBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void; updateViews(): void; isMultiSessionView(): boolean; @@ -615,8 +618,6 @@ export interface IPlatformSpecificAdapterContribution { export interface IDebuggerContribution extends IPlatformSpecificAdapterContribution { type: string; label?: string; - // debug adapter executable - adapterExecutableCommand?: string; win?: IPlatformSpecificAdapterContribution; winx86?: IPlatformSpecificAdapterContribution; windows?: IPlatformSpecificAdapterContribution; @@ -628,7 +629,7 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut // supported languages languages?: string[]; - enableBreakpointsFor?: { languageIds: string[] }; + enableBreakpointsFor?: { languageIds?: string[] }; // debug configuration support configurationAttributes?: any; @@ -643,7 +644,6 @@ export interface IDebugConfigurationProvider { resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; resolveDebugConfigurationWithSubstitutedVariables?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; provideDebugConfigurations?(folderUri: uri | undefined, token: CancellationToken): Promise; - debugAdapterExecutable?(folderUri: uri | undefined): Promise; // TODO@AW legacy } export interface IDebugAdapterDescriptorFactory { @@ -660,57 +660,54 @@ export interface ITerminalLauncher { } export interface IConfigurationManager { - /** - * Returns true if breakpoints can be set for a given editor model. Depends on mode. - */ - canSetBreakpointsIn(model: EditorIModel): boolean; /** * Returns an object containing the selected launch configuration and the selected configuration name. Both these fields can be null (no folder workspace). */ readonly selectedConfiguration: { launch: ILaunch | undefined; - config: IConfig | undefined; + // Potentially activates extensions + getConfig: () => Promise; name: string | undefined; // Type is used when matching dynamic configurations to their corresponding provider type: string | undefined; }; - selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, type?: string): Promise; + selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfigOptions?: { type?: string }): Promise; getLaunches(): ReadonlyArray; - - hasDebuggers(): boolean; - getLaunch(workspaceUri: uri | undefined): ILaunch | undefined; - getAllConfigurations(): { launch: ILaunch, name: string, presentation?: IConfigPresentation }[]; + getRecentDynamicConfigurations(): { name: string, type: string }[]; /** * Allows to register on change of selected debug configuration. */ onDidSelectConfiguration: Event; - onDidRegisterDebugger: Event; - - activateDebuggers(activationEvent: string, debugType?: string): Promise; - - isDebuggerInterestedInLanguage(language: string): boolean; hasDebugConfigurationProvider(debugType: string): boolean; getDynamicProviders(): Promise<{ label: string, type: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[]>; registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void; - registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable; - unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void; - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any, token: CancellationToken): Promise; +} + +export interface IAdapterManager { + + onDidRegisterDebugger: Event; + + hasDebuggers(): boolean; getDebugAdapterDescriptor(session: IDebugSession): Promise; getDebuggerLabel(type: string): string | undefined; + isDebuggerInterestedInLanguage(language: string): boolean; + activateDebuggers(activationEvent: string, debugType?: string): Promise; registerDebugAdapterFactory(debugTypes: string[], debugAdapterFactory: IDebugAdapterFactory): IDisposable; createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined; + registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable; + unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void; substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise; runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments): Promise; @@ -795,15 +792,25 @@ export interface IDebugService { onDidEndSession: Event; /** - * Gets the current configuration manager. + * Gets the configuration manager. */ getConfigurationManager(): IConfigurationManager; + /** + * Gets the adapter manager. + */ + getAdapterManager(): IAdapterManager; + /** * Sets the focused stack frame and evaluates all expressions against the newly focused stack frame, */ focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise; + /** + * Returns true if breakpoints can be set for a given editor model. Depends on mode. + */ + canSetBreakpointsIn(model: EditorIModel): boolean; + /** * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes. */ @@ -860,6 +867,8 @@ export interface IDebugService { */ removeDataBreakpoints(id?: string): Promise; + setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise; + /** * Sends all breakpoints to the passed session. * If session is not passed, sends all breakpoints to each session. @@ -932,6 +941,7 @@ export const enum BreakpointWidgetContext { export interface IDebugEditorContribution extends editorCommon.IEditorContribution { showHover(range: Range, focus: boolean): Promise; addLaunchConfiguration(): Promise; + closeExceptionWidget(): void; } export interface IBreakpointEditorContribution extends editorCommon.IEditorContribution { diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 1d203c90..b98b3b89 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -393,6 +393,7 @@ export class Thread implements IThread { private callStackCancellationTokens: CancellationTokenSource[] = []; public stoppedDetails: IRawStoppedDetails | undefined; public stopped: boolean; + public reachedEndOfCallStack = false; constructor(public session: IDebugSession, public name: string, public threadId: number) { this.callStack = []; @@ -445,6 +446,7 @@ export class Thread implements IThread { if (this.stopped) { const start = this.callStack.length; const callStack = await this.getCallStackImpl(start, levels); + this.reachedEndOfCallStack = callStack.length < levels; if (start < this.callStack.length) { // Set the stack frames for exact position we requested. To make sure no concurrent requests create duplicate stack frames #30660 this.callStack.splice(start, this.callStack.length - start); @@ -613,6 +615,17 @@ export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoi return this.data ? this.data.verified : true; } + get sessionsThatVerified() { + const sessionIds: string[] = []; + for (const [sessionId, data] of this.sessionData) { + if (data.verified) { + sessionIds.push(sessionId); + } + } + + return sessionIds; + } + abstract get supported(): boolean; getIdFromAdapter(sessionId: string): number | undefined { @@ -847,7 +860,7 @@ export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpoint { - constructor(public filter: string, public label: string, enabled: boolean) { + constructor(public filter: string, public label: string, enabled: boolean, public supportsCondition: boolean, public condition: string | undefined) { super(enabled, generateUuid()); } @@ -856,6 +869,8 @@ export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpo result.filter = this.filter; result.label = this.label; result.enabled = this.enabled; + result.supportsCondition = this.supportsCondition; + result.condition = this.condition; return result; } @@ -1049,19 +1064,24 @@ export class DebugModel implements IDebugModel { setExceptionBreakpoints(data: DebugProtocol.ExceptionBreakpointsFilter[]): void { if (data) { - if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) => exbp.filter === data[i].filter && exbp.label === data[i].label)) { + if (this.exceptionBreakpoints.length === data.length && this.exceptionBreakpoints.every((exbp, i) => exbp.filter === data[i].filter && exbp.label === data[i].label && exbp.supportsCondition === data[i].supportsCondition)) { // No change return; } this.exceptionBreakpoints = data.map(d => { const ebp = this.exceptionBreakpoints.filter(ebp => ebp.filter === d.filter).pop(); - return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default); + return new ExceptionBreakpoint(d.filter, d.label, ebp ? ebp.enabled : !!d.default, !!d.supportsCondition, ebp?.condition); }); this._onDidChangeBreakpoints.fire(undefined); } } + setExceptionBreakpointCondition(exceptionBreakpoint: IExceptionBreakpoint, condition: string | undefined): void { + (exceptionBreakpoint as ExceptionBreakpoint).condition = condition; + this._onDidChangeBreakpoints.fire(undefined); + } + areBreakpointsActivated(): boolean { return this.breakpointsActivated; } diff --git a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts index 0f0374d5..f11bab4f 100644 --- a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts @@ -16,7 +16,7 @@ declare module DebugProtocol { /** Message type. Values: 'request', 'response', 'event', etc. */ - type: string; + type: 'request' | 'response' | 'event' | string; } /** A client or debug adapter initiated request. */ @@ -56,7 +56,7 @@ declare module DebugProtocol { 'cancelled': request was cancelled. etc. */ - message?: string; + message?: 'cancelled' | string; /** Contains request result if success is true and optional error details if success is false. */ body?: any; } @@ -129,7 +129,7 @@ declare module DebugProtocol { For backward compatibility this string is shown in the UI if the 'description' attribute is missing (but it must not be translated). Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint', 'instruction breakpoint', etc. */ - reason: string; + reason: 'step' | 'breakpoint' | 'exception' | 'pause' | 'entry' | 'goto' | 'function breakpoint' | 'data breakpoint' | 'instruction breakpoint' | string; /** The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated. */ description?: string; /** The thread which was stopped. */ @@ -194,7 +194,7 @@ declare module DebugProtocol { /** The reason for the event. Values: 'started', 'exited', etc. */ - reason: string; + reason: 'started' | 'exited' | string; /** The identifier of the thread. */ threadId: number; }; @@ -209,7 +209,7 @@ declare module DebugProtocol { /** The output category. If not specified, 'console' is assumed. Values: 'console', 'stdout', 'stderr', 'telemetry', etc. */ - category?: string; + category?: 'console' | 'stdout' | 'stderr' | 'telemetry' | string; /** The output to report. */ output: string; /** Support for keeping an output log organized by grouping related messages. @@ -221,7 +221,7 @@ declare module DebugProtocol { A non empty 'output' attribute is shown as the unindented end of the group. */ group?: 'start' | 'startCollapsed' | 'end'; - /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */ + /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference?: number; /** An optional source location where the output was produced. */ source?: Source; @@ -243,7 +243,7 @@ declare module DebugProtocol { /** The reason for the event. Values: 'changed', 'new', 'removed', etc. */ - reason: string; + reason: 'changed' | 'new' | 'removed' | string; /** The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values. */ breakpoint: Breakpoint; }; @@ -383,7 +383,7 @@ declare module DebugProtocol { export interface InvalidatedEvent extends Event { // event: 'invalidated'; body: { - /** Optional set of logical areas that got invalidated. If this property is missing or empty, a single value 'all' is assumed. */ + /** Optional set of logical areas that got invalidated. This property has a hint characteristic: a client can only be expected to make a 'best effort' in honouring the areas but there are no guarantees. If this property is missing, empty, or if values are not understand the client should assume a single value 'all'. */ areas?: InvalidatedAreas[]; /** If specified, the client only needs to refetch data related to this thread. */ threadId?: number; @@ -408,7 +408,7 @@ declare module DebugProtocol { kind?: 'integrated' | 'external'; /** Optional title of the terminal. */ title?: string; - /** Working directory of the command. */ + /** Working directory for the command. For non-empty, valid paths this typically results in execution of a change directory command. */ cwd: string; /** List of arguments. The first argument is the command to run. */ args: string[]; @@ -419,9 +419,9 @@ declare module DebugProtocol { /** Response to 'runInTerminal' request. */ export interface RunInTerminalResponse extends Response { body: { - /** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */ + /** The process ID. The value should be less than or equal to 2147483647 (2^31-1). */ processId?: number; - /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */ + /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31-1). */ shellProcessId?: number; }; } @@ -455,7 +455,7 @@ declare module DebugProtocol { /** Determines in what format paths are specified. The default is 'path', which is the native format. Values: 'path', 'uri', etc. */ - pathFormat?: string; + pathFormat?: 'path' | 'uri' | string; /** Client supports the optional type attribute for variables. */ supportsVariableType?: boolean; /** Client supports the paging of variables. */ @@ -712,8 +712,10 @@ declare module DebugProtocol { /** Arguments for 'setExceptionBreakpoints' request. */ export interface SetExceptionBreakpointsArguments { - /** IDs of checked exception options. The set of IDs is returned via the 'exceptionBreakpointFilters' capability. */ + /** Set of exception filters specified by their ID. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. The 'filter' and 'filterOptions' sets are additive. */ filters: string[]; + /** Set of exception filters and their options. The set of all possible exception filters is defined by the 'exceptionBreakpointFilters' capability. This attribute is only honored by a debug adapter if the capability 'supportsExceptionFilterOptions' is true. The 'filter' and 'filterOptions' sets are additive. */ + filterOptions?: ExceptionFilterOptions[]; /** Configuration options for selected exceptions. The attribute is only honored by a debug adapter if the capability 'supportsExceptionOptions' is true. */ @@ -1009,7 +1011,8 @@ declare module DebugProtocol { } /** StackTrace request; value of command field is 'stackTrace'. - The request returns a stacktrace from the current execution state. + The request returns a stacktrace from the current execution state of a given thread. + A client can request all stack frames by omitting the startFrame and levels arguments. For performance conscious clients stack frames can be retrieved in a piecemeal way with the startFrame and levels arguments. The response of the stackTrace request may contain a totalFrames property that hints at the total number of frames in the stack. If a client needs this total number upfront, it can issue a request for a single (first) frame and depending on the value of totalFrames decide how to proceed. In any case a client should be prepared to receive less frames than requested, which is an indication that the end of the stack has been reached. */ export interface StackTraceRequest extends Request { // command: 'stackTrace'; @@ -1037,7 +1040,7 @@ declare module DebugProtocol { This means that there is no location information available. */ stackFrames: StackFrame[]; - /** The total number of frames available. */ + /** The total number of frames available in the stack. If omitted or if totalFrames is larger than the available frames, a client is expected to request frames until a request returns less frames than requested (which indicates the end of the stack). Returning monotonically increasing totalFrames values for subsequent requests can be used to enforce paging in the client. */ totalFrames?: number; }; } @@ -1125,17 +1128,17 @@ declare module DebugProtocol { /** The type of the new value. Typically shown in the UI when hovering over the value. */ type?: string; /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference?: number; /** The number of named child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ namedVariables?: number; /** The number of indexed child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; }; @@ -1275,7 +1278,7 @@ declare module DebugProtocol { The attribute is only honored by a debug adapter if the capability 'supportsClipboardContext' is true. etc. */ - context?: string; + context?: 'watch' | 'repl' | 'hover' | 'clipboard' | string; /** Specifies details on how to format the Evaluate result. The attribute is only honored by a debug adapter if the capability 'supportsValueFormattingOptions' is true. */ @@ -1294,17 +1297,17 @@ declare module DebugProtocol { /** Properties of a evaluate result that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference: number; /** The number of named child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ namedVariables?: number; /** The number of indexed child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; /** Optional memory reference to a location appropriate for this result. @@ -1349,17 +1352,17 @@ declare module DebugProtocol { /** Properties of a value that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ variablesReference?: number; /** The number of named child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ namedVariables?: number; /** The number of indexed child variables. The client can use this optional information to present the variables in a paged UI and fetch them in chunks. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ indexedVariables?: number; }; @@ -1556,7 +1559,7 @@ declare module DebugProtocol { supportsHitConditionalBreakpoints?: boolean; /** The debug adapter supports a (side effect free) evaluate request for data hovers. */ supportsEvaluateForHovers?: boolean; - /** Available filters or options for the setExceptionBreakpoints request. */ + /** Available exception filter options for the 'setExceptionBreakpoints' request. */ exceptionBreakpointFilters?: ExceptionBreakpointsFilter[]; /** The debug adapter supports stepping back via the 'stepBack' and 'reverseContinue' requests. */ supportsStepBack?: boolean; @@ -1616,16 +1619,20 @@ declare module DebugProtocol { supportsSteppingGranularity?: boolean; /** The debug adapter supports adding breakpoints based on instruction references. */ supportsInstructionBreakpoints?: boolean; + /** The debug adapter supports 'filterOptions' as an argument on the 'setExceptionBreakpoints' request. */ + supportsExceptionFilterOptions?: boolean; } - /** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */ + /** An ExceptionBreakpointsFilter is shown in the UI as an filter option for configuring how exceptions are dealt with. */ export interface ExceptionBreakpointsFilter { - /** The internal ID of the filter. This value is passed to the setExceptionBreakpoints request. */ + /** The internal ID of the filter option. This value is passed to the 'setExceptionBreakpoints' request. */ filter: string; - /** The name of the filter. This will be shown in the UI. */ + /** The name of the filter option. This will be shown in the UI. */ label: string; - /** Initial value of the filter. If not specified a value 'false' is assumed. */ + /** Initial value of the filter option. If not specified a value 'false' is assumed. */ default?: boolean; + /** Controls whether a condition can be specified for this filter option. If false or missing, a condition can not be set. */ + supportsCondition?: boolean; } /** A structured message object. Used to return errors from requests. */ @@ -1730,7 +1737,7 @@ declare module DebugProtocol { path?: string; /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. - The value should be less than or equal to 2147483647 (2^31 - 1). + The value should be less than or equal to 2147483647 (2^31-1). */ sourceReference?: number; /** An optional hint for how to present the source in the UI. @@ -1788,7 +1795,7 @@ declare module DebugProtocol { 'registers': Scope contains registers. Only a single 'registers' scope should be returned from a 'scopes' request. etc. */ - presentationHint?: string; + presentationHint?: 'arguments' | 'locals' | 'registers' | string; /** The variables of this scope can be retrieved by passing the value of variablesReference to the VariablesRequest. */ variablesReference: number; /** The number of named variables in this scope. @@ -1867,7 +1874,7 @@ declare module DebugProtocol { 'dataBreakpoint': Indicates that a data breakpoint is registered for the object. etc. */ - kind?: string; + kind?: 'property' | 'method' | 'class' | 'data' | 'event' | 'baseClass' | 'innerClass' | 'interface' | 'mostDerivedClass' | 'virtual' | 'dataBreakpoint' | string; /** Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values. Values: 'static': Indicates that the object is static. @@ -1879,11 +1886,11 @@ declare module DebugProtocol { 'hasSideEffects': Indicates that the evaluation had side effects. etc. */ - attributes?: string[]; + attributes?: ('static' | 'constant' | 'readOnly' | 'rawString' | 'hasObjectId' | 'canHaveObjectId' | 'hasSideEffects' | string)[]; /** Visibility of variable. Before introducing additional values, try to use the listed values. Values: 'public', 'private', 'protected', 'internal', 'final', etc. */ - visibility?: string; + visibility?: 'public' | 'private' | 'protected' | 'internal' | 'final' | string; } /** Properties of a breakpoint location returned from the 'breakpointLocations' request. */ @@ -2108,6 +2115,16 @@ declare module DebugProtocol { includeAll?: boolean; } + /** An ExceptionFilterOptions is used to specify an exception filter together with a condition for the setExceptionsFilter request. */ + export interface ExceptionFilterOptions { + /** ID of an exception filter returned by the 'exceptionBreakpointFilters' capability. */ + filterId: string; + /** An optional expression for conditional exceptions. + The exception will break into the debugger if the result of the condition is true. + */ + condition?: string; + } + /** An ExceptionOptions assigns configuration options to a set of exceptions. */ export interface ExceptionOptions { /** A path that selects a single or multiple exceptions in a tree. If 'path' is missing, the whole tree is selected. @@ -2179,11 +2196,13 @@ declare module DebugProtocol { } /** Logical areas that can be invalidated by the 'invalidated' event. + Values: 'all': All previously fetched data has become invalid and needs to be refetched. 'stacks': Previously fetched stack related data has become invalid and needs to be refetched. 'threads': Previously fetched thread related data has become invalid and needs to be refetched. 'variables': Previously fetched variable data has become invalid and needs to be refetched. + etc. */ - export type InvalidatedAreas = 'all' | 'stacks' | 'threads' | 'variables'; + export type InvalidatedAreas = 'all' | 'stacks' | 'threads' | 'variables' | string; } diff --git a/src/vs/workbench/contrib/debug/common/debugStorage.ts b/src/vs/workbench/contrib/debug/common/debugStorage.ts index 6cabc0ba..42583a61 100644 --- a/src/vs/workbench/contrib/debug/common/debugStorage.ts +++ b/src/vs/workbench/contrib/debug/common/debugStorage.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage'; import { ExceptionBreakpoint, Expression, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IEvaluate, IExpression, IDebugModel } from 'vs/workbench/contrib/debug/common/debug'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -49,7 +49,7 @@ export class DebugStorage { let result: ExceptionBreakpoint[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => { - return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled); + return new ExceptionBreakpoint(exBreakpoint.filter, exBreakpoint.label, exBreakpoint.enabled, exBreakpoint.supportsCondition, exBreakpoint.condition); }); } catch (e) { } @@ -80,7 +80,7 @@ export class DebugStorage { storeWatchExpressions(watchExpressions: (IExpression & IEvaluate)[]): void { if (watchExpressions.length) { - this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE); } @@ -89,28 +89,28 @@ export class DebugStorage { storeBreakpoints(debugModel: IDebugModel): void { const breakpoints = debugModel.getBreakpoints(); if (breakpoints.length) { - this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } const functionBreakpoints = debugModel.getFunctionBreakpoints(); if (functionBreakpoints.length) { - this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } const dataBreakpoints = debugModel.getDataBreakpoints().filter(dbp => dbp.canPersist); if (dataBreakpoints.length) { - this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } const exceptionBreakpoints = debugModel.getExceptionBreakpoints(); if (exceptionBreakpoints.length) { - this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/contrib/debug/common/debugViewModel.ts b/src/vs/workbench/contrib/debug/common/debugViewModel.ts index 7a4b1fba..e7541cd8 100644 --- a/src/vs/workbench/contrib/debug/common/debugViewModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; +import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, IExceptionBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils'; @@ -16,7 +16,7 @@ export class ViewModel implements IViewModel { private _focusedSession: IDebugSession | undefined; private _focusedThread: IThread | undefined; private selectedExpression: IExpression | undefined; - private selectedFunctionBreakpoint: IFunctionBreakpoint | undefined; + private selectedBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined; private readonly _onDidFocusSession = new Emitter(); private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>(); private readonly _onDidSelectExpression = new Emitter(); @@ -112,8 +112,8 @@ export class ViewModel implements IViewModel { return this._onDidSelectExpression.event; } - getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined { - return this.selectedFunctionBreakpoint; + getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined { + return this.selectedBreakpoint; } updateViews(): void { @@ -124,9 +124,9 @@ export class ViewModel implements IViewModel { return this._onWillUpdateViews.event; } - setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void { - this.selectedFunctionBreakpoint = functionBreakpoint; - this.breakpointSelectedContextKey.set(!!functionBreakpoint); + setSelectedBreakpoint(breakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void { + this.selectedBreakpoint = breakpoint; + this.breakpointSelectedContextKey.set(!!breakpoint); } isMultiSessionView(): boolean { diff --git a/src/vs/workbench/contrib/debug/common/debugger.ts b/src/vs/workbench/contrib/debug/common/debugger.ts index ae8bc52d..b60cda04 100644 --- a/src/vs/workbench/contrib/debug/common/debugger.ts +++ b/src/vs/workbench/contrib/debug/common/debugger.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import { isObject } from 'vs/base/common/types'; import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { IConfig, IDebuggerContribution, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, IDebugger, IDebugSession, IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug'; +import { IConfig, IDebuggerContribution, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IDebugAdapter, IDebugger, IDebugSession, IDebugHelperService, IAdapterManager, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils'; @@ -30,14 +30,15 @@ export class Debugger implements IDebugger { private mainExtensionDescription: IExtensionDescription | undefined; constructor( - private configurationManager: IConfigurationManager, + private adapterManager: IAdapterManager, dbgContribution: IDebuggerContribution, extensionDescription: IExtensionDescription, @IConfigurationService private readonly configurationService: IConfigurationService, @ITextResourcePropertiesService private readonly resourcePropertiesService: ITextResourcePropertiesService, @IConfigurationResolverService private readonly configurationResolverService: IConfigurationResolverService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IDebugHelperService private readonly debugHelperService: IDebugHelperService + @IDebugHelperService private readonly debugHelperService: IDebugHelperService, + @IDebugService private readonly debugService: IDebugService ) { this.debuggerContribution = { type: dbgContribution.type }; this.merge(dbgContribution, extensionDescription); @@ -97,8 +98,8 @@ export class Debugger implements IDebugger { } createDebugAdapter(session: IDebugSession): Promise { - return this.configurationManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => { - const da = this.configurationManager.createDebugAdapter(session); + return this.adapterManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => { + const da = this.adapterManager.createDebugAdapter(session); if (da) { return Promise.resolve(da); } @@ -107,13 +108,13 @@ export class Debugger implements IDebugger { } substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise { - return this.configurationManager.substituteVariables(this.type, folder, config).then(config => { + return this.adapterManager.substituteVariables(this.type, folder, config).then(config => { return this.configurationResolverService.resolveWithInteractionReplace(folder, config, 'launch', this.variables, config.__configurationTarget); }); } runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { - return this.configurationManager.runInTerminal(this.type, args); + return this.adapterManager.runInTerminal(this.type, args); } get label(): string { @@ -141,7 +142,7 @@ export class Debugger implements IDebugger { } hasConfigurationProvider(): boolean { - return this.configurationManager.hasDebugConfigurationProvider(this.type); + return this.debugService.getConfigurationManager().hasDebugConfigurationProvider(this.type); } getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise { diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index 04297b0e..f10608a0 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -11,12 +11,16 @@ import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; const MAX_REPL_LENGTH = 10000; let topReplElementCounter = 0; export class SimpleReplElement implements IReplElement { + + private _count = 1; + private _onDidChangeCount = new Emitter(); + constructor( public session: IDebugSession, private id: string, @@ -33,6 +37,19 @@ export class SimpleReplElement implements IReplElement { getId(): string { return this.id; } + + set count(value: number) { + this._count = value; + this._onDidChangeCount.fire(); + } + + get count(): number { + return this._count; + } + + get onDidChangeCount(): Event { + return this._onDidChangeCount.event; + } } export class RawObjectReplElement implements IExpression { @@ -202,13 +219,21 @@ export class ReplModel { if (typeof data === 'string') { const previousElement = this.replElements.length ? this.replElements[this.replElements.length - 1] : undefined; - if (previousElement instanceof SimpleReplElement && previousElement.severity === sev && !previousElement.value.endsWith('\n') && !previousElement.value.endsWith('\r\n')) { - previousElement.value += data; - this._onDidChangeElements.fire(); - } else { - const element = new SimpleReplElement(session, `topReplElement:${topReplElementCounter++}`, data, sev, source); - this.addReplElement(element); + if (previousElement instanceof SimpleReplElement && previousElement.severity === sev) { + if (previousElement.value === data) { + previousElement.count++; + // No need to fire an event, just the count updates and badge will adjust automatically + return; + } + if (!previousElement.value.endsWith('\n') && !previousElement.value.endsWith('\r\n') && previousElement.count === 1) { + previousElement.value += data; + this._onDidChangeElements.fire(); + return; + } } + + const element = new SimpleReplElement(session, `topReplElement:${topReplElementCounter++}`, data, sev, source); + this.addReplElement(element); } else { // TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression (data).severity = sev; diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index dc6d941a..d8ba9f71 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -199,9 +199,9 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { "Cannot determine executable for debug adapter '{0}'.", this.debugType)); } - let env = objects.mixin({}, process.env); - if (options.env) { - env = objects.mixin(env, options.env); + let env = process.env; + if (options.env && Object.keys(options.env).length > 0) { + env = objects.mixin(objects.deepClone(process.env), options.env); } if (command === 'node') { @@ -263,6 +263,8 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { channel.append(sanitize(data)); } }); + } else { + this.serverProcess.stderr!.resume(); } // finally connect to the DA diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index 0c27cb6d..45a8fc65 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -9,6 +9,7 @@ import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExtern import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; +import { extractDriveLetter } from 'vs/base/common/labels'; let externalTerminalService: IExternalTerminalService | undefined = undefined; @@ -111,7 +112,11 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? }; if (cwd) { - command += `cd '${cwd}'; `; + const driveLetter = extractDriveLetter(cwd); + if (driveLetter) { + command += `${driveLetter}:; `; + } + command += `cd ${quote(cwd)}; `; } if (env) { for (let key in env) { @@ -140,6 +145,10 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? }; if (cwd) { + const driveLetter = extractDriveLetter(cwd); + if (driveLetter) { + command += `${driveLetter}: && `; + } command += `cd ${quote(cwd)} && `; } if (env) { diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index aece705d..1a2bed97 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { URI as uri } from 'vs/base/common/uri'; import { DebugModel, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; -import { getExpandedBodySize, getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { getExpandedBodySize, getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { dispose } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { IBreakpointData, IBreakpointUpdateData, State } from 'vs/workbench/contrib/debug/common/debug'; @@ -258,40 +258,40 @@ suite('Debug - Breakpoints', () => { ]); const breakpoints = model.getBreakpoints(); - let result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[0]); - assert.equal(result.message, 'Expression: x > 5'); - assert.equal(result.className, 'codicon-debug-breakpoint-conditional'); + let result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0]); + assert.equal(result.message, 'Expression condition: x > 5'); + assert.equal(result.icon.id, 'debug-breakpoint-conditional'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[1]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[1]); assert.equal(result.message, 'Disabled Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-disabled'); + assert.equal(result.icon.id, 'debug-breakpoint-disabled'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[2]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2]); assert.equal(result.message, 'Log Message: hello'); - assert.equal(result.className, 'codicon-debug-breakpoint-log'); + assert.equal(result.icon.id, 'debug-breakpoint-log'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[3]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[3]); assert.equal(result.message, 'Hit Count: 12'); - assert.equal(result.className, 'codicon-debug-breakpoint-conditional'); + assert.equal(result.icon.id, 'debug-breakpoint-conditional'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[4]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[4]); assert.equal(result.message, 'Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint'); + assert.equal(result.icon.id, 'debug-breakpoint'); - result = getBreakpointMessageAndClassName(State.Stopped, false, breakpoints[2]); + result = getBreakpointMessageAndIcon(State.Stopped, false, breakpoints[2]); assert.equal(result.message, 'Disabled Logpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-log-disabled'); + assert.equal(result.icon.id, 'debug-breakpoint-log-disabled'); model.addDataBreakpoint('label', 'id', true, ['read']); const dataBreakpoints = model.getDataBreakpoints(); - result = getBreakpointMessageAndClassName(State.Stopped, true, dataBreakpoints[0]); + result = getBreakpointMessageAndIcon(State.Stopped, true, dataBreakpoints[0]); assert.equal(result.message, 'Data Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-data'); + assert.equal(result.icon.id, 'debug-breakpoint-data'); const functionBreakpoint = model.addFunctionBreakpoint('foo', '1'); - result = getBreakpointMessageAndClassName(State.Stopped, true, functionBreakpoint); + result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint); assert.equal(result.message, 'Function Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-function'); + assert.equal(result.icon.id, 'debug-breakpoint-function'); const data = new Map(); data.set(breakpoints[0].getId(), { verified: false, line: 10 }); @@ -300,17 +300,17 @@ suite('Debug - Breakpoints', () => { data.set(functionBreakpoint.getId(), { verified: true }); model.setBreakpointSessionData('mocksessionid', { supportsFunctionBreakpoints: false, supportsDataBreakpoints: true, supportsLogPoints: true }, data); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[0]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0]); assert.equal(result.message, 'Unverified Breakpoint'); - assert.equal(result.className, 'codicon-debug-breakpoint-unverified'); + assert.equal(result.icon.id, 'debug-breakpoint-unverified'); - result = getBreakpointMessageAndClassName(State.Stopped, true, functionBreakpoint); + result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint); assert.equal(result.message, 'Function breakpoints not supported by this debug type'); - assert.equal(result.className, 'codicon-debug-breakpoint-function-unverified'); + assert.equal(result.icon.id, 'debug-breakpoint-function-unverified'); - result = getBreakpointMessageAndClassName(State.Stopped, true, breakpoints[2]); + result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2]); assert.equal(result.message, 'Log Message: hello, world'); - assert.equal(result.className, 'codicon-debug-breakpoint-log'); + assert.equal(result.icon.id, 'debug-breakpoint-log'); }); test('decorations', () => { @@ -337,7 +337,7 @@ suite('Debug - Breakpoints', () => { assert.equal(decorations[0].options.beforeContentClassName, undefined); assert.equal(decorations[1].options.beforeContentClassName, `debug-breakpoint-placeholder`); assert.equal(decorations[0].options.overviewRuler?.position, OverviewRulerLane.Left); - const expected = new MarkdownString().appendCodeblock(languageIdentifier.language, 'Expression: x > 5'); + const expected = new MarkdownString().appendCodeblock(languageIdentifier.language, 'Expression condition: x > 5'); assert.deepEqual(decorations[0].options.glyphMarginHoverMessage, expected); decorations = createBreakpointDecorations(textModel, breakpoints, State.Running, true, false); diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index ba1d0fa3..ca7c0db5 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -17,6 +17,8 @@ import { Constants } from 'vs/base/common/uint'; import { getContext, getContextForContributedActions, getSpecificSourceName } from 'vs/workbench/contrib/debug/browser/callStackView'; import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug/browser/debugService'; import { generateUuid } from 'vs/base/common/uuid'; +import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession { return new DebugSession(generateUuid(), { resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, { @@ -308,7 +310,7 @@ suite('Debug - CallStack', () => { let decorations = createDecorationsForStackFrame(firstStackFrame, firstStackFrame.range, true); assert.equal(decorations.length, 2); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); - assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe'); + assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframe)); assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1)); assert.equal(decorations[1].options.className, 'debug-top-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); @@ -316,7 +318,7 @@ suite('Debug - CallStack', () => { decorations = createDecorationsForStackFrame(secondStackFrame, firstStackFrame.range, true); assert.equal(decorations.length, 2); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); - assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe-focused'); + assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframeFocused)); assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1)); assert.equal(decorations[1].options.className, 'debug-focused-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); @@ -324,7 +326,7 @@ suite('Debug - CallStack', () => { decorations = createDecorationsForStackFrame(firstStackFrame, new Range(1, 5, 1, 6), true); assert.equal(decorations.length, 3); assert.deepEqual(decorations[0].range, new Range(1, 2, 1, 1)); - assert.equal(decorations[0].options.glyphMarginClassName, 'codicon-debug-stackframe'); + assert.equal(decorations[0].options.glyphMarginClassName, ThemeIcon.asClassName(debugStackframe)); assert.deepEqual(decorations[1].range, new Range(1, Constants.MAX_SAFE_SMALL_INTEGER, 1, 1)); assert.equal(decorations[1].options.className, 'debug-top-stack-frame-line'); assert.equal(decorations[1].options.isWholeLine, true); diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index 4a885f89..96a346ab 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -7,7 +7,7 @@ import { URI as uri } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Position, IPosition } from 'vs/editor/common/core/position'; -import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate } from 'vs/workbench/contrib/debug/common/debug'; +import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint, IDebugSessionOptions, IEvaluate, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import Severity from 'vs/base/common/severity'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; @@ -17,6 +17,7 @@ import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompou import { CancellationToken } from 'vs/base/common/cancellation'; import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { ITextModel } from 'vs/editor/common/model'; const fileService = new TestFileService(); export const mockUriIdentityService = new UriIdentityService(fileService); @@ -49,6 +50,14 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } + getAdapterManager(): IAdapterManager { + throw new Error('Method not implemented.'); + } + + canSetBreakpointsIn(model: ITextModel): boolean { + throw new Error('Method not implemented.'); + } + public focusStackFrame(focusedStackFrame: IStackFrame): Promise { throw new Error('not implemented'); } @@ -77,6 +86,10 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } + setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string): Promise { + throw new Error('Method not implemented.'); + } + public addFunctionBreakpoint(): void { } public moveWatchExpression(id: string, position: number): void { } diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index 815ec28a..288fa962 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -64,6 +64,41 @@ suite('Debug - REPL', () => { assert.equal(elements[0], '1\n'); assert.equal(elements[1], '23\n45\n'); assert.equal(elements[2], '6'); + + repl.removeReplExpressions(); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'third line', severity.Info); + elements = repl.getReplElements(); + assert.equal(elements.length, 3); + assert.equal(elements[0], 'first line\n'); + assert.equal(elements[0].count, 3); + assert.equal(elements[1], 'second line'); + assert.equal(elements[1].count, 2); + assert.equal(elements[2], 'third line'); + assert.equal(elements[2].count, 1); + }); + + test('repl output count', () => { + const session = createMockSession(model); + const repl = new ReplModel(); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'first line\n', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'second line', severity.Info); + repl.appendToRepl(session, 'third line', severity.Info); + const elements = repl.getReplElements(); + assert.equal(elements.length, 3); + assert.equal(elements[0], 'first line\n'); + assert.equal(elements[0].count, 3); + assert.equal(elements[1], 'second line'); + assert.equal(elements[1].count, 2); + assert.equal(elements[2], 'third line'); + assert.equal(elements[2].count, 1); }); test('repl merging', () => { @@ -85,7 +120,7 @@ suite('Debug - REPL', () => { assert.equal(grandChild.getReplElements().length, 1); assert.equal(child3.getReplElements().length, 0); - grandChild.appendToRepl('1\n', severity.Info); + grandChild.appendToRepl('2\n', severity.Info); assert.equal(parentChanges, 2); assert.equal(parent.getReplElements().length, 2); assert.equal(child1.getReplElements().length, 0); @@ -93,7 +128,7 @@ suite('Debug - REPL', () => { assert.equal(grandChild.getReplElements().length, 2); assert.equal(child3.getReplElements().length, 0); - child3.appendToRepl('1\n', severity.Info); + child3.appendToRepl('3\n', severity.Info); assert.equal(parentChanges, 2); assert.equal(parent.getReplElements().length, 2); assert.equal(child1.getReplElements().length, 0); @@ -101,7 +136,7 @@ suite('Debug - REPL', () => { assert.equal(grandChild.getReplElements().length, 2); assert.equal(child3.getReplElements().length, 1); - child1.appendToRepl('1\n', severity.Info); + child1.appendToRepl('4\n', severity.Info); assert.equal(parentChanges, 2); assert.equal(parent.getReplElements().length, 2); assert.equal(child1.getReplElements().length, 1); diff --git a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts index b56194b2..2a915d52 100644 --- a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts @@ -49,8 +49,8 @@ suite('Debug - ANSI Handling', () => { assert.equal(0, root.children.length); - appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session); - appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session); + appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session.root); + appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session.root); assert.equal(2, root.children.length); @@ -80,7 +80,7 @@ suite('Debug - ANSI Handling', () => { * @returns An {@link HTMLSpanElement} that contains the stylized text. */ function getSequenceOutput(sequence: string): HTMLSpanElement { - const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session); + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session.root); assert.equal(1, root.children.length); const child: Node = root.lastChild!; if (child instanceof HTMLSpanElement) { @@ -320,7 +320,7 @@ suite('Debug - ANSI Handling', () => { if (elementsExpected === undefined) { elementsExpected = assertions.length; } - const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session); + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, themeService, session.root); assert.equal(elementsExpected, root.children.length); for (let i = 0; i < elementsExpected; i++) { const child: Node = root.children[i]; diff --git a/src/vs/workbench/contrib/debug/test/node/debugger.test.ts b/src/vs/workbench/contrib/debug/test/node/debugger.test.ts index 6fda912c..68a34b4b 100644 --- a/src/vs/workbench/contrib/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/contrib/debug/test/node/debugger.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { join, normalize } from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import { IDebugAdapterExecutable, IConfigurationManager, IConfig, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugAdapterExecutable, IConfig, IDebugSession, IAdapterManager } from 'vs/workbench/contrib/debug/common/debug'; import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { URI } from 'vs/base/common/uri'; @@ -22,7 +22,6 @@ suite('Debug - Debugger', () => { const debuggerContribution = { type: 'mock', label: 'Mock Debug', - enableBreakpointsFor: { 'languageIds': ['markdown'] }, program: './out/mock/mockDebug.js', args: ['arg1', 'arg2'], configurationAttributes: { @@ -123,7 +122,7 @@ suite('Debug - Debugger', () => { }; - const configurationManager = { + const adapterManager = { getDebugAdapterDescriptor(session: IDebugSession, config: IConfig): Promise { return Promise.resolve(undefined); } @@ -133,7 +132,7 @@ suite('Debug - Debugger', () => { const testResourcePropertiesService = new TestTextResourcePropertiesService(configurationService); setup(() => { - _debugger = new Debugger(configurationManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!); + _debugger = new Debugger(adapterManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!, undefined!); }); teardown(() => { diff --git a/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts b/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts deleted file mode 100644 index 1d931423..00000000 --- a/src/vs/workbench/contrib/emmet/browser/actions/showEmmetCommands.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; - -import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; - -const EMMET_COMMANDS_PREFIX = '>Emmet: '; - -class ShowEmmetCommandsAction extends EditorAction { - - constructor() { - super({ - id: 'workbench.action.showEmmetCommands', - label: nls.localize('showEmmetCommands', "Show Emmet Commands"), - alias: 'Show Emmet Commands', - precondition: EditorContextKeys.writable, - menuOpts: { - menuId: MenuId.MenubarEditMenu, - group: '5_insert', - title: nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."), - order: 4 - } - }); - } - - public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - const quickInputService = accessor.get(IQuickInputService); - quickInputService.quickAccess.show(EMMET_COMMANDS_PREFIX); - } -} - -registerEditorAction(ShowEmmetCommandsAction); diff --git a/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts b/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts index b603e987..f29d6b2c 100644 --- a/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts +++ b/src/vs/workbench/contrib/emmet/browser/emmet.contribution.ts @@ -3,6 +3,5 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './actions/showEmmetCommands'; import './actions/expandAbbreviation'; diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index ee883ca5..a544ac4c 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -5,7 +5,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -222,7 +222,7 @@ export class ExperimentService extends Disposable implements IExperimentService const storageKey = 'experiments.' + experimentId; const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); experimentState.state = ExperimentState.Complete; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); } protected async getExperiments(): Promise { @@ -280,7 +280,7 @@ export class ExperimentService extends Disposable implements IExperimentService }); } if (enabledExperiments.length) { - this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL); + this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove('allExperiments', StorageScope.GLOBAL); } @@ -350,7 +350,7 @@ export class ExperimentService extends Disposable implements IExperimentService return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { experimentState.state = processedExperiment.state = state; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); if (state === ExperimentState.Run) { this.fireRunExperiment(processedExperiment); @@ -370,7 +370,7 @@ export class ExperimentService extends Disposable implements IExperimentService // Ensure we dont store duplicates const distinctExperiments = distinct(runExperimentIdsFromStorage); if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { - this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL); + this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -399,7 +399,7 @@ export class ExperimentService extends Disposable implements IExperimentService const key = experimentEventStorageKey(event); const record = getCurrentActivationRecord(safeParse(this.storageService.get(key, StorageScope.GLOBAL), undefined)); record.count[0]++; - this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL); + this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL, StorageTarget.MACHINE); this._experiments .filter(e => e.state === ExperimentState.Evaluating && e.raw?.condition?.activationEvent?.event === event) @@ -558,12 +558,12 @@ export class ExperimentService extends Disposable implements IExperimentService if (filePathCheck && workspaceCheck) { latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; latestExperimentState.lastEditedDate = date; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); } }); if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); if (latestExperimentState.state === ExperimentState.Run && processedExperiment.action && ExperimentActionType[processedExperiment.action.type] === ExperimentActionType.Prompt) { this.fireRunExperiment(processedExperiment); } diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts index b0e18778..584f71b2 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -64,7 +64,7 @@ suite('Experimental Prompts', () => { storageData = {}; instantiationService.stub(IStorageService, >{ get: (a: string, b: StorageScope, c?: string) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c, - store: (a, b, c) => { + store: (a, b, c, d) => { if (a === 'experiments.experiment1') { storageData = JSON.parse(b + ''); } diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts new file mode 100644 index 00000000..96c54f0a --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -0,0 +1,474 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/runtimeExtensionsEditor'; +import * as nls from 'vs/nls'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; +import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; +import { WorkbenchList } from 'vs/platform/list/browser/listService'; +import { append, $, reset, Dimension, clearNode } from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { memoize } from 'vs/base/common/decorators'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { Event } from 'vs/base/common/event'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { renderCodicons } from 'vs/base/browser/codicons'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { Schemas } from 'vs/base/common/network'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { domEvent } from 'vs/base/browser/event'; +import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; + +interface IExtensionProfileInformation { + /** + * segment when the extension was running. + * 2*i = segment start time + * 2*i+1 = segment end time + */ + segments: number[]; + /** + * total time when the extension was running. + * (sum of all segment lengths). + */ + totalTime: number; +} + +export interface IRuntimeExtension { + originalIndex: number; + description: IExtensionDescription; + marketplaceInfo: IExtension; + status: IExtensionsStatus; + profileInfo?: IExtensionProfileInformation; + unresponsiveProfile?: IExtensionHostProfile; +} + +export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { + + public static readonly ID: string = 'workbench.editor.runtimeExtensions'; + + private _list: WorkbenchList | null; + private _elements: IRuntimeExtension[] | null; + private _updateSoon: RunOnceScheduler; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService, + @IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService private readonly _extensionService: IExtensionService, + @INotificationService private readonly _notificationService: INotificationService, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ILabelService private readonly _labelService: ILabelService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + ) { + super(AbstractRuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); + + this._list = null; + this._elements = null; + this._updateSoon = this._register(new RunOnceScheduler(() => this._updateExtensions(), 200)); + + this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateSoon.schedule())); + this._updateExtensions(); + } + + protected async _updateExtensions(): Promise { + this._elements = await this._resolveExtensions(); + if (this._list) { + this._list.splice(0, this._list.length, this._elements); + } + } + + private async _resolveExtensions(): Promise { + // We only deal with extensions with source code! + const extensionsDescriptions = (await this._extensionService.getExtensions()).filter((extension) => { + return Boolean(extension.main) || Boolean(extension.browser); + }); + let marketplaceMap: { [id: string]: IExtension; } = Object.create(null); + const marketPlaceExtensions = await this._extensionsWorkbenchService.queryLocal(); + for (let extension of marketPlaceExtensions) { + marketplaceMap[ExtensionIdentifier.toKey(extension.identifier.id)] = extension; + } + + let statusMap = this._extensionService.getExtensionsStatus(); + + // group profile segments by extension + let segments: { [id: string]: number[]; } = Object.create(null); + + const profileInfo = this._getProfileInfo(); + if (profileInfo) { + let currentStartTime = profileInfo.startTime; + for (let i = 0, len = profileInfo.deltas.length; i < len; i++) { + const id = profileInfo.ids[i]; + const delta = profileInfo.deltas[i]; + + let extensionSegments = segments[ExtensionIdentifier.toKey(id)]; + if (!extensionSegments) { + extensionSegments = []; + segments[ExtensionIdentifier.toKey(id)] = extensionSegments; + } + + extensionSegments.push(currentStartTime); + currentStartTime = currentStartTime + delta; + extensionSegments.push(currentStartTime); + } + } + + let result: IRuntimeExtension[] = []; + for (let i = 0, len = extensionsDescriptions.length; i < len; i++) { + const extensionDescription = extensionsDescriptions[i]; + + let profileInfo: IExtensionProfileInformation | null = null; + if (profileInfo) { + let extensionSegments = segments[ExtensionIdentifier.toKey(extensionDescription.identifier)] || []; + let extensionTotalTime = 0; + for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) { + const startTime = extensionSegments[2 * j]; + const endTime = extensionSegments[2 * j + 1]; + extensionTotalTime += (endTime - startTime); + } + profileInfo = { + segments: extensionSegments, + totalTime: extensionTotalTime + }; + } + + result[i] = { + originalIndex: i, + description: extensionDescription, + marketplaceInfo: marketplaceMap[ExtensionIdentifier.toKey(extensionDescription.identifier)], + status: statusMap[extensionDescription.identifier.value], + profileInfo: profileInfo || undefined, + unresponsiveProfile: this._getUnresponsiveProfile(extensionDescription.identifier) + }; + } + + result = result.filter(element => element.status.activationTimes); + + // bubble up extensions that have caused slowness + + const isUnresponsive = (extension: IRuntimeExtension): boolean => + extension.unresponsiveProfile === profileInfo; + + const profileTime = (extension: IRuntimeExtension): number => + extension.profileInfo?.totalTime ?? 0; + + const activationTime = (extension: IRuntimeExtension): number => + (extension.status.activationTimes?.codeLoadingTime ?? 0) + + (extension.status.activationTimes?.activateCallTime ?? 0); + + result = result.sort((a, b) => { + if (isUnresponsive(a) || isUnresponsive(b)) { + return +isUnresponsive(b) - +isUnresponsive(a); + } else if (profileTime(a) || profileTime(b)) { + return profileTime(b) - profileTime(a); + } else if (activationTime(a) || activationTime(b)) { + return activationTime(b) - activationTime(a); + } + return a.originalIndex - b.originalIndex; + }); + + return result; + } + + protected createEditor(parent: HTMLElement): void { + parent.classList.add('runtime-extensions-editor'); + + const TEMPLATE_ID = 'runtimeExtensionElementTemplate'; + + const delegate = new class implements IListVirtualDelegate{ + getHeight(element: IRuntimeExtension): number { + return 62; + } + getTemplateId(element: IRuntimeExtension): string { + return TEMPLATE_ID; + } + }; + + interface IRuntimeExtensionTemplateData { + root: HTMLElement; + element: HTMLElement; + icon: HTMLImageElement; + name: HTMLElement; + version: HTMLElement; + msgContainer: HTMLElement; + actionbar: ActionBar; + activationTime: HTMLElement; + profileTime: HTMLElement; + disposables: IDisposable[]; + elementDisposables: IDisposable[]; + } + + const renderer: IListRenderer = { + templateId: TEMPLATE_ID, + renderTemplate: (root: HTMLElement): IRuntimeExtensionTemplateData => { + const element = append(root, $('.extension')); + const iconContainer = append(element, $('.icon-container')); + const icon = append(iconContainer, $('img.icon')); + + const desc = append(element, $('div.desc')); + const headerContainer = append(desc, $('.header-container')); + const header = append(headerContainer, $('.header')); + const name = append(header, $('div.name')); + const version = append(header, $('span.version')); + + const msgContainer = append(desc, $('div.msg')); + + const actionbar = new ActionBar(desc, { animated: false }); + actionbar.onDidRun(({ error }) => error && this._notificationService.error(error)); + + + const timeContainer = append(element, $('.time')); + const activationTime = append(timeContainer, $('div.activation-time')); + const profileTime = append(timeContainer, $('div.profile-time')); + + const disposables = [actionbar]; + + return { + root, + element, + icon, + name, + version, + actionbar, + activationTime, + profileTime, + msgContainer, + disposables, + elementDisposables: [], + }; + }, + + renderElement: (element: IRuntimeExtension, index: number, data: IRuntimeExtensionTemplateData): void => { + + data.elementDisposables = dispose(data.elementDisposables); + + data.root.classList.toggle('odd', index % 2 === 1); + + const onError = Event.once(domEvent(data.icon, 'error')); + onError(() => data.icon.src = element.marketplaceInfo.iconUrlFallback, null, data.elementDisposables); + data.icon.src = element.marketplaceInfo.iconUrl; + + if (!data.icon.complete) { + data.icon.style.visibility = 'hidden'; + data.icon.onload = () => data.icon.style.visibility = 'inherit'; + } else { + data.icon.style.visibility = 'inherit'; + } + data.name.textContent = element.marketplaceInfo.displayName; + data.version.textContent = element.description.version; + + const activationTimes = element.status.activationTimes!; + let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime; + data.activationTime.textContent = activationTimes.activationReason.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`; + + data.actionbar.clear(); + const slowExtensionAction = this._createSlowExtensionAction(element); + if (slowExtensionAction) { + data.actionbar.push(slowExtensionAction, { icon: true, label: true }); + } + if (isNonEmptyArray(element.status.runtimeErrors)) { + const reportExtensionIssueAction = this._createReportExtensionIssueAction(element); + if (reportExtensionIssueAction) { + data.actionbar.push(reportExtensionIssueAction, { icon: true, label: true }); + } + } + + let title: string; + const activationId = activationTimes.activationReason.extensionId.value; + const activationEvent = activationTimes.activationReason.activationEvent; + if (activationEvent === '*') { + title = nls.localize('starActivation', "Activated by {0} on start-up", activationId); + } else if (/^workspaceContains:/.test(activationEvent)) { + let fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); + if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { + title = nls.localize({ + key: 'workspaceContainsGlobActivation', + comment: [ + '{0} will be a glob pattern' + ] + }, "Activated by {1} because a file matching {1} exists in your workspace", fileNameOrGlob, activationId); + } else { + title = nls.localize({ + key: 'workspaceContainsFileActivation', + comment: [ + '{0} will be a file name' + ] + }, "Activated by {1} because file {0} exists in your workspace", fileNameOrGlob, activationId); + } + } else if (/^workspaceContainsTimeout:/.test(activationEvent)) { + const glob = activationEvent.substr('workspaceContainsTimeout:'.length); + title = nls.localize({ + key: 'workspaceContainsTimeout', + comment: [ + '{0} will be a glob pattern' + ] + }, "Activated by {1} because searching for {0} took too long", glob, activationId); + } else if (activationEvent === 'onStartupFinished') { + title = nls.localize({ + key: 'startupFinishedActivation', + comment: [ + 'This refers to an extension. {0} will be an activation event.' + ] + }, "Activated by {0} after start-up finished", activationId); + } else if (/^onLanguage:/.test(activationEvent)) { + let language = activationEvent.substr('onLanguage:'.length); + title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId); + } else { + title = nls.localize({ + key: 'workspaceGenericActivation', + comment: [ + 'The {0} placeholder will be an activation event, like e.g. \'language:typescript\', \'debug\', etc.' + ] + }, "Activated by {1} on {0}", activationEvent, activationId); + } + data.activationTime.title = title; + + clearNode(data.msgContainer); + + if (this._getUnresponsiveProfile(element.description.identifier)) { + const el = $('span', undefined, ...renderCodicons(` $(alert) Unresponsive`)); + el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + data.msgContainer.appendChild(el); + } + + if (isNonEmptyArray(element.status.runtimeErrors)) { + const el = $('span', undefined, ...renderCodicons(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`)); + data.msgContainer.appendChild(el); + } + + if (element.status.messages && element.status.messages.length > 0) { + const el = $('span', undefined, ...renderCodicons(`$(alert) ${element.status.messages[0].message}`)); + data.msgContainer.appendChild(el); + } + + if (element.description.extensionLocation.scheme === Schemas.vscodeRemote) { + const el = $('span', undefined, ...renderCodicons(`$(remote) ${element.description.extensionLocation.authority}`)); + data.msgContainer.appendChild(el); + + const hostLabel = this._labelService.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority); + if (hostLabel) { + reset(el, ...renderCodicons(`$(remote) ${hostLabel}`)); + } + } + + if (element.profileInfo) { + data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`; + } else { + data.profileTime.textContent = ''; + } + + }, + + disposeTemplate: (data: IRuntimeExtensionTemplateData): void => { + data.disposables = dispose(data.disposables); + } + }; + + this._list = >this._instantiationService.createInstance(WorkbenchList, + 'RuntimeExtensions', + parent, delegate, [renderer], { + multipleSelectionSupport: false, + setRowLineHeight: false, + horizontalScrolling: false, + overrideStyles: { + listBackground: editorBackground + }, + accessibilityProvider: new class implements IListAccessibilityProvider { + getWidgetAriaLabel(): string { + return nls.localize('runtimeExtensions', "Runtime Extensions"); + } + getAriaLabel(element: IRuntimeExtension): string | null { + return element.description.name; + } + } + }); + + this._list.splice(0, this._list.length, this._elements || undefined); + + this._list.onContextMenu((e) => { + if (!e.element) { + return; + } + + const actions: IAction[] = []; + + const reportExtensionIssueAction = this._createReportExtensionIssueAction(e.element); + if (reportExtensionIssueAction) { + actions.push(reportExtensionIssueAction); + actions.push(new Separator()); + } + + actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledWorkspace))); + actions.push(new Action('runtimeExtensionsEditor.action.disable', nls.localize('disable', "Disable"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledGlobally))); + actions.push(new Separator()); + + const profileAction = this._createProfileAction(); + if (profileAction) { + actions.push(profileAction); + } + const saveExtensionHostProfileAction = this.saveExtensionHostProfileAction; + if (saveExtensionHostProfileAction) { + actions.push(saveExtensionHostProfileAction); + } + + this._contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => actions + }); + }); + } + + @memoize + private get saveExtensionHostProfileAction(): IAction | null { + return this._createSaveExtensionHostProfileAction(); + } + + public layout(dimension: Dimension): void { + if (this._list) { + this._list.layout(dimension.height); + } + } + + protected abstract _getProfileInfo(): IExtensionHostProfile | null; + protected abstract _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined; + protected abstract _createSlowExtensionAction(element: IRuntimeExtension): Action | null; + protected abstract _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null; + protected abstract _createSaveExtensionHostProfileAction(): Action | null; + protected abstract _createProfileAction(): Action | null; +} + +export class ShowRuntimeExtensionsAction extends Action { + static readonly ID = 'workbench.action.showRuntimeExtensions'; + static readonly LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions"); + + constructor( + id: string, label: string, + @IEditorService private readonly _editorService: IEditorService + ) { + super(id, label); + } + + public async run(e?: any): Promise { + await this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true, pinned: true }); + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.ts new file mode 100644 index 00000000..a3b1e809 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IExtensionService, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor'; + +export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor { + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService extensionService: IExtensionService, + @INotificationService notificationService: INotificationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService storageService: IStorageService, + @ILabelService labelService: ILabelService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + ) { + super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService); + } + + protected _getProfileInfo(): IExtensionHostProfile | null { + return null; + } + + protected _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined { + return undefined; + } + + protected _createSlowExtensionAction(element: IRuntimeExtension): Action | null { + return null; + } + + protected _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null { + return null; + } + + protected _createSaveExtensionHostProfileAction(): Action | null { + return null; + } + + protected _createProfileAction(): Action | null { + return null; + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts index 87780840..87e238bf 100644 --- a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -81,7 +81,7 @@ export class DynamicWorkspaceRecommendations extends ExtensionRecommendations { const workspaceTip = workspacesTips.filter(workspaceTip => isNonEmptyArray(workspaceTip.remoteSet) && workspaceTip.remoteSet.indexOf(hashedRemote) > -1)[0]; if (workspaceTip) { this._recommendations = workspaceTip.recommendations.map(id => this.toExtensionRecommendation(id, folder)); - this.storageService.store(dynamicWorkspaceRecommendationsStorageKey, JSON.stringify({ recommendations: workspaceTip.recommendations, timestamp: Date.now() }), StorageScope.WORKSPACE); + this.storageService.store(dynamicWorkspaceRecommendationsStorageKey, JSON.stringify({ recommendations: workspaceTip.recommendations, timestamp: Date.now() }), StorageScope.WORKSPACE, StorageTarget.MACHINE); this.telemetryService.publicLog2<{ count: number, cache: number }, DynamicWorkspaceRecommendationsClassification>('dynamicWorkspaceRecommendations', { count: this._recommendations.length, cache: 0 }); return; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 16dac477..7a58b96d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -14,7 +14,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose, toDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { domEvent } from 'vs/base/browser/event'; -import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { append, $, finalHandler, join, hide, show, addDisposableListener, EventType, setParentFlowTo } from 'vs/base/browser/dom'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -27,7 +27,12 @@ import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { UpdateAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, SyncIgnoredIconAction, SetProductIconThemeAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { + UpdateAction, ReloadAction, MaliciousStatusLabelAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, + RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction, + ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, + InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction +} from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; @@ -38,7 +43,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Color } from 'vs/base/common/color'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { ExtensionsTree, ExtensionData, ExtensionsGridView, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; @@ -251,6 +256,9 @@ export class ExtensionEditor extends EditorPane { const extensionActionBar = this._register(new ActionBar(extensionActions, { animated: false, actionViewItemProvider: (action: IAction) => { + if (action instanceof ExtensionDropDownAction) { + return action.createActionViewItem(); + } if (action instanceof ActionWithDropDownAction) { return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } @@ -276,6 +284,7 @@ export class ExtensionEditor extends EditorPane { const navbar = new NavBar(body); const content = append(body, $('.content')); + content.id = generateUuid(); // An id is needed for the webview parent flow to this.template = { builtin, @@ -323,8 +332,6 @@ export class ExtensionEditor extends EditorPane { } private async updateTemplate(input: ExtensionsInput, template: IExtensionEditorTemplate, preserveFocus: boolean): Promise { - const runningExtensions = await this.extensionService.getExtensions(); - this.activeElement = null; this.editorLoadComplete = false; const extension = input.extension; @@ -405,7 +412,6 @@ export class ExtensionEditor extends EditorPane { const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction); const actions = [ reloadAction, - this.instantiationService.createInstance(SyncIgnoredIconAction), this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(UpdateAction), this.instantiationService.createInstance(SetColorThemeAction, await this.workbenchThemeService.getColorThemes()), @@ -413,13 +419,19 @@ export class ExtensionEditor extends EditorPane { this.instantiationService.createInstance(SetProductIconThemeAction, await this.workbenchThemeService.getProductIconThemes()), this.instantiationService.createInstance(EnableDropDownAction), - this.instantiationService.createInstance(DisableDropDownAction, runningExtensions), + this.instantiationService.createInstance(DisableDropDownAction), this.instantiationService.createInstance(RemoteInstallAction, false), this.instantiationService.createInstance(LocalInstallAction), + this.instantiationService.createInstance(WebInstallAction), combinedInstallAction, this.instantiationService.createInstance(InstallingLabelAction), - this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.uninstall', UninstallAction.UninstallLabel, [ + this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(InstallAnotherVersionAction), + ]), + this.instantiationService.createInstance(ToggleSyncExtensionAction), systemDisabledWarningAction, + this.instantiationService.createInstance(ExtensionEditorManageExtensionAction), this.instantiationService.createInstance(ExtensionToolTipAction, systemDisabledWarningAction, reloadAction), this.instantiationService.createInstance(MaliciousStatusLabelAction, true), ]; @@ -432,7 +444,7 @@ export class ExtensionEditor extends EditorPane { this.transientDisposables.add(disposable); } - this.setSubText(extension, reloadAction, template); + this.setSubText(extension, template); template.content.innerText = ''; // Clear content before setting navbar actions. template.navbar.clear(); @@ -463,56 +475,24 @@ export class ExtensionEditor extends EditorPane { this.editorLoadComplete = true; } - private setSubText(extension: IExtension, reloadAction: ReloadAction, template: IExtensionEditorTemplate): void { + private setSubText(extension: IExtension, template: IExtensionEditorTemplate): void { hide(template.subtextContainer); - const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction, extension); - const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction, extension); - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; - - template.ignoreActionbar.clear(); - template.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true }); - this.transientDisposables.add(ignoreAction); - this.transientDisposables.add(undoIgnoreAction); - const updateRecommendationFn = () => { const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { - ignoreAction.enabled = true; - undoIgnoreAction.enabled = false; template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; show(template.subtextContainer); } else if (this.extensionIgnoredRecommendationsService.globalIgnoredRecommendations.indexOf(extension.identifier.id.toLowerCase()) !== -1) { - ignoreAction.enabled = false; - undoIgnoreAction.enabled = true; template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); show(template.subtextContainer); } else { - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; template.subtext.textContent = ''; hide(template.subtextContainer); } }; updateRecommendationFn(); this.transientDisposables.add(this.extensionRecommendationsService.onDidChangeRecommendations(() => updateRecommendationFn())); - - this.transientDisposables.add(reloadAction.onDidChange(e => { - if (e.tooltip) { - template.subtext.textContent = reloadAction.tooltip; - show(template.subtextContainer); - ignoreAction.enabled = false; - undoIgnoreAction.enabled = false; - } - if (e.enabled === true) { - show(template.subtextContainer); - } - if (e.enabled === false) { - hide(template.subtextContainer); - } - this.layout(); - })); } clearInput(): void { @@ -558,8 +538,13 @@ export class ExtensionEditor extends EditorPane { template.content.innerText = ''; this.activeElement = null; if (id) { - this.open(id, extension, template) + const cts = new CancellationTokenSource(); + this.contentDisposables.add(toDisposable(() => cts.dispose(true))); + this.open(id, extension, template, cts.token) .then(activeElement => { + if (cts.token.isCancellationRequested) { + return; + } this.activeElement = activeElement; if (focus) { this.focus(); @@ -568,26 +553,31 @@ export class ExtensionEditor extends EditorPane { } } - private open(id: string, extension: IExtension, template: IExtensionEditorTemplate): Promise { + private open(id: string, extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { switch (id) { - case NavbarSection.Readme: return this.openReadme(template); - case NavbarSection.Contributions: return this.openContributions(template); - case NavbarSection.Changelog: return this.openChangelog(template); - case NavbarSection.Dependencies: return this.openDependencies(extension, template); + case NavbarSection.Readme: return this.openReadme(template, token); + case NavbarSection.Contributions: return this.openContributions(template, token); + case NavbarSection.Changelog: return this.openChangelog(template, token); + case NavbarSection.Dependencies: return this.openDependencies(extension, template, token); } return Promise.resolve(null); } - private async openMarkdown(cacheResult: CacheResult, noContentCopy: string, template: IExtensionEditorTemplate): Promise { + private async openMarkdown(cacheResult: CacheResult, noContentCopy: string, template: IExtensionEditorTemplate, token: CancellationToken): Promise { try { const body = await this.renderMarkdown(cacheResult, template); + if (token.isCancellationRequested) { + return Promise.resolve(null); + } const webview = this.contentDisposables.add(this.webviewService.createWebviewOverlay('extensionEditor', { enableFindWidget: true, }, {}, undefined)); - webview.claim(this); + webview.claim(this, this.scopedContextKeyService); + setParentFlowTo(webview.container, template.content); webview.layoutWebviewOverElement(template.content); + webview.html = body; this.contentDisposables.add(webview.onDidFocus(() => this.fireOnDidFocus())); @@ -828,15 +818,19 @@ export class ExtensionEditor extends EditorPane { `; } - private async openReadme(template: IExtensionEditorTemplate): Promise { + private async openReadme(template: IExtensionEditorTemplate, token: CancellationToken): Promise { const manifest = await this.extensionManifest!.get().promise; if (manifest && manifest.extensionPack && manifest.extensionPack.length) { - return this.openExtensionPackReadme(manifest, template); + return this.openExtensionPackReadme(manifest, template, token); } - return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template); + return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template, token); } - private async openExtensionPackReadme(manifest: IExtensionManifest, template: IExtensionEditorTemplate): Promise { + private async openExtensionPackReadme(manifest: IExtensionManifest, template: IExtensionEditorTemplate, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return Promise.resolve(null); + } + const extensionPackReadme = append(template.content, $('div', { class: 'extension-pack-readme' })); extensionPackReadme.style.margin = '0 auto'; extensionPackReadme.style.maxWidth = '882px'; @@ -860,21 +854,25 @@ export class ExtensionEditor extends EditorPane { const readmeContent = append(extensionPackReadme, $('div.readme-content')); await Promise.all([ - this.renderExtensionPack(manifest, extensionPackContent), - this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), { ...template, ...{ content: readmeContent } }), + this.renderExtensionPack(manifest, extensionPackContent, token), + this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), { ...template, ...{ content: readmeContent } }, token), ]); return { focus: () => extensionPackContent.focus() }; } - private openChangelog(template: IExtensionEditorTemplate): Promise { - return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template); + private openChangelog(template: IExtensionEditorTemplate, token: CancellationToken): Promise { + return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template, token); } - private openContributions(template: IExtensionEditorTemplate): Promise { + private openContributions(template: IExtensionEditorTemplate, token: CancellationToken): Promise { const content = $('div', { class: 'subcontent', tabindex: '0' }); return this.loadContents(() => this.extensionManifest!.get(), template) .then(manifest => { + if (token.isCancellationRequested) { + return null; + } + if (!manifest) { return content; } @@ -900,6 +898,7 @@ export class ExtensionEditor extends EditorPane { this.renderLocalizations(content, manifest, layout), this.renderCustomEditors(content, manifest, layout), this.renderAuthentication(content, manifest, layout), + this.renderActivationEvents(content, manifest, layout), ]; scrollableContent.scanDomNode(); @@ -914,13 +913,21 @@ export class ExtensionEditor extends EditorPane { } return content; }, () => { + if (token.isCancellationRequested) { + return null; + } + append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions"); append(template.content, content); return content; }); } - private openDependencies(extension: IExtension, template: IExtensionEditorTemplate): Promise { + private openDependencies(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return Promise.resolve(null); + } + if (arrays.isFalsyOrEmpty(extension.dependencies)) { append(template.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); return Promise.resolve(template.content); @@ -949,7 +956,11 @@ export class ExtensionEditor extends EditorPane { return Promise.resolve({ focus() { dependenciesTree.domFocus(); } }); } - private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement): Promise { + private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return; + } + const content = $('div', { class: 'subcontent' }); const scrollableContent = new DomScrollableElement(content, { useShadows: false }); append(parent, scrollableContent.getDomNode()); @@ -1414,6 +1425,21 @@ export class ExtensionEditor extends EditorPane { return true; } + private renderActivationEvents(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { + const activationEvents = manifest.activationEvents || []; + if (!activationEvents.length) { + return false; + } + + const details = $('details', { open: true, ontoggle: onDetailsToggle }, + $('summary', { tabindex: '0' }, localize('activation events', "Activation Events ({0})", activationEvents.length)), + $('ul', undefined, ...activationEvents.map(activationEvent => $('li', undefined, $('code', undefined, activationEvent)))) + ); + + append(container, details); + return true; + } + private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding | null { let key: string | undefined; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index f6aa5db5..ab902f41 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -11,14 +11,13 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; -import { INotificationHandle, INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { INotificationHandle, INotificationService, IPromptChoice, IPromptChoiceWithMenu, Severity } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -26,14 +25,6 @@ import { ITASExperimentService } from 'vs/workbench/services/experiment/common/e import { EnablementState, IWorkbenchExtensioManagementService, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -interface IExtensionsConfiguration { - autoUpdate: boolean; - autoCheckUpdates: boolean; - ignoreRecommendations: boolean; - showRecommendationsOnlyOnDemand: boolean; - closeExtensionDetailsOnViewChange: boolean; -} - type ExtensionRecommendationsNotificationClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; extensionId?: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -142,18 +133,16 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec @IWorkbenchExtensioManagementService private readonly extensionManagementService: IWorkbenchExtensioManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, ) { - storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendationStorageKey, version: 1 }); this.tasExperimentService = tasExperimentService; } hasToIgnoreRecommendationNotifications(): boolean { - const config = this.configurationService.getValue('extensions'); - return config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand; + const config = this.configurationService.getValue<{ ignoreRecommendations: boolean, showRecommendationsOnlyOnDemand?: boolean }>('extensions'); + return config.ignoreRecommendations || !!config.showRecommendationsOnlyOnDemand; } async promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string, source: RecommendationSource): Promise { @@ -207,7 +196,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec }); if (result === RecommendationsNotificationResult.Accepted) { - this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE, StorageTarget.USER); } } @@ -252,7 +241,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec { onDidInstallRecommendedExtensions, onDidShowRecommendedExtensions, onDidCancelRecommendedExtensions, onDidNeverShowRecommendedExtensionsAgain }: RecommendationsNotificationActions): CancelablePromise { return createCancelablePromise(async token => { let accepted = false; - const choices: IPromptChoice[] = []; + const choices: (IPromptChoice | IPromptChoiceWithMenu)[] = []; const installExtensions = async (isMachineScoped?: boolean) => { this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); onDidInstallRecommendedExtensions(extensions); @@ -263,14 +252,12 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec }; choices.push({ label: localize('install', "Install"), - run: () => installExtensions() - }); - if (this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions)) { - choices.push({ + run: () => installExtensions(), + menu: this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions) ? [{ label: localize('install and do no sync', "Install (Do not sync)"), run: () => installExtensions(true) - }); - } + }] : undefined, + }); choices.push(...[{ label: localize('show recommendations', "Show Recommendations"), run: async () => { @@ -423,11 +410,11 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec const importantRecommendationsIgnoreList = [...this.ignoredRecommendations]; if (!importantRecommendationsIgnoreList.includes(id.toLowerCase())) { importantRecommendationsIgnoreList.push(id.toLowerCase()); - this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL); + this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL, StorageTarget.USER); } } private setIgnoreRecommendationsConfig(configVal: boolean) { - this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); + this.configurationService.updateValue('extensions.ignoreRecommendations', configVal); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 7df6cf1b..60fa348e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -7,8 +7,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, IExtensionGalleryService, InstallOperation, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { distinct, shuffle } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; @@ -52,7 +50,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte @IInstantiationService instantiationService: IInstantiationService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, - @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @@ -92,15 +89,9 @@ export class ExtensionRecommendationsService extends Disposable implements IExte this.fileBasedRecommendations.activate(), this.experimentalRecommendations.activate(), this.keymapRecommendations.activate(), - this.lifecycleService.when(LifecyclePhase.Eventually) - .then(async () => { - if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { - await this.activateProactiveRecommendations(); - } - }) ]); - this._register(this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations(() => this._onDidChangeRecommendations.fire())); + this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations, this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations)(() => this._onDidChangeRecommendations.fire())); this._register(this.extensionRecommendationsManagementService.onDidChangeGlobalIgnoredRecommendation(({ extensionId, isRecommended }) => { if (!isRecommended) { const reason = this.getAllRecommendationsWithReason()[extensionId]; @@ -111,7 +102,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte })); await this.promptWorkspaceRecommendations(); - this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations)(() => this.promptWorkspaceRecommendations())); } private isEnabled(): boolean { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index f4b33985..839c3ffb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -6,11 +6,11 @@ import { localize } from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, registerAction2, Action2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsLocalizedLabel, ExtensionsChannelId, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -18,7 +18,7 @@ import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, - EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction + EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction, ClearExtensionsSearchResultsAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, RefreshExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; @@ -26,7 +26,7 @@ import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContrib import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeymapExtensions } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -54,7 +54,7 @@ import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; -import { CATEGORIES } from 'vs/workbench/common/actions'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService'; import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; @@ -62,6 +62,10 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IAction } from 'vs/base/common/actions'; +import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { Schemas } from 'vs/base/common/network'; +import { ShowRuntimeExtensionsAction } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor'; +import { extensionsViewIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -100,12 +104,13 @@ Registry.as(EditorExtensions.Editors).registerEditor( new SyncDescriptor(ExtensionsInput) ]); + Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( { id: VIEWLET_ID, name: localize('extensions', "Extensions"), ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer), - icon: 'codicon-extensions', + icon: extensionsViewIcon, order: 4, rejectAddedViews: true, alwaysUseContainerInfo: true @@ -140,7 +145,7 @@ Registry.as(ConfigurationExtensions.Configuration) }, 'extensions.showRecommendationsOnlyOnDemand': { type: 'boolean', - description: localize('extensionsShowRecommendationsOnlyOnDemand', "When enabled, recommendations will not be fetched or shown unless specifically requested by the user. Some recommendations are fetched from a Microsoft online service."), + deprecationMessage: localize('extensionsShowRecommendationsOnlyOnDemand_Deprecated', "This setting is deprecated. Use extensions.ignoreRecommendations setting to control recommendation notifications. Use Extensions view's visibility actions to hide Recommended view by default."), default: false, tags: ['usesOnlineServices'] }, @@ -234,8 +239,8 @@ CommandsRegistry.registerCommand({ .then(async (extensions) => { for (const extension of extensions) { const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local))); - const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name) - : localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name); + const message = requireReload ? localize('InstallVSIXAction.successReload', "Completed installing {0} extension from VSIX. Please reload Visual Studio Code to enable it.", extension.displayName || extension.name) + : localize('InstallVSIXAction.success', "Completed installing {0} extension from VSIX.", extension.displayName || extension.name); const actions = requireReload ? [{ label: localize('InstallVSIXAction.reloadNow', "Reload Now"), run: () => hostService.reload() @@ -279,7 +284,7 @@ CommandsRegistry.registerCommand({ } try { - await extensionManagementService.uninstall(extensionToUninstall, true); + await extensionManagementService.uninstall(extensionToUninstall); } catch (e) { onUnexpectedError(e); throw e; @@ -771,6 +776,22 @@ class ExtensionsContributions implements IWorkbenchContribution { } }); + registerAction2(class extends Action2 { + constructor() { + super({ + id: RefreshExtensionsAction.ID, + title: { value: RefreshExtensionsAction.LABEL, original: 'Refresh' }, + category: ExtensionsLocalizedLabel, + menu: { + id: MenuId.CommandPalette, + } + }); + } + run(accessor: ServicesAccessor) { + return runAction(accessor.get(IInstantiationService).createInstance(RefreshExtensionsAction, RefreshExtensionsAction.ID, RefreshExtensionsAction.LABEL)); + } + }); + registerAction2(class extends Action2 { constructor() { super({ @@ -916,7 +937,7 @@ class ExtensionsContributions implements IWorkbenchContribution { menu: { id: MenuId.ExtensionContext, group: '2_configure', - when: CONTEXT_SYNC_ENABLEMENT + when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, ContextKeyExpr.has('inExtensionEditor').negate()) }, }); } @@ -929,6 +950,220 @@ class ExtensionsContributions implements IWorkbenchContribution { } } }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.ignoreRecommendation', + title: { value: localize('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), original: `Ignore Recommendation` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.has('isExtensionRecommended'), + order: 1 + }, + }); + } + + async run(accessor: ServicesAccessor, id: string): Promise { + accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, true); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.undoIgnoredRecommendation', + title: { value: localize('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), original: `Undo Ignored Recommendation` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.has('isUserIgnoredRecommendation'), + order: 1 + }, + }); + } + + async run(accessor: ServicesAccessor, id: string): Promise { + accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, false); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), original: `Add to Workspace Recommendations` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate()), + order: 2 + }, + }); + } + + run(accessor: ServicesAccessor, id: string): Promise { + return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), original: `Remove from Workspace Recommendations` }, + menu: { + id: MenuId.ExtensionContext, + group: '3_recommendations', + when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended')), + order: 2 + }, + }); + } + + run(accessor: ServicesAccessor, id: string): Promise { + return accessor.get(IWorkpsaceExtensionsConfigService).toggleRecommendation(id); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), original: `Add Extension to Workspace Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService); + if (!(editorService.activeEditor instanceof ExtensionsInput)) { + return; + } + const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); + const recommendations = await workpsaceExtensionsConfigService.getRecommendations(); + if (recommendations.includes(extensionId)) { + return; + } + await workpsaceExtensionsConfigService.toggleRecommendation(extensionId); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), original: `Add Extension to Workspace Folder Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceRecommendations'); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), original: `Add Extension to Workspace Ignored Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const workpsaceExtensionsConfigService = accessor.get(IWorkpsaceExtensionsConfigService); + if (!(editorService.activeEditor instanceof ExtensionsInput)) { + return; + } + const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); + const unwatedRecommendations = await workpsaceExtensionsConfigService.getUnwantedRecommendations(); + if (unwatedRecommendations.includes(extensionId)) { + return; + } + await workpsaceExtensionsConfigService.toggleUnwantedRecommendation(extensionId); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', + title: { value: localize('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), original: `Add Extension to Workspace Folder Ignored Recommendations` }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(ICommandService).executeCommand('workbench.extensions.action.addToWorkspaceIgnoredRecommendations'); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: ConfigureWorkspaceRecommendedExtensionsAction.ID, + title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: WorkbenchStateContext.isEqualTo('workspace'), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run(); + } + }); + + registerAction2(class extends Action2 { + + constructor() { + super({ + id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, + title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' }, + category: localize('extensions', "Extensions"), + menu: { + id: MenuId.CommandPalette, + when: WorkbenchStateContext.notEqualsTo('empty'), + }, + }); + } + + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run(); + } + }); } } @@ -936,9 +1171,12 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(ConfigureRecommendedExtensionsCommandsContributor, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInstaller, LifecyclePhase.Eventually); + +// Running Extensions +const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExtensionsAction), 'Show Running Extensions', CATEGORIES.Developer.value); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts new file mode 100644 index 00000000..f5d4587b --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; +import { RuntimeExtensionsEditor } from 'vs/workbench/contrib/extensions/browser/browserRuntimeExtensionsEditor'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; + +// Running Extensions +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create(RuntimeExtensionsEditor, RuntimeExtensionsEditor.ID, localize('runtimeExtension', "Running Extensions")), + [new SyncDescriptor(RuntimeExtensionsInput)] +); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index c46dbde9..36dbf7c7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -11,12 +11,12 @@ import * as DOM from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { dispose, Disposable } from 'vs/base/common/lifecycle'; +import { dispose } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; -import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionIgnoredRecommendationsService, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensioManagementService, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -30,22 +30,19 @@ import { IExtensionService, toExtension, toExtensionDescription } from 'vs/workb import { URI } from 'vs/base/common/uri'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, registerColor, foreground } from 'vs/platform/theme/common/colorRegistry'; -import { Color } from 'vs/base/common/color'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { MenuRegistry, MenuId, IMenuService } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { IQuickPickItem, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -58,51 +55,16 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IProductService } from 'vs/platform/product/common/productService'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { Codicon } from 'vs/base/common/codicons'; import { IViewsService } from 'vs/workbench/common/views'; import { IActionViewItemOptions, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { EXTENSIONS_CONFIG } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; -import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { EXTENSIONS_CONFIG, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { ActionWithDropdownActionViewItem, IActionWithDropdownActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; - -const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService): Promise => { - return instantiationService.invokeFunction(accessor => { - if (isPromiseCanceledError(error)) { - return Promise.resolve(); - } - - const productService = accessor.get(IProductService); - const openerService = accessor.get(IOpenerService); - const notificationService = accessor.get(INotificationService); - const dialogService = accessor.get(IDialogService); - const erorrsToShows = [INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_NOT_SUPPORTED]; - if (!extension || erorrsToShows.indexOf(error.name) !== -1 || !productService.extensionsGallery) { - return dialogService.show(Severity.Error, error.message, []); - } else { - const downloadUrl = `${productService.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; - notificationService.prompt(Severity.Error, message, [{ - label: localize('download', "Download Manually"), - run: () => openerService.open(URI.parse(downloadUrl)).then(() => { - notificationService.prompt( - Severity.Info, - localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', extension.identifier.id), - [{ - label: InstallVSIXAction.LABEL, - run: () => { - const action = instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); - action.run(); - action.dispose(); - } - }] - ); - }) - }]); - return Promise.resolve(); - } - }); -}; +import { ILogService } from 'vs/platform/log/common/log'; +import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; +import { clearSearchResultsIcon, infoIcon, manageExtensionIcon, refreshIcon, syncEnabledIcon, syncIgnoredIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; function getRelativeDateLabel(date: Date): string { const delta = new Date().getTime() - date.getTime(); @@ -138,6 +100,61 @@ function getRelativeDateLabel(date: Date): string { return ''; } +class PromptExtensionInstallFailureAction extends Action { + + constructor( + private readonly extension: IExtension, + private readonly installOperation: InstallOperation, + private readonly error: Error, + @IProductService private readonly productService: IProductService, + @IOpenerService private readonly openerService: IOpenerService, + @INotificationService private readonly notificationService: INotificationService, + @IDialogService private readonly dialogService: IDialogService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService, + ) { + super('extension.promptExtensionInstallFailure'); + } + + async run(): Promise { + if (isPromiseCanceledError(this.error)) { + return; + } + + this.logService.error(this.error); + const operationMessage = this.installOperation === InstallOperation.Update ? localize('update operation', "Error while updating '{0}' extension.", this.extension.displayName || this.extension.identifier.id) + : localize('install operation', "Error while installing '{0}' extension.", this.extension.displayName || this.extension.identifier.id); + + if ([INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_NOT_SUPPORTED].includes(this.error.name)) { + await this.dialogService.show(Severity.Error, `${operationMessage}\n${getErrorMessage(this.error)}`, []); + return; + } + + const promptChoices: IPromptChoice[] = []; + if (this.extension.gallery && this.productService.extensionsGallery) { + promptChoices.push({ + label: localize('download', "Try Downloading Manually..."), + run: () => this.openerService.open(URI.parse(`${this.productService.extensionsGallery!.serviceUrl}/publishers/${this.extension.publisher}/vsextensions/${this.extension.name}/${this.extension.version}/vspackage`)).then(() => { + this.notificationService.prompt( + Severity.Info, + localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', this.extension.identifier.id), + [{ + label: InstallVSIXAction.LABEL, + run: () => { + const action = this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); + action.run(); + action.dispose(); + } + }] + ); + }) + }); + } + const checkLogsMessage = localize('check logs', "Please check the [log]({0}) for more details.", `command:${Constants.showWindowLogActionId}`); + this.notificationService.prompt(Severity.Error, `${operationMessage} ${checkLogsMessage}`, promptChoices); + } +} + export abstract class ExtensionAction extends Action implements IExtensionContainer { static readonly EXTENSION_ACTION_CLASS = 'extension-action'; static readonly TEXT_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} text`; @@ -149,13 +166,17 @@ export abstract class ExtensionAction extends Action implements IExtensionContai abstract update(): void; } -export abstract class ActionWithDropDownAction extends ExtensionAction { +export class ActionWithDropDownAction extends ExtensionAction { private action: IAction | undefined; private _menuActions: IAction[] = []; get menuActions(): IAction[] { return [...this._menuActions]; } + get extension(): IExtension | null { + return super.extension; + } + set extension(extension: IExtension | null) { this.actions.forEach(a => a.extension = extension); super.extension = extension; @@ -168,6 +189,7 @@ export abstract class ActionWithDropDownAction extends ExtensionAction { super(id, label); this.update(); this._register(Event.any(...actions.map(a => a.onDidChange))(() => this.update(true))); + actions.forEach(a => this._register(a)); } update(donotUpdateActions?: boolean): void { @@ -213,7 +235,6 @@ export abstract class AbstractInstallAction extends ExtensionAction { id: string, label: string, cssClass: string, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @INotificationService private readonly notificationService: INotificationService, @IExtensionService private readonly runtimeExtensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @ILabelService private readonly labelService: ILabelService, @@ -243,11 +264,10 @@ export abstract class AbstractInstallAction extends ExtensionAction { const extension = await this.install(this.extension); - alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName)); - - if (extension && extension.local) { + if (extension?.local) { + alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName)); const runningExtension = await this.getRunningExtension(extension.local); - if (runningExtension) { + if (runningExtension && !(runningExtension.activationEvents && runningExtension.activationEvents.some(activationEent => activationEent.startsWith('onLanguage')))) { let action = await SetColorThemeAction.create(this.workbenchThemeService, this.instantiationService, extension) || await SetFileIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension) || await SetProductIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension); @@ -263,17 +283,13 @@ export abstract class AbstractInstallAction extends ExtensionAction { } - private install(extension: IExtension): Promise { - return this.extensionsWorkbenchService.install(extension, this.getInstallOptions()) - .then(null, err => { - if (!extension.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); + private async install(extension: IExtension): Promise { + try { + return await this.extensionsWorkbenchService.install(extension, this.getInstallOptions()); + } catch (error) { + await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Install, error).run(); + return undefined; + } } private async getRunningExtension(extension: ILocalExtension): Promise { @@ -304,18 +320,16 @@ export class InstallAction extends AbstractInstallAction { constructor( @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService instantiationService: IInstantiationService, - @INotificationService notificationService: INotificationService, @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IProductService private readonly productService: IProductService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IWorkbenchExtensioManagementService private readonly workbenchExtensioManagementService: IWorkbenchExtensioManagementService, @IUserDataAutoSyncEnablementService protected readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService protected readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, ) { super(`extensions.installAndSync`, localize('install', "Install"), InstallAction.Class, - extensionsWorkbenchService, instantiationService, notificationService, runtimeExtensionService, workbenchThemeService, labelService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); this.updateLabel(); this._register(labelService.onDidChangeFormatters(() => this.updateLabel(), this)); this._register(Event.any(userDataAutoSyncEnablementService.onDidChangeEnablement, @@ -333,20 +347,17 @@ export class InstallAction extends AbstractInstallAction { // When remote connection exists if (this._manifest && this.extensionManagementServerService.remoteExtensionManagementServer) { - // On Desktop and UI Extension - if (this.extensionManagementServerService.localExtensionManagementServer && prefersExecuteOnUI(this._manifest, this.productService, this.configurationService)) { - this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally"); + const server = this.workbenchExtensioManagementService.getExtensionManagementServerToInstall(this._manifest); + + if (server === this.extensionManagementServerService.remoteExtensionManagementServer) { + const host = this.extensionManagementServerService.remoteExtensionManagementServer.label; + this.label = isMachineScoped + ? localize({ key: 'install in remote and do not sync', comment: ['This is the name of the action to install an extension in remote server and do not sync it. Placeholder is for the name of remote server.'] }, "Install in {0} (Do not sync)", host) + : localize({ key: 'install in remote', comment: ['This is the name of the action to install an extension in remote server. Placeholder is for the name of remote server.'] }, "Install in {0}", host); return; } - // On Web and Web Extension - if (this.extensionManagementServerService.webExtensionManagementServer && prefersExecuteOnWeb(this._manifest, this.productService, this.configurationService)) { - this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally"); - return; - } - - const host = this.extensionManagementServerService.remoteExtensionManagementServer.label; - this.label = isMachineScoped ? localize('install on remote and do not sync', "Install on {0} (Do not sync)", host) : localize('install on remote', "Install on {0}", host); + this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally"); return; } } @@ -362,7 +373,6 @@ export class InstallAndSyncAction extends AbstractInstallAction { constructor( @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService instantiationService: IInstantiationService, - @INotificationService notificationService: INotificationService, @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, @@ -371,8 +381,8 @@ export class InstallAndSyncAction extends AbstractInstallAction { @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, ) { super(`extensions.installAndSync`, localize('install', "Install"), InstallAndSyncAction.Class, - extensionsWorkbenchService, instantiationService, notificationService, runtimeExtensionService, workbenchThemeService, labelService); - this.tooltip = localize('install everywhere tooltip', "Install this extension in all your synced {0} instances", productService.nameLong); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); + this.tooltip = localize({ key: 'install everywhere tooltip', comment: ['Placeholder is the name of the product. Eg: Visual Studio Code or Visual Studio Code - Insiders'] }, "Install this extension in all your synced {0} instances", productService.nameLong); this._register(Event.any(userDataAutoSyncEnablementService.onDidChangeEnablement, Event.filter(userDataSyncResourceEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update())); } @@ -469,7 +479,7 @@ export abstract class InstallInOtherServerAction extends ExtensionAction { } } - private canInstall(): boolean { + protected canInstall(): boolean { // Disable if extension is not installed or not an user extension if ( !this.extension @@ -496,6 +506,11 @@ export abstract class InstallInOtherServerAction extends ExtensionAction { return true; } + // Prefers to run on Web + if (this.server === this.extensionManagementServerService.webExtensionManagementServer && prefersExecuteOnWeb(this.extension.local.manifest, this.productService, this.configurationService)) { + return true; + } + if (this.canInstallAnyWhere) { // Can run on UI if (this.server === this.extensionManagementServerService.localExtensionManagementServer && canExecuteOnUI(this.extension.local.manifest, this.productService, this.configurationService)) { @@ -543,7 +558,9 @@ export class RemoteInstallAction extends InstallInOtherServerAction { } protected getInstallLabel(): string { - return this.extensionManagementServerService.remoteExtensionManagementServer ? localize('Install on Server', "Install in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label) : InstallInOtherServerAction.INSTALL_LABEL; + return this.extensionManagementServerService.remoteExtensionManagementServer + ? localize({ key: 'install in remote', comment: ['This is the name of the action to install an extension in remote server. Placeholder is for the name of remote server.'] }, "Install in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label) + : InstallInOtherServerAction.INSTALL_LABEL; } } @@ -565,9 +582,34 @@ export class LocalInstallAction extends InstallInOtherServerAction { } +export class WebInstallAction extends InstallInOtherServerAction { + + constructor( + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, + @IWebExtensionsScannerService private readonly webExtensionsScannerService: IWebExtensionsScannerService, + ) { + super(`extensions.webInstall`, extensionManagementServerService.webExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, productService, configurationService); + } + + protected getInstallLabel(): string { + return localize('install browser', "Install in Browser"); + } + + protected canInstall(): boolean { + if (super.canInstall()) { + return !!this.extension?.gallery && this.webExtensionsScannerService.canAddExtension(this.extension.gallery); + } + return false; + } + +} + export class UninstallAction extends ExtensionAction { - private static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); + static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling"); private static readonly UninstallClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall`; @@ -632,7 +674,6 @@ export class UpdateAction extends ExtensionAction { constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @INotificationService private readonly notificationService: INotificationService, ) { super(`extensions.update`, '', UpdateAction.DisabledClass, false); this.update(); @@ -669,18 +710,13 @@ export class UpdateAction extends ExtensionAction { return this.install(this.extension); } - private install(extension: IExtension): Promise { - return this.extensionsWorkbenchService.install(extension).then(() => { + private async install(extension: IExtension): Promise { + try { + await this.extensionsWorkbenchService.install(extension); alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", extension.displayName, extension.latestVersion)); - }, err => { - if (!extension.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); + } catch (err) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Update, err).run(); + } } private getUpdateLabel(version?: string): string { @@ -828,44 +864,51 @@ export class DropDownMenuActionViewItem extends ExtensionActionViewItem { } } -export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): IAction[][] { - const scopedContextKeyService = contextKeyService.createScoped(); - if (extension) { - scopedContextKeyService.createKey('extension', extension.identifier.id); - scopedContextKeyService.createKey('isBuiltinExtension', extension.isBuiltin); - scopedContextKeyService.createKey('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration); - if (extension.state === ExtensionState.Installed) { - scopedContextKeyService.createKey('extensionStatus', 'installed'); +export function getContextMenuActions(extension: IExtension | undefined | null, inExtensionEditor: boolean, instantiationService: IInstantiationService): IAction[][] { + return instantiationService.invokeFunction(accessor => { + const scopedContextKeyService = accessor.get(IContextKeyService).createScoped(); + const menuService = accessor.get(IMenuService); + const extensionRecommendationsService = accessor.get(IExtensionRecommendationsService); + const extensionIgnoredRecommendationsService = accessor.get(IExtensionIgnoredRecommendationsService); + if (extension) { + scopedContextKeyService.createKey('extension', extension.identifier.id); + scopedContextKeyService.createKey('isBuiltinExtension', extension.isBuiltin); + scopedContextKeyService.createKey('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration); + scopedContextKeyService.createKey('isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]); + scopedContextKeyService.createKey('isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace); + scopedContextKeyService.createKey('isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())); + scopedContextKeyService.createKey('inExtensionEditor', inExtensionEditor); + if (extension.state === ExtensionState.Installed) { + scopedContextKeyService.createKey('extensionStatus', 'installed'); + } } - } - const groups: IAction[][] = []; - const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); - menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { - if (action instanceof SubmenuAction) { - return action; - } - return instantiationService.createInstance(MenuItemExtensionAction, action); - }))); - menu.dispose(); - scopedContextKeyService.dispose(); + const groups: IAction[][] = []; + const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService); + menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => { + if (action instanceof SubmenuAction) { + return action; + } + return instantiationService.createInstance(MenuItemExtensionAction, action); + }))); + menu.dispose(); + scopedContextKeyService.dispose(); - return groups; + return groups; + }); } export class ManageExtensionAction extends ExtensionDropDownAction { static readonly ID = 'extensions.manage'; - private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage codicon-gear`; + private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage ` + ThemeIcon.asClassName(manageExtensionIcon); private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`; constructor( @IInstantiationService instantiationService: IInstantiationService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, - @IMenuService private readonly menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(ManageExtensionAction.ID, '', '', true, true, instantiationService); @@ -902,10 +945,12 @@ export class ManageExtensionAction extends ExtensionDropDownAction { this.instantiationService.createInstance(DisableGloballyAction, runningExtensions), this.instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions) ]); - groups.push([this.instantiationService.createInstance(UninstallAction)]); - groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]); + groups.push([ + this.instantiationService.createInstance(UninstallAction), + this.instantiationService.createInstance(InstallAnotherVersionAction) + ]); - getContextMenuActions(this.menuService, this.contextKeyService, this.instantiationService, this.extension).forEach(actions => groups.push(actions)); + getContextMenuActions(this.extension, false, this.instantiationService).forEach(actions => groups.push(actions)); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { @@ -933,6 +978,30 @@ export class ManageExtensionAction extends ExtensionDropDownAction { } } +export class ExtensionEditorManageExtensionAction extends ExtensionDropDownAction { + + constructor( + @IInstantiationService instantiationService: IInstantiationService + ) { + super('extensionEditor.manageExtension', '', `${ExtensionAction.ICON_ACTION_CLASS} manage ${ThemeIcon.asClassName(manageExtensionIcon)}`, true, true, instantiationService); + this.tooltip = localize('manage', "Manage"); + } + + update(): void { } + + run(): Promise { + const actionGroups: IAction[][] = []; + getContextMenuActions(this.extension, true, this.instantiationService).forEach(actions => actionGroups.push(actions)); + actionGroups.forEach(group => group.forEach(extensionAction => { + if (extensionAction instanceof ExtensionAction) { + extensionAction.extension = this.extension; + } + })); + return super.run({ actionGroups, disposeActionsOnHide: true }); + } + +} + export class MenuItemExtensionAction extends ExtensionAction { constructor( @@ -968,14 +1037,13 @@ export class InstallAnotherVersionAction extends ExtensionAction { @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @INotificationService private readonly notificationService: INotificationService, ) { - super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL); + super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); this.update(); } update(): void { - this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery; + this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && this.extension.state === ExtensionState.Installed; } run(): Promise { @@ -983,22 +1051,20 @@ export class InstallAnotherVersionAction extends ExtensionAction { return Promise.resolve(); } return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true }) - .then(pick => { + .then(async pick => { if (pick) { if (this.extension!.version === pick.id) { return Promise.resolve(); } - const promise: Promise = pick.latest ? this.extensionsWorkbenchService.install(this.extension!) : this.extensionsWorkbenchService.installVersion(this.extension!, pick.id); - return promise - .then(null, err => { - if (!this.extension!.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(this.extension!.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension!.identifier.id), err, this.instantiationService); - }); + try { + if (pick.latest) { + await this.extensionsWorkbenchService.install(this.extension!); + } else { + await this.extensionsWorkbenchService.installVersion(this.extension!, pick.id); + } + } catch (error) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, InstallOperation.Install, error).run(); + } } return null; }); @@ -1020,6 +1086,7 @@ export class EnableForWorkspaceAction extends ExtensionAction { @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(EnableForWorkspaceAction.ID, EnableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('enableForWorkspaceActionToolTip', "Enable this extension only in this workspace"); this.update(); } @@ -1050,6 +1117,7 @@ export class EnableGloballyAction extends ExtensionAction { @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(EnableGloballyAction.ID, EnableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('enableGloballyActionToolTip', "Enable this extension"); this.update(); } @@ -1075,18 +1143,24 @@ export class DisableForWorkspaceAction extends ExtensionAction { static readonly ID = 'extensions.disableForWorkspace'; static readonly LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)"); - constructor(readonly runningExtensions: IExtensionDescription[], + constructor(private _runningExtensions: IExtensionDescription[], @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(DisableForWorkspaceAction.ID, DisableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('disableForWorkspaceActionToolTip', "Disable this extension only in this workspace"); + this.update(); + } + + set runningExtensions(runningExtensions: IExtensionDescription[]) { + this._runningExtensions = runningExtensions; this.update(); } update(): void { this.enabled = false; - if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { + if (this.extension && this.extension.local && this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace) && this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local); @@ -1106,17 +1180,24 @@ export class DisableGloballyAction extends ExtensionAction { static readonly ID = 'extensions.disableGlobally'; static readonly LABEL = localize('disableGloballyAction', "Disable"); - constructor(readonly runningExtensions: IExtensionDescription[], + constructor( + private _runningExtensions: IExtensionDescription[], @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { super(DisableGloballyAction.ID, DisableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS); + this.tooltip = localize('disableGloballyActionToolTip', "Disable this extension"); + this.update(); + } + + set runningExtensions(runningExtensions: IExtensionDescription[]) { + this._runningExtensions = runningExtensions; this.update(); } update(): void { this.enabled = false; - if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) { + if (this.extension && this.extension.local && this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) { this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace) && this.extensionEnablementService.canChangeEnablement(this.extension.local); @@ -1146,13 +1227,21 @@ export class EnableDropDownAction extends ActionWithDropDownAction { export class DisableDropDownAction extends ActionWithDropDownAction { constructor( - runningExtensions: IExtensionDescription[], + @IExtensionService extensionService: IExtensionService, @IInstantiationService instantiationService: IInstantiationService ) { - super('extensions.disable', localize('disableAction', "Disable"), [ - instantiationService.createInstance(DisableGloballyAction, runningExtensions), - instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions) - ]); + const actions = [ + instantiationService.createInstance(DisableGloballyAction, []), + instantiationService.createInstance(DisableForWorkspaceAction, []) + ]; + super('extensions.disable', localize('disableAction', "Disable"), actions); + + const updateRunningExtensions = async () => { + const runningExtensions = await extensionService.getExtensions(); + actions.forEach(a => a.runningExtensions = runningExtensions); + }; + updateRunningExtensions(); + this._register(extensionService.onDidChangeExtensions(() => updateRunningExtensions())); } } @@ -1265,7 +1354,6 @@ export class UpdateAllAction extends Action { constructor( id: string, label: string, isPrimary: boolean, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @INotificationService private readonly notificationService: INotificationService, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(id, label, '', false); @@ -1283,16 +1371,12 @@ export class UpdateAllAction extends Action { return Promise.all(this.extensionsWorkbenchService.outdated.map(e => this.install(e))); } - private install(extension: IExtension): Promise { - return this.extensionsWorkbenchService.install(extension).then(undefined, err => { - if (!extension.gallery) { - return this.notificationService.error(err); - } - - console.error(err); - - return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); + private async install(extension: IExtension): Promise { + try { + await this.extensionsWorkbenchService.install(extension); + } catch (err) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Update, err).run(); + } } } @@ -1345,11 +1429,12 @@ export class ReloadAction extends ExtensionAction { } const isUninstalled = this.extension.state === ExtensionState.Uninstalled; - const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0]; - const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)); + const runningExtension = this._runningExtensions.find(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier)); if (isUninstalled) { - if (isSameExtensionRunning && !this.extensionService.canRemoveExtension(runningExtension)) { + const canRemoveRunningExtension = runningExtension && this.extensionService.canRemoveExtension(runningExtension); + const isSameExtensionRunning = runningExtension && (!this.extension.server || this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension))); + if (!canRemoveRunningExtension && isSameExtensionRunning) { this.enabled = true; this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension."); @@ -1358,6 +1443,7 @@ export class ReloadAction extends ExtensionAction { return; } if (this.extension.local) { + const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)); const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local); // Extension is running @@ -1693,11 +1779,11 @@ export class ShowInstalledExtensionsAction extends Action { super(id, label, undefined, true); } - run(refresh?: boolean): Promise { + run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { - viewlet.search('@installed ', refresh); + viewlet.search('@installed '); viewlet.focus(); }); } @@ -1736,7 +1822,7 @@ export class ClearExtensionsSearchResultsAction extends Action { label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'codicon-clear-all', true); + super(id, label, ThemeIcon.asClassName(clearSearchResultsIcon), true); } async run(): Promise { @@ -1755,18 +1841,39 @@ export class ClearExtensionsInputAction extends ClearExtensionsSearchResultsActi id: string, label: string, onSearchChange: Event, - value: string, + getValue: () => string, @IViewsService viewsService: IViewsService ) { super(id, label, viewsService); - this.onSearchChange(value); + this.onSearchChange(getValue()); this._register(onSearchChange(this.onSearchChange, this)); } private onSearchChange(value: string): void { this.enabled = !!value; } +} +export class RefreshExtensionsAction extends Action { + + static readonly ID = 'workbench.extensions.action.refreshExtension'; + static readonly LABEL = localize('refreshExtension', "Refresh"); + + constructor( + id: string, + label: string, + @IViewsService private readonly viewsService: IViewsService + ) { + super(id, label, ThemeIcon.asClassName(refreshIcon), true); + } + + async run(): Promise { + const viewPaneContainer = this.viewsService.getActiveViewPaneContainerWithId(VIEWLET_ID); + if (viewPaneContainer) { + const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer; + return extensionsViewPaneContainer.refresh(); + } + } } export class ShowBuiltInExtensionsAction extends Action { @@ -1899,7 +2006,7 @@ export class ShowRecommendedExtensionsAction extends Action { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { - viewlet.search('@recommended ', true); + viewlet.search('@recommended '); viewlet.focus(); }); } @@ -1956,26 +2063,21 @@ export class InstallRecommendedExtensionAction extends Action { this.extensionId = extensionId; } - run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) - .then(viewlet => { - viewlet.search(`@id:${this.extensionId}`); - viewlet.focus(); - return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None) - .then(pager => { - if (pager && pager.firstPage && pager.firstPage.length) { - const extension = pager.firstPage[0]; - return this.extensionWorkbenchService.install(extension) - .then(() => this.extensionWorkbenchService.open(extension)) - .then(() => null, err => { - console.error(err); - return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService); - }); - } - return null; - }); - }); + async run(): Promise { + const viewlet = await this.viewletService.openViewlet(VIEWLET_ID, true); + const viewPaneContainer = viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer; + viewPaneContainer.search(`@id:${this.extensionId}`); + viewPaneContainer.focus(); + const pager = await this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None); + if (pager && pager.firstPage && pager.firstPage.length) { + const extension = pager.firstPage[0]; + await this.extensionWorkbenchService.open(extension); + try { + await this.extensionWorkbenchService.install(extension); + } catch (err) { + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Install, err).run(); + } + } } } @@ -1983,7 +2085,7 @@ export class IgnoreExtensionRecommendationAction extends Action { static readonly ID = 'extensions.ignore'; - private static readonly Class = 'extension-action ignore'; + private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} ignore`; constructor( private readonly extension: IExtension, @@ -2006,7 +2108,7 @@ export class UndoIgnoreExtensionRecommendationAction extends Action { static readonly ID = 'extensions.ignore'; - private static readonly Class = 'extension-action undo-ignore'; + private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} undo-ignore`; constructor( private readonly extension: IExtension, @@ -2122,13 +2224,15 @@ export class ChangeSortAction extends Action { this.query = Query.parse(''); this.enabled = false; + this.checked = false; this._register(onSearchChange(this.onSearchChange, this)); } private onSearchChange(value: string): void { const query = Query.parse(value); this.query = new Query(query.value, this.sortBy || query.sortBy, query.groupBy); - this.enabled = !!value && this.query.isValid() && !this.query.equals(query); + this.enabled = !!value && this.query.isValid(); + this.checked = this.enabled && this.query.equals(query); } run(): Promise { @@ -2141,124 +2245,6 @@ export class ChangeSortAction extends Action { } } -export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution { - - private workspaceContextKey = new RawContextKey('workspaceRecommendations', true); - private workspaceFolderContextKey = new RawContextKey('workspaceFolderRecommendations', true); - private addToWorkspaceRecommendationsContextKey = new RawContextKey('addToWorkspaceRecommendations', false); - private addToWorkspaceFolderRecommendationsContextKey = new RawContextKey('addToWorkspaceFolderRecommendations', false); - - constructor( - @IContextKeyService contextKeyService: IContextKeyService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService - ) { - super(); - const boundWorkspaceContextKey = this.workspaceContextKey.bindTo(contextKeyService); - boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE); - this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - - const boundWorkspaceFolderContextKey = this.workspaceFolderContextKey.bindTo(contextKeyService); - boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0); - this._register(workspaceContextService.onDidChangeWorkspaceFolders(() => boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0))); - - const boundAddToWorkspaceRecommendationsContextKey = this.addToWorkspaceRecommendationsContextKey.bindTo(contextKeyService); - boundAddToWorkspaceRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE); - this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceRecommendationsContextKey.set( - editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundAddToWorkspaceRecommendationsContextKey.set( - editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE))); - - const boundAddToWorkspaceFolderRecommendationsContextKey = this.addToWorkspaceFolderRecommendationsContextKey.bindTo(contextKeyService); - boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput); - this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput))); - - this.registerCommands(); - } - - private registerCommands(): void { - CommandsRegistry.registerCommand(ConfigureWorkspaceRecommendedExtensionsAction.ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run(); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: ConfigureWorkspaceRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.workspaceContextKey - }); - - CommandsRegistry.registerCommand(ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run(); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.workspaceFolderContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.ADD_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.ADD_ID, AddToWorkspaceRecommendationsAction.ADD_LABEL) - .run(AddToWorkspaceRecommendationsAction.ADD); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceRecommendationsAction.ADD_ID, - title: { value: AddToWorkspaceRecommendationsAction.ADD_LABEL, original: 'Add to Recommended Extensions (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.ADD_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.ADD_ID, AddToWorkspaceFolderRecommendationsAction.ADD_LABEL) - .run(AddToWorkspaceRecommendationsAction.ADD); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceFolderRecommendationsAction.ADD_ID, - title: { value: AddToWorkspaceFolderRecommendationsAction.ADD_LABEL, original: 'Extensions: Add to Recommended Extensions (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceFolderRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.IGNORE_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.IGNORE_ID, AddToWorkspaceRecommendationsAction.IGNORE_LABEL) - .run(AddToWorkspaceRecommendationsAction.IGNORE); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceRecommendationsAction.IGNORE_ID, - title: { value: AddToWorkspaceRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceRecommendationsContextKey - }); - - CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, serviceAccessor => { - serviceAccessor.get(IInstantiationService) - .createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL) - .run(AddToWorkspaceRecommendationsAction.IGNORE); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, - title: { value: AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace Folder)' }, - category: localize('extensions', "Extensions") - }, - when: this.addToWorkspaceFolderRecommendationsContextKey - }); - } -} - export abstract class AbstractConfigureRecommendedExtensionsAction extends Action { constructor( @@ -2300,83 +2286,6 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio })); } - protected addExtensionToWorkspaceConfig(workspaceConfigurationFile: URI, extensionId: string, shouldRecommend: boolean) { - return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile) - .then(content => { - const extensionIdLowerCase = extensionId.toLowerCase(); - const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value.toString()) || {})['extensions'] || {}; - let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || []; - let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || []; - - if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) { - return Promise.resolve(null); - } - - insertInto.push(extensionId); - removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); - - return this.jsonEditingService.write(workspaceConfigurationFile, - [{ - path: ['extensions'], - value: { - recommendations: shouldRecommend ? insertInto : removeFrom, - unwantedRecommendations: shouldRecommend ? removeFrom : insertInto - } - }], - true); - }); - } - - protected addExtensionToWorkspaceFolderConfig(extensionsFileResource: URI, extensionId: string, shouldRecommend: boolean): Promise { - return this.getOrCreateExtensionsFile(extensionsFileResource) - .then(({ content }) => { - const extensionIdLowerCase = extensionId.toLowerCase(); - const extensionsConfigContent: IExtensionsConfigContent = json.parse(content) || {}; - let insertInto = shouldRecommend ? extensionsConfigContent.recommendations || [] : extensionsConfigContent.unwantedRecommendations || []; - let removeFrom = shouldRecommend ? extensionsConfigContent.unwantedRecommendations || [] : extensionsConfigContent.recommendations || []; - - if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) { - return Promise.resolve(null); - } - - insertInto.push(extensionId); - - let removeFromPromise: Promise = Promise.resolve(); - if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) { - removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); - removeFromPromise = this.jsonEditingService.write(extensionsFileResource, - [{ - path: shouldRecommend ? ['unwantedRecommendations'] : ['recommendations'], - value: removeFrom - }], - true); - } - - return removeFromPromise.then(() => - this.jsonEditingService.write(extensionsFileResource, - [{ - path: shouldRecommend ? ['recommendations'] : ['unwantedRecommendations'], - value: insertInto - }], - true) - ); - }); - } - - protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise { - return Promise.resolve(this.fileService.readFile(extensionsFileResource)) - .then(content => { - return (json.parse(content.value.toString()) || {})['extensions'] || {}; - }, err => ({ recommendations: [], unwantedRecommendations: [] })); - } - - protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise { - return Promise.resolve(this.fileService.readFile(extensionsFileResource)) - .then(content => { - return (json.parse(content.value.toString()) || {}); - }, err => ({ recommendations: [], unwantedRecommendations: [] })); - } - private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise { return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile)) .then(content => { @@ -2495,161 +2404,6 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac } } -export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction { - static readonly ADD = true; - static readonly IGNORE = false; - static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceFolderRecommendations'; - static readonly ADD_LABEL = localize('addToWorkspaceFolderRecommendations', "Add to Recommended Extensions (Workspace Folder)"); - static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations'; - static readonly IGNORE_LABEL = localize('addToWorkspaceFolderIgnoredRecommendations', "Ignore Recommended Extension (Workspace Folder)"); - - constructor( - id: string, - label: string, - @IFileService fileService: IFileService, - @ITextFileService textFileService: ITextFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService, - @IJSONEditingService jsonEditingService: IJSONEditingService, - @ITextModelService textModelResolverService: ITextModelService, - @ICommandService private readonly commandService: ICommandService, - @INotificationService private readonly notificationService: INotificationService - ) { - super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService); - } - - run(shouldRecommend: boolean): Promise { - if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension) { - return Promise.resolve(); - } - const folders = this.contextService.getWorkspace().folders; - if (!folders || !folders.length) { - this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.noWorkspace', 'There are no workspace folders open to add recommendations.')); - return Promise.resolve(); - } - - const extensionId = this.editorService.activeEditor.extension.identifier; - const pickFolderPromise = folders.length === 1 - ? Promise.resolve(folders[0]) - : this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID); - return Promise.resolve(pickFolderPromise) - .then(workspaceFolder => { - if (!workspaceFolder) { - return Promise.resolve(); - } - const configurationFile = workspaceFolder.toResource(EXTENSIONS_CONFIG); - return this.getWorkspaceFolderExtensionsConfigContent(configurationFile).then(content => { - const extensionIdLowerCase = extensionId.id.toLowerCase(); - if (shouldRecommend) { - if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openExtensionsFile(configurationFile) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); - }); - } - else { - if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceFolderIgnoredRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s unwanted recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openExtensionsFile(configurationFile) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err)); - }); - } - }); - }); - } -} - -export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction { - static readonly ADD = true; - static readonly IGNORE = false; - static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceRecommendations'; - static readonly ADD_LABEL = localize('addToWorkspaceRecommendations', "Add to Recommended Extensions (Workspace)"); - static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations'; - static readonly IGNORE_LABEL = localize('addToWorkspaceIgnoredRecommendations', "Ignore Recommended Extension (Workspace)"); - - constructor( - id: string, - label: string, - @IFileService fileService: IFileService, - @ITextFileService textFileService: ITextFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEditorService editorService: IEditorService, - @IJSONEditingService jsonEditingService: IJSONEditingService, - @ITextModelService textModelResolverService: ITextModelService, - @INotificationService private readonly notificationService: INotificationService - ) { - super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService); - } - - run(shouldRecommend: boolean): Promise { - const workspaceConfig = this.contextService.getWorkspace().configuration; - - if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension || !workspaceConfig) { - return Promise.resolve(); - } - - const extensionId = this.editorService.activeEditor.extension.identifier; - - return this.getWorkspaceExtensionsConfigContent(workspaceConfig).then(content => { - const extensionIdLowerCase = extensionId.id.toLowerCase(); - if (shouldRecommend) { - if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceRecommendations.alreadyExists', 'This extension is already present in workspace recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openWorkspaceConfigurationFile(workspaceConfig) - }]); - - }, err => { - this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); - }); - } else { - if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) { - this.notificationService.info(localize('AddToWorkspaceUnwantedRecommendations.alreadyExists', 'This extension is already present in workspace unwanted recommendations.')); - return Promise.resolve(); - } - - return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => { - this.notificationService.prompt(Severity.Info, - localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'), - [{ - label: localize('viewChanges', "View Changes"), - run: () => this.openWorkspaceConfigurationFile(workspaceConfig) - }]); - }, err => { - this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err)); - }); - } - }); - } -} - export class StatusLabelAction extends Action implements IExtensionContainer { private static readonly ENABLED_CLASS = `${ExtensionAction.TEXT_ACTION_CLASS} extension-status-label`; @@ -2776,30 +2530,43 @@ export class MaliciousStatusLabelAction extends ExtensionAction { } } -export class SyncIgnoredIconAction extends ExtensionAction { +export class ToggleSyncExtensionAction extends ExtensionDropDownAction { - private static readonly ENABLE_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} codicon-sync-ignored`; - private static readonly DISABLE_CLASS = `${SyncIgnoredIconAction.ENABLE_CLASS} hide`; + private static readonly IGNORED_SYNC_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncIgnoredIcon)}`; + private static readonly SYNC_CLASS = `${ToggleSyncExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncEnabledIcon)}`; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, + @IInstantiationService instantiationService: IInstantiationService, ) { - super('extensions.syncignore', '', SyncIgnoredIconAction.DISABLE_CLASS, false); + super('extensions.sync', '', ToggleSyncExtensionAction.SYNC_CLASS, false, true, instantiationService); this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('settingsSync.ignoredExtensions'))(() => this.update())); + this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(() => this.update())); this.update(); - this.tooltip = localize('syncingore.label', "This extension is ignored during sync."); } update(): void { - this.class = SyncIgnoredIconAction.DISABLE_CLASS; - if (this.extension && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)) { - this.class = SyncIgnoredIconAction.ENABLE_CLASS; + this.enabled = !!this.extension && this.userDataAutoSyncEnablementService.isEnabled() && this.extension.state === ExtensionState.Installed; + if (this.extension) { + const isIgnored = this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension); + this.class = isIgnored ? ToggleSyncExtensionAction.IGNORED_SYNC_CLASS : ToggleSyncExtensionAction.SYNC_CLASS; + this.tooltip = isIgnored ? localize('ignored', "This extension is ignored during sync") : localize('synced', "This extension is synced"); } } - run(): Promise { - return Promise.resolve(); + async run(): Promise { + return super.run({ + actionGroups: [ + [ + new Action( + 'extensions.syncignore', + this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension!) ? localize('sync', "Sync this extension") : localize('do not sync', "Do not sync this extension") + , undefined, true, () => this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(this.extension!)) + ] + ], disposeActionsOnHide: true + }); } } @@ -2883,8 +2650,8 @@ export class ExtensionToolTipAction extends ExtensionAction { export class SystemDisabledWarningAction extends ExtensionAction { private static readonly CLASS = `${ExtensionAction.ICON_ACTION_CLASS} system-disable`; - private static readonly WARNING_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.warning.classNames}`; - private static readonly INFO_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.info.classNames}`; + private static readonly WARNING_CLASS = `${SystemDisabledWarningAction.CLASS} ${ThemeIcon.asClassName(warningIcon)}`; + private static readonly INFO_CLASS = `${SystemDisabledWarningAction.CLASS} ${ThemeIcon.asClassName(infoIcon)}`; updateWhenCounterExtensionChanges: boolean = true; private _runningExtensions: IExtensionDescription[] | null = null; @@ -3105,6 +2872,7 @@ export class InstallVSIXAction extends Action { title: localize('installFromVSIX', "Install from VSIX"), filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }], canSelectFiles: true, + canSelectMany: true, openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install")) }); @@ -3273,21 +3041,18 @@ interface IExtensionPickItem extends IQuickPickItem { extension?: IExtension; } -export class InstallLocalExtensionsInRemoteAction extends Action { +export abstract class AbstractInstallExtensionsInServerAction extends Action { private extensions: IExtension[] | undefined = undefined; constructor( - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + id: string, + @IExtensionsWorkbenchService protected readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IQuickInputService private readonly quickInputService: IQuickInputService, @INotificationService private readonly notificationService: INotificationService, - @IHostService private readonly hostService: IHostService, @IProgressService private readonly progressService: IProgressService, - @IInstantiationService private readonly instantiationService: IInstantiationService ) { - super('workbench.extensions.actions.installLocalExtensionsInRemote'); + super(id); this.update(); this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions()); this._register(this.extensionsWorkbenchService.onChange(() => { @@ -3297,13 +3062,6 @@ export class InstallLocalExtensionsInRemoteAction extends Action { })); } - get label(): string { - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label); - } - return ''; - } - private updateExtensions(): void { this.extensions = this.extensionsWorkbenchService.local; this.update(); @@ -3315,7 +3073,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { } async run(): Promise { - return this.selectAndInstallLocalExtensions(); + return this.selectAndInstallExtensions(); } private async queryExtensionsToInstall(): Promise { @@ -3323,15 +3081,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { return this.getExtensionsToInstall(local); } - private getExtensionsToInstall(local: IExtension[]): IExtension[] { - return local.filter(extension => { - const action = this.instantiationService.createInstance(RemoteInstallAction, true); - action.extension = extension; - return action.enabled; - }); - } - - private async selectAndInstallLocalExtensions(): Promise { + private async selectAndInstallExtensions(): Promise { const quickPick = this.quickInputService.createQuickPick(); quickPick.busy = true; const disposable = quickPick.onDidAccept(() => { @@ -3344,7 +3094,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { const localExtensionsToInstall = await this.queryExtensionsToInstall(); quickPick.busy = false; if (localExtensionsToInstall.length) { - quickPick.title = localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label); + quickPick.title = this.getQuickPickTitle(); quickPick.placeholder = localize('select extensions to install', "Select extensions to install"); quickPick.canSelectMany = true; localExtensionsToInstall.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)); @@ -3359,21 +3109,60 @@ export class InstallLocalExtensionsInRemoteAction extends Action { } } - private onDidAccept(selectedItems: ReadonlyArray): void { + private async onDidAccept(selectedItems: ReadonlyArray): Promise { if (selectedItems.length) { const localExtensionsToInstall = selectedItems.filter(r => !!r.extension).map(r => r.extension!); if (localExtensionsToInstall.length) { - this.progressService.withProgress( + await this.progressService.withProgress( { location: ProgressLocation.Notification, title: localize('installing extensions', "Installing Extensions...") }, - () => this.installLocalExtensions(localExtensionsToInstall)); + () => this.installExtensions(localExtensionsToInstall)); + this.notificationService.info(localize('finished installing', "Successfully installed extensions.")); } } } - private async installLocalExtensions(localExtensionsToInstall: IExtension[]): Promise { + protected abstract getQuickPickTitle(): string; + protected abstract getExtensionsToInstall(local: IExtension[]): IExtension[]; + protected abstract installExtensions(extensions: IExtension[]): Promise; +} + +export class InstallLocalExtensionsInRemoteAction extends AbstractInstallExtensionsInServerAction { + + constructor( + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IQuickInputService quickInputService: IQuickInputService, + @IProgressService progressService: IProgressService, + @INotificationService notificationService: INotificationService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super('workbench.extensions.actions.installLocalExtensionsInRemote', extensionsWorkbenchService, quickInputService, notificationService, progressService); + } + + get label(): string { + if (this.extensionManagementServerService && this.extensionManagementServerService.remoteExtensionManagementServer) { + return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label); + } + return ''; + } + + protected getQuickPickTitle(): string { + return localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label); + } + + protected getExtensionsToInstall(local: IExtension[]): IExtension[] { + return local.filter(extension => { + const action = this.instantiationService.createInstance(RemoteInstallAction, true); + action.extension = extension; + return action.enabled; + }); + } + + protected async installExtensions(localExtensionsToInstall: IExtension[]): Promise { const galleryExtensions: IGalleryExtension[] = []; const vsixs: URI[] = []; await Promise.all(localExtensionsToInstall.map(async extension => { @@ -3390,15 +3179,54 @@ export class InstallLocalExtensionsInRemoteAction extends Action { await Promise.all(galleryExtensions.map(gallery => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(gallery))); await Promise.all(vsixs.map(vsix => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.install(vsix))); + } +} - this.notificationService.notify({ - severity: Severity.Info, - message: localize('finished installing', "Successfully installed extensions in {0}. Please reload the window to enable them.", this.extensionManagementServerService.remoteExtensionManagementServer!.label), - actions: { - primary: [new Action('realod', localize('reload', "Reload Window"), '', true, - () => this.hostService.reload())] +export class InstallRemoteExtensionsInLocalAction extends AbstractInstallExtensionsInServerAction { + + constructor( + id: string, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IQuickInputService quickInputService: IQuickInputService, + @IProgressService progressService: IProgressService, + @INotificationService notificationService: INotificationService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + ) { + super(id, extensionsWorkbenchService, quickInputService, notificationService, progressService); + } + + get label(): string { + return localize('select and install remote extensions', "Install Remote Extensions Locally..."); + } + + protected getQuickPickTitle(): string { + return localize('install remote extensions', "Install Remote Extensions Locally"); + } + + protected getExtensionsToInstall(local: IExtension[]): IExtension[] { + return local.filter(extension => + extension.type === ExtensionType.User && extension.server !== this.extensionManagementServerService.localExtensionManagementServer + && !this.extensionsWorkbenchService.installed.some(e => e.server === this.extensionManagementServerService.localExtensionManagementServer && areSameExtensions(e.identifier, extension.identifier))); + } + + protected async installExtensions(extensions: IExtension[]): Promise { + const galleryExtensions: IGalleryExtension[] = []; + const vsixs: URI[] = []; + await Promise.all(extensions.map(async extension => { + if (this.extensionGalleryService.isEnabled()) { + const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, extension.version); + if (gallery) { + galleryExtensions.push(gallery); + return; + } } - }); + const vsix = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.zip(extension.local!); + vsixs.push(vsix); + })); + + await Promise.all(galleryExtensions.map(gallery => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.installFromGallery(gallery))); + await Promise.all(vsixs.map(vsix => this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.install(vsix))); } } @@ -3428,20 +3256,20 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWith }); export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', { - dark: '#327e36', - light: '#327e36', + dark: buttonBackground, + light: buttonBackground, hc: null }, localize('extensionButtonProminentBackground', "Button background color for actions extension that stand out (e.g. install button).")); export const extensionButtonProminentForeground = registerColor('extensionButton.prominentForeground', { - dark: Color.white, - light: Color.white, + dark: buttonForeground, + light: buttonForeground, hc: null }, localize('extensionButtonProminentForeground', "Button foreground color for actions extension that stand out (e.g. install button).")); export const extensionButtonProminentHoverBackground = registerColor('extensionButton.prominentHoverBackground', { - dark: '#28632b', - light: '#28632b', + dark: buttonHoverBackground, + light: buttonHoverBackground, hc: null }, localize('extensionButtonProminentHoverBackground', "Button background hover color for actions extension that stand out (e.g. install button).")); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts b/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts new file mode 100644 index 00000000..55fea5dd --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const extensionsViewIcon = registerIcon('extensions-view-icon', Codicon.extensions, localize('extensionsViewIcon', 'View icon of the extensions view.')); + +export const manageExtensionIcon = registerIcon('extensions-manage', Codicon.gear, localize('manageExtensionIcon', 'Icon for the \'Manage\' action in the extensions view.')); + +export const clearSearchResultsIcon = registerIcon('extensions-clear-search-results', Codicon.clearAll, localize('clearSearchResultsIcon', 'Icon for the \'Clear Search Result\' action in the extensions view.')); +export const refreshIcon = registerIcon('extensions-refresh', Codicon.refresh, localize('refreshIcon', 'Icon for the \'Refresh\' action in the extensions view.')); +export const filterIcon = registerIcon('extensions-filter', Codicon.filter, localize('filterIcon', 'Icon for the \'Filter\' action in the extensions view.')); + +export const installLocalInRemoteIcon = registerIcon('extensions-install-local-in-remote', Codicon.cloudDownload, localize('installLocalInRemoteIcon', 'Icon for the \'Install Local Extension in Remote\' action in the extensions view.')); +export const installWorkspaceRecommendedIcon = registerIcon('extensions-install-workspace-recommended', Codicon.cloudDownload, localize('installWorkspaceRecommendedIcon', 'Icon for the \'Install Workspace Recommended Extensions\' action in the extensions view.')); +export const configureRecommendedIcon = registerIcon('extensions-configure-recommended', Codicon.pencil, localize('configureRecommendedIcon', 'Icon for the \'Configure Recommended Extensions\' action in the extensions view.')); + +export const syncEnabledIcon = registerIcon('extensions-sync-enabled', Codicon.sync, localize('syncEnabledIcon', 'Icon to indicate that an extension is synced.')); +export const syncIgnoredIcon = registerIcon('extensions-sync-ignored', Codicon.syncIgnored, localize('syncIgnoredIcon', 'Icon to indicate that an extension is ignored when syncing.')); +export const remoteIcon = registerIcon('extensions-remote', Codicon.remote, localize('remoteIcon', 'Icon to indicate that an extension is remote in the extensions view and editor.')); +export const installCountIcon = registerIcon('extensions-install-count', Codicon.cloudDownload, localize('installCountIcon', 'Icon shown along with the install count in the extensions view and editor.')); +export const ratingIcon = registerIcon('extensions-rating', Codicon.star, localize('ratingIcon', 'Icon shown along with the rating in the extensions view and editor.')); + +export const starFullIcon = registerIcon('extensions-star-full', Codicon.starFull, localize('starFullIcon', 'Full star icon used for the rating in the extensions editor.')); +export const starHalfIcon = registerIcon('extensions-star-half', Codicon.starHalf, localize('starHalfIcon', 'Half star icon used for the rating in the extensions editor.')); +export const starEmptyIcon = registerIcon('extensions-star-empty', Codicon.starEmpty, localize('starEmptyIcon', 'Empty star icon used for the rating in the extensions editor.')); + +export const warningIcon = registerIcon('extensions-warning-message', Codicon.warning, localize('warningIcon', 'Icon shown with a warning message in the extensions editor.')); +export const infoIcon = registerIcon('extensions-info-message', Codicon.info, localize('infoIcon', 'Icon shown with an info message in the extensions editor.')); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index f1aede07..563a5534 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -14,9 +14,9 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionViewItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, ExtensionToolTipAction, LocalInstallAction, SyncIgnoredIconAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionViewItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, ExtensionToolTipAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; +import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -26,6 +26,8 @@ import { foreground, listActiveSelectionForeground, listActiveSelectionBackgroun import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +export const EXTENSION_LIST_ELEMENT_HEIGHT = 62; + export interface IExtensionsViewState { onFocus: Event; onBlur: Event; @@ -47,7 +49,7 @@ export interface ITemplateData { } export class Delegate implements IListVirtualDelegate { - getHeight() { return 62; } + getHeight() { return EXTENSION_LIST_ELEMENT_HEIGHT; } getTemplateId() { return 'extension'; } } @@ -81,6 +83,7 @@ export class Renderer implements IPagedRenderer { const version = append(header, $('span.version')); const installCount = append(header, $('span.install-count')); const ratings = append(header, $('span.ratings')); + const syncIgnore = append(header, $('span.sync-ignored')); const headerRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, header, false); const description = append(details, $('.description.ellipsis')); const footer = append(details, $('.footer')); @@ -91,8 +94,8 @@ export class Renderer implements IPagedRenderer { if (action instanceof ActionWithDropDownAction) { return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } - if (action.id === ManageExtensionAction.ID) { - return (action).createActionViewItem(); + if (action instanceof ExtensionDropDownAction) { + return action.createActionViewItem(); } return new ExtensionActionViewItem(null, action, actionOptions); } @@ -103,13 +106,13 @@ export class Renderer implements IPagedRenderer { const reloadAction = this.instantiationService.createInstance(ReloadAction); const actions = [ this.instantiationService.createInstance(StatusLabelAction), - this.instantiationService.createInstance(SyncIgnoredIconAction), this.instantiationService.createInstance(UpdateAction), reloadAction, this.instantiationService.createInstance(InstallDropdownAction), this.instantiationService.createInstance(InstallingLabelAction), this.instantiationService.createInstance(RemoteInstallAction, false), this.instantiationService.createInstance(LocalInstallAction), + this.instantiationService.createInstance(WebInstallAction), this.instantiationService.createInstance(MaliciousStatusLabelAction, false), systemDisabledWarningAction, this.instantiationService.createInstance(ManageExtensionAction) @@ -123,6 +126,7 @@ export class Renderer implements IPagedRenderer { headerRemoteBadgeWidget, tooltipWidget, this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version), + this.instantiationService.createInstance(SyncIgnoredWidget, syncIgnore), this.instantiationService.createInstance(InstallCountWidget, installCount, true), this.instantiationService.createInstance(RatingsWidget, ratings, true) ]; @@ -220,10 +224,14 @@ export class Renderer implements IPagedRenderer { }); } }, this, data.extensionDisposables); + } + disposeElement(extension: IExtension, index: number, data: ITemplateData): void { + data.extensionDisposables = dispose(data.extensionDisposables); } disposeTemplate(data: ITemplateData): void { + data.extensionDisposables = dispose(data.extensionDisposables); data.disposables = dispose(data.disposables); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 9cd1effb..afe9ebc8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -9,7 +9,7 @@ import { timeout, Delayer } from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; -import { Event as EventOf, Emitter } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IAction, Action, Separator, SubmenuAction } from 'vs/base/common/actions'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -22,20 +22,20 @@ import { ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction, RecentlyPublishedExtensionsAction, ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ShowDisabledExtensionsAction, - ShowEnabledExtensionsAction, PredefinedExtensionFilterAction + ShowEnabledExtensionsAction, PredefinedExtensionFilterAction, RefreshExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; -import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView, OutdatedExtensionsView, InstalledExtensionsView, SearchBuiltInExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews'; +import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerInstalledExtensionsView, DefaultRecommendedExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import Severity from 'vs/base/common/severity'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewDescriptorService, IAddedViewDescriptorRef } from 'vs/workbench/common/views'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -61,6 +61,9 @@ import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { isWeb } from 'vs/base/common/platform'; +import { memoize } from 'vs/base/common/decorators'; +import { filterIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); @@ -69,6 +72,7 @@ const SearchOutdatedExtensionsContext = new RawContextKey('searchOutdat const SearchEnabledExtensionsContext = new RawContextKey('searchEnabledExtensions', false); const SearchDisabledExtensionsContext = new RawContextKey('searchDisabledExtensions', false); const HasInstalledExtensionsContext = new RawContextKey('hasInstalledExtensions', true); +const HasInstalledWebExtensionsContext = new RawContextKey('hasInstalledWebExtensions', false); const BuiltInExtensionsContext = new RawContextKey('builtInExtensions', false); const SearchBuiltInExtensionsContext = new RawContextKey('searchBuiltInExtensions', false); const RecommendedExtensionsContext = new RawContextKey('recommendedExtensions', false); @@ -80,7 +84,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio constructor( @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @ILabelService private readonly labelService: ILabelService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { this.container = viewDescriptorService.getViewContainerById(VIEWLET_ID)!; this.registerViews(); @@ -107,20 +112,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio private createDefaultExtensionsViewDescriptors(): IViewDescriptor[] { const viewDescriptors: IViewDescriptor[] = []; - /* - * Default popular extensions view - * Separate view for popular extensions required as we need to show popular and recommended sections - * in the default view when there is no search text, and user has no installed extensions. - */ - viewDescriptors.push({ - id: 'workbench.views.extensions.popular', - name: localize('popularExtensions', "Popular"), - ctorDescriptor: new SyncDescriptor(ExtensionsListView), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), - weight: 60, - order: 1, - }); - /* * Default installed extensions views - Shows all user installed extensions. */ @@ -135,27 +126,70 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio servers.push(this.extensionManagementServerService.remoteExtensionManagementServer); } const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => { - if (servers.length) { - const serverLabel = server === this.extensionManagementServerService.webExtensionManagementServer && !this.extensionManagementServerService.localExtensionManagementServer ? localize('local', "Local") : server.label; - return servers.length > 1 ? `${serverLabel} - ${viewTitle}` : viewTitle; + if (servers.length > 1) { + // In Web, use view title as is for remote server, when web extension server is enabled and no web extensions are installed + if (isWeb && server === this.extensionManagementServerService.remoteExtensionManagementServer && + this.extensionManagementServerService.webExtensionManagementServer && !this.contextKeyService.getContextKeyValue('hasInstalledWebExtensions')) { + return viewTitle; + } + return `${server.label} - ${viewTitle}`; } return viewTitle; }; + let installedWebExtensionsContextChangeEvent = Event.None; + if (this.extensionManagementServerService.webExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { + const interestingContextKeys = new Set(); + interestingContextKeys.add('hasInstalledWebExtensions'); + installedWebExtensionsContextChangeEvent = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(interestingContextKeys)); + } + const serverLabelChangeEvent = Event.any(this.labelService.onDidChangeFormatters, installedWebExtensionsContextChangeEvent); for (const server of servers) { const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server); - const onDidChangeServerLabel: EventOf = EventOf.map(this.labelService.onDidChangeFormatters, () => undefined); + const onDidChangeTitle = Event.map(serverLabelChangeEvent, () => getInstalledViewName()); + const id = servers.length > 1 ? `workbench.views.extensions.${server.id}.installed` : `workbench.views.extensions.installed`; + const isWebServer = server === this.extensionManagementServerService.webExtensionManagementServer; + if (!isWebServer) { + /* Empty installed extensions view */ + viewDescriptors.push({ + id: `${id}.empty`, + get name() { return getInstalledViewName(); }, + weight: 100, + order: 1, + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), + /* Empty installed extensions view shall have fixed height */ + ctorDescriptor: new SyncDescriptor(ServerInstalledExtensionsView, [{ server, fixedHeight: true, onDidChangeTitle }]), + /* Empty installed extensions views shall not be allowed to hidden */ + canToggleVisibility: false + }); + } + /* Installed extensions view */ viewDescriptors.push({ - id: servers.length > 1 ? `workbench.views.extensions.${server.id}.installed` : `workbench.views.extensions.installed`, + id, get name() { return getInstalledViewName(); }, - ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]), - when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), weight: 100, - order: 2, - /* Installed extensions views shall not be hidden when there are more than one server */ + order: 1, + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), isWebServer ? ContextKeyExpr.has('hasInstalledWebExtensions') : ContextKeyExpr.has('hasInstalledExtensions')), + ctorDescriptor: new SyncDescriptor(ServerInstalledExtensionsView, [{ server, onDidChangeTitle }]), + /* Installed extensions views shall not be allowed to hidden when there are more than one server */ canToggleVisibility: servers.length === 1 }); } + /* + * Default popular extensions view + * Separate view for popular extensions required as we need to show popular and recommended sections + * in the default view when there is no search text, and user has no installed extensions. + */ + viewDescriptors.push({ + id: 'workbench.views.extensions.popular', + name: localize('popularExtensions', "Popular"), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), + when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')), + weight: 60, + order: 2, + canToggleVisibility: false + }); + /* * Default recommended extensions view * When user has installed extensions, this is shown along with the views for enabled & disabled extensions @@ -164,7 +198,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'extensions.recommendedList', name: localize('recommendedExtensions', "Recommended"), - ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView), + ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand')), weight: 40, order: 3, @@ -180,7 +214,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.enabled', name: localize('enabledExtensions', "Enabled"), - ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), + ctorDescriptor: new SyncDescriptor(EnabledExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), hideByDefault: true, weight: 40, @@ -195,7 +229,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.disabled', name: localize('disabledExtensions', "Disabled"), - ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), + ctorDescriptor: new SyncDescriptor(DisabledExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')), hideByDefault: true, weight: 10, @@ -217,7 +251,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.marketplace', name: localize('marketPlace', "Marketplace"), - ctorDescriptor: new SyncDescriptor(ExtensionsListView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')), }); @@ -227,7 +261,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchInstalled', name: localize('installed', "Installed"), - ctorDescriptor: new SyncDescriptor(InstalledExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')), }); @@ -237,7 +271,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchEnabled', name: localize('enabled', "Enabled"), - ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')), }); @@ -247,7 +281,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchDisabled', name: localize('disabled', "Disabled"), - ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')), }); @@ -257,7 +291,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchOutdated', name: localize('outdated', "Outdated"), - ctorDescriptor: new SyncDescriptor(OutdatedExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')), }); @@ -267,7 +301,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.searchBuiltin', name: localize('builtin', "Builtin"), - ctorDescriptor: new SyncDescriptor(SearchBuiltInExtensionsView), + ctorDescriptor: new SyncDescriptor(ExtensionsListView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('searchBuiltInExtensions')), }); @@ -280,7 +314,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.workspaceRecommendations', name: localize('workspaceRecommendedExtensions', "Workspace Recommendations"), - ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView), + ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView, [{}]), when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), WorkbenchStateContext.notEqualsTo('empty')), order: 1 }); @@ -288,7 +322,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.otherRecommendations', name: localize('otherRecommendedExtensions', "Other Recommendations"), - ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView), + ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView, [{}]), when: ContextKeyExpr.has('recommendedExtensions'), order: 2 }); @@ -302,21 +336,21 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push({ id: 'workbench.views.extensions.builtinFeatureExtensions', name: localize('builtinFeatureExtensions', "Features"), - ctorDescriptor: new SyncDescriptor(BuiltInFeatureExtensionsView), + ctorDescriptor: new SyncDescriptor(BuiltInFeatureExtensionsView, [{}]), when: ContextKeyExpr.has('builtInExtensions'), }); viewDescriptors.push({ id: 'workbench.views.extensions.builtinThemeExtensions', name: localize('builtInThemesExtensions', "Themes"), - ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView), + ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView, [{}]), when: ContextKeyExpr.has('builtInExtensions'), }); viewDescriptors.push({ id: 'workbench.views.extensions.builtinProgrammingLanguageExtensions', name: localize('builtinProgrammingLanguageExtensions', "Programming Languages"), - ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView), + ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView, [{}]), when: ContextKeyExpr.has('builtInExtensions'), }); @@ -328,7 +362,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IExtensionsViewPaneContainer { private readonly _onSearchChange: Emitter = this._register(new Emitter()); - private readonly onSearchChange: EventOf = this._onSearchChange.event; + private readonly onSearchChange: Event = this._onSearchChange.event; private defaultViewsContextKey: IContextKey; private searchMarketplaceExtensionsContextKey: IContextKey; private searchInstalledExtensionsContextKey: IContextKey; @@ -336,6 +370,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private searchEnabledExtensionsContextKey: IContextKey; private searchDisabledExtensionsContextKey: IContextKey; private hasInstalledExtensionsContextKey: IContextKey; + private hasInstalledWebExtensionsContextKey: IContextKey; private builtInExtensionsContextKey: IContextKey; private searchBuiltInExtensionsContextKey: IContextKey; private recommendedExtensionsContextKey: IContextKey; @@ -345,6 +380,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private searchBox: SuggestEnabledInput | undefined; private readonly searchViewletState: MementoObject; private readonly sortActions: ChangeSortAction[]; + private secondaryActions: IAction[] | undefined = undefined; constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -352,7 +388,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IProgressService private readonly progressService: IProgressService, @IInstantiationService instantiationService: IInstantiationService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @INotificationService private readonly notificationService: INotificationService, @IViewletService private readonly viewletService: IViewletService, @@ -377,22 +414,30 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService); this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService); this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService); + this.hasInstalledWebExtensionsContextKey = HasInstalledWebExtensionsContext.bindTo(contextKeyService); this.builtInExtensionsContextKey = BuiltInExtensionsContext.bindTo(contextKeyService); this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService); this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService); this._register(this.viewletService.onDidViewletOpen(this.onViewletOpen, this)); - this.searchViewletState = this.getMemento(StorageScope.WORKSPACE); - - this.extensionManagementService.getInstalled().then(result => { - this.hasInstalledExtensionsContextKey.set(result.some(r => !r.isBuiltin)); - }); + this.searchViewletState = this.getMemento(StorageScope.WORKSPACE, StorageTarget.USER); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { + this.secondaryActions = undefined; this.updateTitleArea(); } }, this)); + if (extensionManagementServerService.webExtensionManagementServer) { + this._register(extensionsWorkbenchService.onChange(() => { + // show installed web extensions view only when it is not visible + // Do not hide the view automatically when it is visible + if (!this.hasInstalledWebExtensionsContextKey.get()) { + this.updateInstalledWebExtensionsContext(); + } + })); + } + this.sortActions = [ this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Install Count"), this.onSearchChange, 'installs')), this._register(this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Rating"), this.onSearchChange, 'rating')), @@ -425,6 +470,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE provideResults: (query: string) => Query.suggestions(query) }, placeholder, 'extensions:searchinput', { placeholderText: placeholder, value: searchValue })); + this.updateInstalledExtensionsContexts(); if (this.searchBox.getValue()) { this.triggerSearch(); } @@ -510,71 +556,80 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE return 400; } + @memoize getActions(): IAction[] { - const filterActions: IAction[] = []; - // Local extensions filters - filterActions.push(...[ - this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, localize('builtin filter', "Built-in")), - this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, localize('installed filter', "Installed")), - this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, localize('enabled filter', "Enabled")), - this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, localize('disabled filter', "Disabled")), - this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, localize('outdated filter', "Outdated")), - ]); + let filterActions: IAction[] = [ + this._register(this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, localize('builtin filter', "Built-in"))), + this._register(this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, localize('installed filter', "Installed"))), + this._register(this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, localize('enabled filter', "Enabled"))), + this._register(this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, localize('disabled filter', "Disabled"))), + this._register(this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, localize('outdated filter', "Outdated"))), + ]; if (this.extensionGalleryService.isEnabled()) { - const galleryFilterActions = [ - this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.featured', localize('featured filter', "Featured"), '@featured'), - this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.popular', localize('most popular filter', "Most Popular"), '@popular'), - this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.recommended', localize('most popular recommended', "Recommended"), '@recommended'), - this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published")), - new Separator(), - new SubmenuAction('workbench.extensions.action.filterExtensionsByCategory', localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category))), - new Separator(), + filterActions = [ + this._register(this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.featured', localize('featured filter', "Featured"), '@featured')), + this._register(this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.popular', localize('most popular filter', "Most Popular"), '@popular')), + this._register(this.instantiationService.createInstance(PredefinedExtensionFilterAction, 'extensions.filter.recommended', localize('most popular recommended', "Recommended"), '@recommended')), + this._register(this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published"))), + this._register(new Separator()), + this._register(new SubmenuAction('workbench.extensions.action.filterExtensionsByCategory', localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category)))), + this._register(new Separator()), + ...filterActions, + this._register(new Separator()), + this._register(new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), this.sortActions)), ]; - filterActions.splice(0, 0, ...galleryFilterActions); - filterActions.push(...[ - new Separator(), - new SubmenuAction('workbench.extensions.action.sortBy', localize('sorty by', "Sort By"), this.sortActions), - ]); } return [ - new SubmenuAction('workbench.extensions.action.filterExtensions', localize('filterExtensions', "Filter Extensions..."), filterActions, 'codicon-filter'), - this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : ''), + this._register(new SubmenuAction('workbench.extensions.action.filterExtensions', localize('filterExtensions', "Filter Extensions..."), filterActions, ThemeIcon.asClassName(filterIcon))), + this._register(this.instantiationService.createInstance(RefreshExtensionsAction, RefreshExtensionsAction.ID, RefreshExtensionsAction.LABEL)), + this._register(this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, () => this.searchBox!.getValue() || '')), ]; } getSecondaryActions(): IAction[] { - const actions: IAction[] = []; + if (!this.secondaryActions) { + this.secondaryActions = []; + this.secondaryActions.push(this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL)); + if (this.configurationService.getValue(AutoUpdateConfigurationKey)) { + this.secondaryActions.push(this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)); + } else { + this.secondaryActions.push(this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL, false), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)); + } - actions.push(this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL)); - if (this.configurationService.getValue(AutoUpdateConfigurationKey)) { - actions.push(this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)); - } else { - actions.push(this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL, false), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)); + this.secondaryActions.push(new Separator()); + this.secondaryActions.push(this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL, false)); + this.secondaryActions.push(this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL, false)); + + this.secondaryActions.push(new Separator()); + this.secondaryActions.push(this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL)); } - - actions.push(new Separator()); - actions.push(this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL, false)); - actions.push(this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL, false)); - - actions.push(new Separator()); - actions.push(this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL)); - - return actions; + return this.secondaryActions; } - search(value: string, refresh: boolean = false): void { - if (this.searchBox) { - if (this.searchBox.getValue() !== value) { - this.searchBox.setValue(value); - } else if (refresh) { - this.doSearch(); - } + search(value: string): void { + if (this.searchBox && this.searchBox.getValue() !== value) { + this.searchBox.setValue(value); } } + async refresh(): Promise { + await this.updateInstalledExtensionsContexts(); + this.doSearch(true); + } + + private async updateInstalledExtensionsContexts(): Promise { + const result = await this.extensionsWorkbenchService.queryLocal(); + this.hasInstalledExtensionsContextKey.set(result.some(r => !r.isBuiltin)); + this.updateInstalledWebExtensionsContext(); + } + + private updateInstalledWebExtensionsContext(): void { + this.hasInstalledWebExtensionsContextKey.set(!!this.extensionManagementServerService.webExtensionManagementServer && this.extensionsWorkbenchService.installed.some(r => r.server === this.extensionManagementServerService.webExtensionManagementServer)); + } + private triggerSearch(): void { this.searchDelayer.trigger(() => this.doSearch(), this.searchBox && this.searchBox.getValue() ? 500 : 0).then(undefined, err => this.onError(err)); } @@ -601,7 +656,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE super.saveState(); } - private doSearch(): Promise { + private doSearch(refresh?: boolean): Promise { const value = this.normalizedQuery(); const isRecommendedExtensionsQuery = ExtensionsListView.isRecommendedExtensionsQuery(value); this.searchInstalledExtensionsContextKey.set(ExtensionsListView.isInstalledExtensionsQuery(value)); @@ -613,9 +668,10 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery); this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery); this.defaultViewsContextKey.set(!value); + this.updateInstalledWebExtensionsContext(); return this.progress(Promise.all(this.panes.map(view => - (view).show(this.normalizedQuery()) + (view).show(this.normalizedQuery(), refresh) .then(model => this.alertSearchResult(model.length, view.id)) ))).then(() => undefined); } @@ -765,7 +821,7 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { .filter(e => maliciousSet.has(e.identifier.id)); if (maliciousExtensions.length) { - return Promise.all(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e, true).then(() => { + return Promise.all(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e).then(() => { this.notificationService.prompt( Severity.Warning, localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", e.identifier.id), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 8dc0c106..932a76b6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; @@ -16,11 +16,11 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { append, $ } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList'; -import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { Delegate, Renderer, IExtensionsViewState, EXTENSION_LIST_ELEMENT_HEIGHT } from 'vs/workbench/contrib/extensions/browser/extensionsList'; +import { ExtensionState, IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -44,11 +44,12 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { IMenuService } from 'vs/platform/actions/common/actions'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { configureRecommendedIcon, installLocalInRemoteIcon, installWorkspaceRecommendedIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; // Extensions that are automatically classified as Programming Language extensions, but should be Feature extensions const FORCE_FEATURE_EXTENSIONS = ['vscode.git', 'vscode.search-result']; @@ -74,15 +75,22 @@ class ExtensionsViewState extends Disposable implements IExtensionsViewState { } } -export interface ExtensionsListViewOptions extends IViewletViewOptions { +export interface ExtensionsListViewOptions { server?: IExtensionManagementServer; + fixedHeight?: boolean; + onDidChangeTitle?: Event; } class ExtensionListViewWarning extends Error { } +interface IQueryResult { + readonly model: IPagedModel; + readonly onDidChangeModel?: Event>; + readonly disposables: DisposableStore; +} + export class ExtensionsListView extends ViewPane { - protected readonly server: IExtensionManagementServer | undefined; private bodyTemplate: { messageContainer: HTMLElement; messageSeverityIcon: HTMLElement; @@ -92,9 +100,11 @@ export class ExtensionsListView extends ViewPane { private badge: CountBadge | undefined; private list: WorkbenchPagedList | null = null; private queryRequest: { query: string, request: CancelablePromise> } | null = null; + private queryResult: IQueryResult | undefined; constructor( - options: ExtensionsListViewOptions, + protected readonly options: ExtensionsListViewOptions, + viewletViewOptions: IViewletViewOptions, @INotificationService protected notificationService: INotificationService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @@ -112,12 +122,18 @@ export class ExtensionsListView extends ViewPane { @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IMenuService private readonly menuService: IMenuService, @IOpenerService openerService: IOpenerService, @IPreferencesService private readonly preferencesService: IPreferencesService, + @IStorageService private readonly storageService: IStorageService, ) { - super({ ...(options as IViewPaneOptions), showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this.server = options.server; + super({ + ...(viewletViewOptions as IViewPaneOptions), + showActionsAlways: true, + maximumBodySize: options.fixedHeight ? storageService.getNumber(viewletViewOptions.id, StorageScope.GLOBAL, 0) : undefined + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + if (this.options.onDidChangeTitle) { + this._register(this.options.onDidChangeTitle(title => this.updateTitle(title))); + } } protected renderHeader(container: HTMLElement): void { @@ -161,7 +177,7 @@ export class ExtensionsListView extends ViewPane { const resourceNavigator = this._register(new ListResourceNavigator(this.list, { openOnSingleClick: true })); this._register(Event.debounce(Event.filter(resourceNavigator.onDidOpen, e => e.element !== null), (_, event) => event, 75, true)(options => { - this.openExtension(this.list!.model.get(options.element!), { sideByside: options.sideBySide, ...options.editorOptions }); + this.openExtension(options.element!, { sideByside: options.sideBySide, ...options.editorOptions }); })); this.bodyTemplate = { @@ -182,15 +198,20 @@ export class ExtensionsListView extends ViewPane { } } - async show(query: string): Promise> { + async show(query: string, refresh?: boolean): Promise> { if (this.queryRequest) { - if (this.queryRequest.query === query) { + if (!refresh && this.queryRequest.query === query) { return this.queryRequest.request; } this.queryRequest.request.cancel(); this.queryRequest = null; } + if (this.queryResult) { + this.queryResult.disposables.dispose(); + this.queryResult = undefined; + } + const parsedQuery = Query.parse(query); let options: IQueryOptions = { @@ -204,23 +225,25 @@ export class ExtensionsListView extends ViewPane { case 'publishedDate': options.sortBy = SortBy.PublishedDate; break; } - const successCallback = (model: IPagedModel) => { - this.queryRequest = null; - this.setModel(model); - return model; - }; - - - const errorCallback = (e: any) => { - const model = new PagedModel([]); - if (!isPromiseCanceledError(e)) { - this.queryRequest = null; - this.setModel(model, e); + const request = createCancelablePromise(async token => { + try { + this.queryResult = await this.query(parsedQuery, options, token); + const model = this.queryResult.model; + this.setModel(model); + if (this.queryResult.onDidChangeModel) { + this.queryResult.disposables.add(this.queryResult.onDidChangeModel(model => this.updateModel(model))); + } + return model; + } catch (e) { + const model = new PagedModel([]); + if (!isPromiseCanceledError(e)) { + this.setModel(model, e); + } + return this.list ? this.list.model : model; } - return this.list ? this.list.model : model; - }; + }); - const request = createCancelablePromise(token => this.query(parsedQuery, options, token).then(successCallback).catch(errorCallback)); + request.finally(() => this.queryRequest = null); this.queryRequest = { query, request }; return request; } @@ -251,7 +274,7 @@ export class ExtensionsListView extends ViewPane { getActions: () => actions.slice(0, actions.length - 1) }); } else if (e.element) { - const groups = getContextMenuActions(this.menuService, this.contextKeyService.createScoped(), this.instantiationService, e.element); + const groups = getContextMenuActions(e.element, false, this.instantiationService); groups.forEach(group => group.forEach(extensionAction => { if (extensionAction instanceof ExtensionAction) { extensionAction.extension = e.element!; @@ -269,7 +292,7 @@ export class ExtensionsListView extends ViewPane { } } - private async query(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { + private async query(query: Query, options: IQueryOptions, token: CancellationToken): Promise { const idRegex = /@id:(([a-z0-9A-Z][a-z0-9\-A-Z]*)\.([a-z0-9A-Z][a-z0-9\-A-Z]*))/g; const ids: string[] = []; let idMatch; @@ -278,21 +301,26 @@ export class ExtensionsListView extends ViewPane { ids.push(name); } if (ids.length) { - return this.queryByIds(ids, options, token); + const model = await this.queryByIds(ids, options, token); + return { model, disposables: new DisposableStore() }; } - if (ExtensionsListView.isLocalExtensionsQuery(query.value) || /@builtin/.test(query.value)) { + + if (ExtensionsListView.isLocalExtensionsQuery(query.value)) { return this.queryLocal(query, options); } - return this.queryGallery(query, options, token) - .then(null, e => { - console.warn('Error querying extensions gallery', getErrorMessage(e)); - return Promise.reject(new ExtensionListViewWarning(localize('galleryError', "We cannot connect to the Extensions Marketplace at this time, please try again later."))); - }); + + try { + const model = await this.queryGallery(query, options, token); + return { model, disposables: new DisposableStore() }; + } catch (e) { + console.warn('Error querying extensions gallery', getErrorMessage(e)); + return Promise.reject(new ExtensionListViewWarning(localize('galleryError', "We cannot connect to the Extensions Marketplace at this time, please try again later."))); + } } private async queryByIds(ids: string[], options: IQueryOptions, token: CancellationToken): Promise> { const idsSet: Set = ids.reduce((result, id) => { result.add(id.toLowerCase()); return result; }, new Set()); - const result = (await this.extensionsWorkbenchService.queryLocal(this.server)) + const result = (await this.extensionsWorkbenchService.queryLocal(this.options.server)) .filter(e => idsSet.has(e.identifier.id.toLowerCase())); if (result.length) { @@ -303,32 +331,70 @@ export class ExtensionsListView extends ViewPane { .then(pager => this.getPagedModel(pager)); } - private async queryLocal(query: Query, options: IQueryOptions): Promise> { - let value = query.value; - if (/@builtin/i.test(value)) { - return this.queryBuiltinExtensions(query, options); + private async queryLocal(query: Query, options: IQueryOptions): Promise { + const local = await this.extensionsWorkbenchService.queryLocal(this.options.server); + const runningExtensions = await this.extensionService.getExtensions(); + let { extensions, canIncludeInstalledExtensions } = this.filterLocal(local, runningExtensions, query, options); + const disposables = new DisposableStore(); + const onDidChangeModel = disposables.add(new Emitter>()); + + if (canIncludeInstalledExtensions) { + let isDisposed: boolean = false; + disposables.add(toDisposable(() => isDisposed = true)); + disposables.add(Event.debounce(Event.any( + Event.filter(this.extensionsWorkbenchService.onChange, e => e?.state === ExtensionState.Installed), + this.extensionService.onDidChangeExtensions + ), () => undefined)(async () => { + const local = this.options.server ? this.extensionsWorkbenchService.installed.filter(e => e.server === this.options.server) : this.extensionsWorkbenchService.local; + const runningExtensions = await this.extensionService.getExtensions(); + const { extensions: newExtensions } = this.filterLocal(local, runningExtensions, query, options); + if (!isDisposed) { + const mergedExtensions = this.mergeAddedExtensions(extensions, newExtensions); + if (mergedExtensions) { + extensions = mergedExtensions; + onDidChangeModel.fire(new PagedModel(extensions)); + } + } + })); } - if (/@installed/i.test(value)) { - return this.queryInstalledExtensions(query, options); - } - - if (/@outdated/i.test(value)) { - return this.queryOutdatedExtensions(query, options); - } - - if (/@disabled/i.test(value)) { - return this.queryDisabledExtensions(query, options); - } - - if (/@enabled/i.test(value)) { - return this.queryEnabledExtensions(query, options); - } - - return new PagedModel([]); + return { + model: new PagedModel(extensions), + onDidChangeModel: onDidChangeModel.event, + disposables + }; } - private async queryBuiltinExtensions(query: Query, options: IQueryOptions): Promise> { + private filterLocal(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): { extensions: IExtension[], canIncludeInstalledExtensions: boolean } { + let value = query.value; + let extensions: IExtension[] = []; + let canIncludeInstalledExtensions = true; + + if (/@builtin/i.test(value)) { + extensions = this.filterBuiltinExtensions(local, query, options); + canIncludeInstalledExtensions = false; + } + + else if (/@installed/i.test(value)) { + extensions = this.filterInstalledExtensions(local, runningExtensions, query, options); + } + + else if (/@outdated/i.test(value)) { + extensions = this.filterOutdatedExtensions(local, query, options); + } + + else if (/@disabled/i.test(value)) { + extensions = this.filterDisabledExtensions(local, runningExtensions, query, options); + } + + else if (/@enabled/i.test(value)) { + extensions = this.filterEnabledExtensions(local, runningExtensions, query, options); + } + + return { extensions, canIncludeInstalledExtensions }; + } + + private filterBuiltinExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] { let value = query.value; const showThemesOnly = /@builtin:themes/i.test(value); if (showThemesOnly) { @@ -344,9 +410,8 @@ export class ExtensionsListView extends ViewPane { } value = value.replace(/@builtin/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - let result = await this.extensionsWorkbenchService.queryLocal(this.server); - result = result + let result = local .filter(e => e.isBuiltin && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)); const isThemeExtension = (e: IExtension): boolean => { @@ -355,7 +420,7 @@ export class ExtensionsListView extends ViewPane { }; if (showThemesOnly) { const themesExtensions = result.filter(isThemeExtension); - return this.getPagedModel(this.sortExtensions(themesExtensions, options)); + return this.sortExtensions(themesExtensions, options); } const isLangaugeBasicExtension = (e: IExtension): boolean => { @@ -364,7 +429,7 @@ export class ExtensionsListView extends ViewPane { }; if (showBasicsOnly) { const basics = result.filter(isLangaugeBasicExtension); - return this.getPagedModel(this.sortExtensions(basics, options)); + return this.sortExtensions(basics, options); } if (showFeaturesOnly) { const others = result.filter(e => { @@ -373,10 +438,10 @@ export class ExtensionsListView extends ViewPane { && !isThemeExtension(e) && !isLangaugeBasicExtension(e); }); - return this.getPagedModel(this.sortExtensions(others, options)); + return this.sortExtensions(others, options); } - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); } private parseCategories(value: string): { value: string, categories: string[] } { @@ -391,14 +456,12 @@ export class ExtensionsListView extends ViewPane { return { value, categories }; } - private async queryInstalledExtensions(query: Query, options: IQueryOptions): Promise> { + private filterInstalledExtensions(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - let result = await this.extensionsWorkbenchService.queryLocal(this.server); - - result = result + let result = local .filter(e => !e.isBuiltin && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); @@ -406,7 +469,6 @@ export class ExtensionsListView extends ViewPane { if (options.sortBy !== undefined) { result = this.sortExtensions(result, options); } else { - const runningExtensions = await this.extensionService.getExtensions(); const runningExtensionsById = runningExtensions.reduce((result, e) => { result.set(ExtensionIdentifier.toKey(e.identifier.value), e); return result; }, new Map()); result = result.sort((e1, e2) => { const running1 = runningExtensionsById.get(ExtensionIdentifier.toKey(e1.identifier.id)); @@ -433,56 +495,76 @@ export class ExtensionsListView extends ViewPane { return isE1Running ? -1 : 1; }); } - return this.getPagedModel(result); + return result; } - private async queryOutdatedExtensions(query: Query, options: IQueryOptions): Promise> { + private filterOutdatedExtensions(local: IExtension[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value.replace(/@outdated/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - const local = await this.extensionsWorkbenchService.queryLocal(this.server); const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) .filter(extension => extension.outdated && (extension.name.toLowerCase().indexOf(value) > -1 || extension.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => !!extension.local && extension.local.manifest.categories!.some(c => c.toLowerCase() === category)))); - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); } - private async queryDisabledExtensions(query: Query, options: IQueryOptions): Promise> { + private filterDisabledExtensions(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value.replace(/@disabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - const local = await this.extensionsWorkbenchService.queryLocal(this.server); - const runningExtensions = await this.extensionService.getExtensions(); - const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) .filter(e => runningExtensions.every(r => !areSameExtensions({ id: r.identifier.value, uuid: r.uuid }, e.identifier)) && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); } - private async queryEnabledExtensions(query: Query, options: IQueryOptions): Promise> { + private filterEnabledExtensions(local: IExtension[], runningExtensions: IExtensionDescription[], query: Query, options: IQueryOptions): IExtension[] { let { value, categories } = this.parseCategories(query.value); value = value ? value.replace(/@enabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : ''; - const local = (await this.extensionsWorkbenchService.queryLocal(this.server)).filter(e => !e.isBuiltin); - const runningExtensions = await this.extensionService.getExtensions(); - + local = local.filter(e => !e.isBuiltin); const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) .filter(e => runningExtensions.some(r => areSameExtensions({ id: r.identifier.value, uuid: r.uuid }, e.identifier)) && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); - return this.getPagedModel(this.sortExtensions(result, options)); + return this.sortExtensions(result, options); + } + + private mergeAddedExtensions(extensions: IExtension[], newExtensions: IExtension[]): IExtension[] | undefined { + const oldExtensions = [...extensions]; + const findPreviousExtensionIndex = (from: number): number => { + let index = -1; + const previousExtensionInNew = newExtensions[from]; + if (previousExtensionInNew) { + index = oldExtensions.findIndex(e => areSameExtensions(e.identifier, previousExtensionInNew.identifier)); + if (index === -1) { + return findPreviousExtensionIndex(from - 1); + } + } + return index; + }; + + let hasChanged: boolean = false; + for (let index = 0; index < newExtensions.length; index++) { + const extension = newExtensions[index]; + if (extensions.every(r => !areSameExtensions(r.identifier, extension.identifier))) { + hasChanged = true; + extensions.splice(findPreviousExtensionIndex(index - 1) + 1, 0, extension); + } + } + + return hasChanged ? extensions : undefined; } private async queryGallery(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { @@ -675,7 +757,7 @@ export class ExtensionsListView extends ViewPane { private async getOtherRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { const value = query.value.replace(/@recommended/g, '').trim().toLowerCase(); - const local = (await this.extensionsWorkbenchService.queryLocal(this.server)) + const local = (await this.extensionsWorkbenchService.queryLocal(this.options.server)) .map(e => e.identifier.id.toLowerCase()); const workspaceRecommendations = (await this.getWorkspaceRecommendations()) .map(extensionId => extensionId.toLowerCase()); @@ -698,7 +780,7 @@ export class ExtensionsListView extends ViewPane { // Get All types of recommendations, trimmed to show a max of 8 at any given time private async getAllRecommendationsModel(query: Query, options: IQueryOptions, token: CancellationToken): Promise> { - const local = (await this.extensionsWorkbenchService.queryLocal(this.server)).map(e => e.identifier.id.toLowerCase()); + const local = (await this.extensionsWorkbenchService.queryLocal(this.options.server)).map(e => e.identifier.id.toLowerCase()); const allRecommendations = distinct( flatten(await Promise.all([ @@ -727,31 +809,51 @@ export class ExtensionsListView extends ViewPane { if (this.list) { this.list.model = new DelayedPagedModel(model); this.list.scrollTop = 0; - const count = this.count(); + this.updateBody(error); + } + } - if (this.bodyTemplate && this.badge) { + private updateBody(error?: any): void { + const count = this.count(); + if (this.bodyTemplate && this.badge) { - this.bodyTemplate.extensionsList.classList.toggle('hidden', count === 0); - this.bodyTemplate.messageContainer.classList.toggle('hidden', count > 0); - this.badge.setCount(count); + this.bodyTemplate.extensionsList.classList.toggle('hidden', count === 0); + this.bodyTemplate.messageContainer.classList.toggle('hidden', count > 0); + this.badge.setCount(count); - if (count === 0 && this.isBodyVisible()) { - if (error) { - if (error instanceof ExtensionListViewWarning) { - this.bodyTemplate.messageSeverityIcon.className = `codicon ${SeverityIcon.className(Severity.Warning)}`; - this.bodyTemplate.messageBox.textContent = getErrorMessage(error); - } else { - this.bodyTemplate.messageSeverityIcon.className = `codicon ${SeverityIcon.className(Severity.Error)}`; - this.bodyTemplate.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error)); - } + if (count === 0 && this.isBodyVisible()) { + if (error) { + if (error instanceof ExtensionListViewWarning) { + this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Warning); + this.bodyTemplate.messageBox.textContent = getErrorMessage(error); } else { - this.bodyTemplate.messageSeverityIcon.className = ''; - this.bodyTemplate.messageBox.textContent = localize('no extensions found', "No extensions found."); + this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Error); + this.bodyTemplate.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error)); } - alert(this.bodyTemplate.messageBox.textContent); + } else { + this.bodyTemplate.messageSeverityIcon.className = ''; + this.bodyTemplate.messageBox.textContent = localize('no extensions found', "No extensions found."); } + alert(this.bodyTemplate.messageBox.textContent); } } + this.updateSize(); + } + + protected updateSize() { + if (this.options.fixedHeight) { + const length = this.list?.model.length || 0; + this.minimumBodySize = Math.min(length, 3) * EXTENSION_LIST_ELEMENT_HEIGHT; + this.maximumBodySize = length * EXTENSION_LIST_ELEMENT_HEIGHT; + this.storageService.store(this.id, this.maximumBodySize, StorageScope.GLOBAL, StorageTarget.MACHINE); + } + } + + private updateModel(model: IPagedModel) { + if (this.list) { + this.list.model = new DelayedPagedModel(model); + this.updateBody(); + } } private openExtension(extension: IExtension, options: { sideByside?: boolean, preserveFocus?: boolean, pinned?: boolean }): void { @@ -799,6 +901,10 @@ export class ExtensionsListView extends ViewPane { this.queryRequest.request.cancel(); this.queryRequest = null; } + if (this.queryResult) { + this.queryResult.disposables.dispose(); + this.queryResult = undefined; + } this.list = null; } @@ -808,7 +914,8 @@ export class ExtensionsListView extends ViewPane { || this.isEnabledExtensionsQuery(query) || this.isDisabledExtensionsQuery(query) || this.isBuiltInExtensionsQuery(query) - || this.isSearchBuiltInExtensionsQuery(query); + || this.isSearchBuiltInExtensionsQuery(query) + || this.isBuiltInGroupExtensionsQuery(query); } static isSearchBuiltInExtensionsQuery(query: string): boolean { @@ -819,6 +926,10 @@ export class ExtensionsListView extends ViewPane { return /^\s*@builtin$/i.test(query.trim()); } + static isBuiltInGroupExtensionsQuery(query: string): boolean { + return /^\s*@builtin:.+$/i.test(query.trim()); + } + static isInstalledExtensionsQuery(query: string): boolean { return /@installed/i.test(query); } @@ -868,39 +979,7 @@ export class ExtensionsListView extends ViewPane { } } -export class ServerExtensionsView extends ExtensionsListView { - - constructor( - server: IExtensionManagementServer, - onDidChangeTitle: Event, - options: ExtensionsListViewOptions, - @INotificationService notificationService: INotificationService, - @IKeybindingService keybindingService: IKeybindingService, - @IContextMenuService contextMenuService: IContextMenuService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IInstantiationService instantiationService: IInstantiationService, - @IExtensionService extensionService: IExtensionService, - @IExtensionRecommendationsService tipsService: IExtensionRecommendationsService, - @ITelemetryService telemetryService: ITelemetryService, - @IConfigurationService configurationService: IConfigurationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IExperimentService experimentService: IExperimentService, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, - @IWorkbenchExtensioManagementService extensionManagementService: IWorkbenchExtensioManagementService, - @IProductService productService: IProductService, - @IContextKeyService contextKeyService: IContextKeyService, - @IMenuService menuService: IMenuService, - @IOpenerService openerService: IOpenerService, - @IThemeService themeService: IThemeService, - @IPreferencesService preferencesService: IPreferencesService, - ) { - options.server = server; - super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, tipsService, - telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, extensionManagementService, productService, - contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); - this._register(onDidChangeTitle(title => this.updateTitle(title))); - } +export class ServerInstalledExtensionsView extends ExtensionsListView { async show(query: string): Promise> { query = query ? query : '@installed'; @@ -911,9 +990,9 @@ export class ServerExtensionsView extends ExtensionsListView { } getActions(): IAction[] { - if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) { + if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.options.server) { const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction)); - installLocalExtensionsInRemoteAction.class = 'codicon codicon-cloud-download'; + installLocalExtensionsInRemoteAction.class = ThemeIcon.asClassName(installLocalInRemoteIcon); return [installLocalExtensionsInRemoteAction]; } return []; @@ -936,28 +1015,6 @@ export class DisabledExtensionsView extends ExtensionsListView { } } -export class OutdatedExtensionsView extends ExtensionsListView { - - async show(query: string): Promise> { - query = query || '@outdated'; - return ExtensionsListView.isOutdatedExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); - } -} - -export class InstalledExtensionsView extends ExtensionsListView { - - async show(query: string): Promise> { - query = query || '@installed'; - return ExtensionsListView.isInstalledExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); - } -} - -export class SearchBuiltInExtensionsView extends ExtensionsListView { - async show(query: string): Promise> { - return ExtensionsListView.isSearchBuiltInExtensionsQuery(query) ? super.show(query) : this.showEmptyModel(); - } -} - export class BuiltInFeatureExtensionsView extends ExtensionsListView { async show(query: string): Promise> { return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:features'); @@ -1030,11 +1087,11 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { getActions(): IAction[] { if (!this.installAllAction) { - this.installAllAction = this._register(new Action('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), 'codicon codicon-cloud-download', false, () => this.installWorkspaceRecommendations())); + this.installAllAction = this._register(new Action('workbench.extensions.action.installWorkspaceRecommendedExtensions', localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), ThemeIcon.asClassName(installWorkspaceRecommendedIcon), false, () => this.installWorkspaceRecommendations())); } const configureWorkspaceFolderAction = this._register(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL)); - configureWorkspaceFolderAction.class = 'codicon codicon-pencil'; + configureWorkspaceFolderAction.class = ThemeIcon.asClassName(configureRecommendedIcon); return [this.installAllAction, configureWorkspaceFolderAction]; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 8718d0b9..55607b03 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/extensionsWidgets'; import { Disposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; -import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtension, IExtensionsWorkbenchService, IExtensionContainer, ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions'; import { append, $ } from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { localize } from 'vs/nls'; @@ -13,11 +13,14 @@ import { IExtensionManagementServerService } from 'vs/workbench/services/extensi import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground, ExtensionToolTipAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { EXTENSION_BADGE_REMOTE_BACKGROUND, EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme'; import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { installCountIcon, ratingIcon, remoteIcon, starEmptyIcon, starFullIcon, starHalfIcon, syncIgnoredIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension | null = null; @@ -83,7 +86,7 @@ export class InstallCountWidget extends ExtensionWidget { installLabel = installCount.toLocaleString(platform.locale); } - append(this.container, $('span.codicon.codicon-cloud-download')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(installCountIcon))); const count = append(this.container, $('span.count')); count.textContent = installLabel; } @@ -123,18 +126,18 @@ export class RatingsWidget extends ExtensionWidget { const rating = Math.round(this.extension.rating * 2) / 2; if (this.small) { - append(this.container, $('span.codicon.codicon-star-full')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); const count = append(this.container, $('span.count')); count.textContent = String(rating); } else { for (let i = 1; i <= 5; i++) { if (rating >= i) { - append(this.container, $('span.codicon.codicon-star-full')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); } else if (rating >= i - 0.5) { - append(this.container, $('span.codicon.codicon-star-half')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starHalfIcon))); } else { - append(this.container, $('span.codicon.codicon-star-empty')); + append(this.container, $('span' + ThemeIcon.asCSSSelector(starEmptyIcon))); } } } @@ -220,7 +223,7 @@ export class RecommendationWidget extends ExtensionWidget { if (extRecommendations[this.extension.identifier.id.toLowerCase()]) { this.element = append(this.parent, $('div.extension-bookmark')); const recommendation = append(this.element, $('.recommendation')); - append(recommendation, $('span.codicon.codicon-star')); + append(recommendation, $('span' + ThemeIcon.asCSSSelector(ratingIcon))); const applyBookmarkStyle = (theme: IColorTheme) => { const bgColor = theme.getColor(extensionButtonProminentBackground); const fgColor = theme.getColor(extensionButtonProminentForeground); @@ -286,7 +289,7 @@ class RemoteBadge extends Disposable { } private render(): void { - append(this.element, $('span.codicon.codicon-remote')); + append(this.element, $('span' + ThemeIcon.asCSSSelector(remoteIcon))); const applyBadgeStyle = () => { if (!this.element) { @@ -340,3 +343,28 @@ export class ExtensionPackCountWidget extends ExtensionWidget { countBadge.setCount(this.extension.extensionPack.length); } } + +export class SyncIgnoredWidget extends ExtensionWidget { + + private element: HTMLElement; + + constructor( + container: HTMLElement, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, + ) { + super(); + this.element = append(container, $('span.extension-sync-ignored' + ThemeIcon.asCSSSelector(syncIgnoredIcon))); + this.element.title = localize('syncingore.label', "This extension is ignored during sync."); + this.element.classList.add(...ThemeIcon.asClassNameArray(syncIgnoredIcon)); + this.element.classList.add('hide'); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('settingsSync.ignoredExtensions'))(() => this.render())); + this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(() => this.update())); + this.render(); + } + + render(): void { + this.element.classList.toggle('hide', !(this.extension && this.extension.state === ExtensionState.Installed && this.userDataAutoSyncEnablementService.isEnabled() && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension))); + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 5bbd4d88..3566ca41 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -8,7 +8,7 @@ import * as semver from 'vs/base/common/semver/semver'; import { Event, Emitter } from 'vs/base/common/event'; import { index, distinct } from 'vs/base/common/arrays'; import { ThrottledDelayer } from 'vs/base/common/async'; -import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { canceled, isPromiseCanceledError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -28,10 +28,10 @@ import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/commo import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -1073,7 +1073,23 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension for (const extension of extensions) { let dependents = this.getDependentsAfterDisablement(extension, allExtensions, this.local); if (dependents.length) { - return Promise.reject(new Error(this.getDependentsErrorMessage(extension, allExtensions, dependents))); + return new Promise((resolve, reject) => { + this.notificationService.prompt(Severity.Error, this.getDependentsErrorMessage(extension, allExtensions, dependents), [ + { + label: nls.localize('disable all', 'Disable All'), + run: async () => { + try { + await this.checkAndSetEnablement(dependents, [extension], enablementState); + resolve(); + } catch (error) { + reject(error); + } + } + } + ], { + onCancel: () => reject(canceled()) + }); + }); } } } @@ -1139,13 +1155,13 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private getErrorMessageForDisablingAnExtensionWithDependents(extension: IExtension, dependents: IExtension[]): string { if (dependents.length === 1) { - return nls.localize('singleDependentError', "Cannot disable extension '{0}'. Extension '{1}' depends on this.", extension.displayName, dependents[0].displayName); + return nls.localize('singleDependentError', "Cannot disable '{0}' extension alone. '{1}' extension depends on this. Do you want to disable all these extensions?", extension.displayName, dependents[0].displayName); } if (dependents.length === 2) { - return nls.localize('twoDependentsError', "Cannot disable extension '{0}'. Extensions '{1}' and '{2}' depend on this.", + return nls.localize('twoDependentsError', "Cannot disable '{0}' extension alone. '{1}' and '{2}' extensions depend on this. Do you want to disable all these extensions?", extension.displayName, dependents[0].displayName, dependents[1].displayName); } - return nls.localize('multipleDependentsError', "Cannot disable extension '{0}'. Extensions '{1}', '{2}' and others depend on this.", + return nls.localize('multipleDependentsError', "Cannot disable '{0}' extension alone. '{1}', '{2}' and other extensions depend on this. Do you want to disable all these extensions?", extension.displayName, dependents[0].displayName, dependents[1].displayName); } @@ -1253,7 +1269,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private set ignoredAutoUpdateExtensions(extensionIds: string[]) { this._ignoredAutoUpdateExtensions = distinct(extensionIds.map(id => id.toLowerCase())); - this.storageService.store('extensions.ignoredAutoUpdateExtension', JSON.stringify(this._ignoredAutoUpdateExtensions), StorageScope.GLOBAL); + this.storageService.store('extensions.ignoredAutoUpdateExtension', JSON.stringify(this._ignoredAutoUpdateExtensions), StorageScope.GLOBAL, StorageTarget.MACHINE); } private ignoreAutoUpdate(identifierWithVersion: ExtensionIdentifierWithVersion): void { diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 0de61475..cab0ed9a 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -11,7 +11,7 @@ import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage'; import { ImportantExtensionTip, IProductService } from 'vs/platform/product/common/productService'; import { forEach, IStringDictionary } from 'vs/base/common/collections'; import { ITextModel } from 'vs/editor/common/model'; @@ -26,6 +26,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { setImmediate } from 'vs/base/common/platform'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { distinct } from 'vs/base/common/arrays'; +import { DisposableStore } from 'vs/base/common/lifecycle'; type FileExtensionSuggestionClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -33,6 +35,7 @@ type FileExtensionSuggestionClassification = { }; const promptedRecommendationsStorageKey = 'fileBasedRecommendations/promptedRecommendations'; +const promptedFileExtensionsStorageKey = 'fileBasedRecommendations/promptedFileExtensions'; const recommendationsStorageKey = 'extensionsAssistant/recommendations'; const searchMarketplace = localize('searchMarketplace', "Search Marketplace"); const milliSecondsInADay = 1000 * 60 * 60 * 24; @@ -148,8 +151,16 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } private onModelAdded(model: ITextModel): void { + const uri = model.uri; + const supportedSchemes = [Schemas.untitled, Schemas.file, Schemas.vscodeRemote]; + if (!uri || !supportedSchemes.includes(uri.scheme)) { + return; + } + this.promptRecommendationsForModel(model); - this._register(model.onDidChangeLanguage(() => this.promptRecommendationsForModel(model))); + const disposables = new DisposableStore(); + disposables.add(model.onDidChangeLanguage(() => this.promptRecommendationsForModel(model))); + disposables.add(model.onWillDispose(() => disposables.dispose())); } /** @@ -158,13 +169,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { */ private promptRecommendationsForModel(model: ITextModel): void { const uri = model.uri; - const supportedSchemes = [Schemas.untitled, Schemas.file, Schemas.vscodeRemote]; - if (!uri || !supportedSchemes.includes(uri.scheme)) { - return; - } - const language = model.getLanguageIdentifier().language; - const fileExtension = extname(uri); + const fileExtension = extname(uri).toLowerCase(); if (this.processedLanguages.includes(language) && this.processedFileExtensions.includes(fileExtension)) { return; } @@ -269,7 +275,17 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private addToPromptedRecommendations(exeName: string, extensions: string[]) { const promptedRecommendations = this.getPromptedRecommendations(); promptedRecommendations[exeName] = extensions; - this.storageService.store(promptedRecommendationsStorageKey, JSON.stringify(promptedRecommendations), StorageScope.GLOBAL); + this.storageService.store(promptedRecommendationsStorageKey, JSON.stringify(promptedRecommendations), StorageScope.GLOBAL, StorageTarget.USER); + } + + private getPromptedFileExtensions(): string[] { + return JSON.parse(this.storageService.get(promptedFileExtensionsStorageKey, StorageScope.GLOBAL, '[]')); + } + + private addToPromptedFileExtensions(fileExtension: string) { + const promptedFileExtensions = this.getPromptedFileExtensions(); + promptedFileExtensions.push(fileExtension); + this.storageService.store(promptedFileExtensionsStorageKey, JSON.stringify(distinct(promptedFileExtensions)), StorageScope.GLOBAL, StorageTarget.USER); } private async promptRecommendedExtensionForFileExtension(fileExtension: string, installed: IExtension[]): Promise { @@ -278,6 +294,11 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return; } + const promptedFileExtensions = this.getPromptedFileExtensions(); + if (promptedFileExtensions.includes(fileExtension)) { + return; + } + const text = `ext:${fileExtension}`; const pager = await this.extensionsWorkbenchService.queryGallery({ text, pageSize: 100 }, CancellationToken.None); if (pager.firstPage.length === 0) { @@ -295,6 +316,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { [{ label: searchMarketplace, run: () => { + this.addToPromptedFileExtensions(fileExtension); this.telemetryService.publicLog2<{ userReaction: string, fileExtension: string }, FileExtensionSuggestionClassification>('fileExtensionSuggestion:popup', { userReaction: 'ok', fileExtension }); this.viewletService.openViewlet('workbench.view.extensions', true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) @@ -310,8 +332,8 @@ export class FileBasedRecommendations extends ExtensionRecommendations { this.storageService.store( 'extensionsAssistant/fileExtensionsSuggestionIgnore', JSON.stringify(fileExtensionSuggestionIgnoreList), - StorageScope.GLOBAL - ); + StorageScope.GLOBAL, + StorageTarget.USER); this.telemetryService.publicLog2<{ userReaction: string, fileExtension: string }, FileExtensionSuggestionClassification>('fileExtensionSuggestion:popup', { userReaction: 'neverShowAgain', fileExtension }); } }], @@ -356,7 +378,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private storeCachedRecommendations(): void { const storedRecommendations: IStringDictionary = {}; this.fileBasedRecommendations.forEach((value, key) => storedRecommendations[key] = value.recommendedTime); - this.storageService.store(recommendationsStorageKey, JSON.stringify(storedRecommendations), StorageScope.GLOBAL); + this.storageService.store(recommendationsStorageKey, JSON.stringify(storedRecommendations), StorageScope.GLOBAL, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extension.css b/src/vs/workbench/contrib/extensions/browser/media/extension.css index 1911ed1d..0502ce08 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extension.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extension.css @@ -139,6 +139,15 @@ color: currentColor; } +.extension-list-item > .details > .header-container > .header .sync-ignored { + display: flex; + margin-left: 6px; +} + +.extension-list-item > .details > .header-container > .header .sync-ignored > .codicon { + font-size: 100%; +} + .extension-list-item > .details > .description { padding-right: 11px; } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css index 340a9ad2..bb1107d0 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css @@ -13,7 +13,10 @@ .monaco-action-bar .action-item > .action-label.extension-action.label, .monaco-action-bar .action-dropdown-item > .action-label.extension-action.label { padding: 0 5px; - outline-offset: 2px; +} + +.monaco-action-bar .action-item .action-label.extension-action.label { + outline-offset: 1px; } .monaco-action-bar .action-item .action-label.extension-action.text, @@ -42,6 +45,7 @@ .monaco-action-bar .action-item.disabled .action-label.extension-action.uninstall:not(.uninstalling), .monaco-action-bar .action-item.disabled .action-label.extension-action.update, .monaco-action-bar .action-item.disabled .action-label.extension-action.theme, +.monaco-action-bar .action-item.disabled .action-label.extension-action.extension-sync, .monaco-action-bar .action-item.action-dropdown-item.disabled, .monaco-action-bar .action-item.action-dropdown-item .action-label.extension-action.hide, .monaco-action-bar .action-item.disabled .action-label.extension-action.reload, diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 2acadd17..7a08b2ed 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -76,7 +76,6 @@ .extension-editor > .header > .details > .title > .identifier { margin-left: 10px; font-size: 14px; - opacity: 0.6; background: rgba(173, 173, 173, 0.31); padding: 0px 4px; border-radius: 4px; @@ -165,6 +164,10 @@ max-width: 300px; } +.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .extension-action.icon { + padding-top: 1px; +} + .extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .extension-action.label { padding: 1px 6px; } @@ -212,8 +215,7 @@ .extension-editor > .header > .details > .subtext-container > .monaco-action-bar .action-label { margin-top: 4px; margin-left: 4px; - padding-top: 0; - padding-bottom: 2px; + padding-bottom: 1px; } .extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .actions-container { @@ -397,7 +399,7 @@ word-break: keep-all; } -.extension-editor > .body > .content table code:not(:empty) { +.extension-editor > .body > .content code:not(:empty) { font-family: var(--monaco-monospace-font); font-size: 90%; background-color: rgba(128, 128, 128, 0.17); diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css index 91cf3cef..737320d9 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.extension-sync-ignored.hide { + display: none; +} + .extension-ratings { display: inline-block; } @@ -11,7 +15,7 @@ font-size: 80%; } -.extension-ratings > .codicon[class*='codicon-star']:not(:first-child) { +.extension-ratings > .codicon[class*='codicon-extensions-rating']:not(:first-child) { margin-left: 3px; } @@ -24,17 +28,17 @@ } /* TODO @misolori make this a color token */ -.extension-ratings .codicon-star-full, -.extension-ratings .codicon-star-half { +.extension-ratings .codicon-extensions-star-full, +.extension-ratings .codicon-extensions-star-half { color: #FF8E00 !important; } .extension-install-count .codicon, -.extension-action.codicon-gear { +.extension-action.codicon-extensions-manage { color: inherit; } -.extension-ratings .codicon-star-empty { +.extension-ratings .codicon-extensions-star-empty { opacity: .4; } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css b/src/vs/workbench/contrib/extensions/browser/media/runtimeExtensionsEditor.css similarity index 91% rename from src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css rename to src/vs/workbench/contrib/extensions/browser/media/runtimeExtensionsEditor.css index 8403039d..050ddc80 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/media/runtimeExtensionsEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/runtimeExtensionsEditor.css @@ -57,11 +57,6 @@ color: currentColor; } -.monaco-workbench.vs .runtime-extensions-editor .extension > .icon-container > .icon, -.monaco-workbench.vs-dark .runtime-extensions-editor .extension > .icon-container > .icon { - opacity: 0.5; -} - .runtime-extensions-editor .extension > .desc > .header-container { display: flex; overflow: hidden; diff --git a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts index 3ce9d79e..3a8caf10 100644 --- a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts +++ b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts @@ -5,13 +5,14 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILabelService } from 'vs/platform/label/common/label'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { InstallLocalExtensionsInRemoteAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { InstallLocalExtensionsInRemoteAction, InstallRemoteExtensionsInLocalAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchContribution { @@ -38,6 +39,20 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC appendMenuItem(); this._register(labelService.onDidChangeFormatters(e => appendMenuItem())); this._register(toDisposable(() => disposable.dispose())); + + this._register(registerAction2(class InstallRemoteExtensionsInLocalAction2 extends Action2 { + constructor() { + super({ + id: 'workbench.extensions.actions.installLocalExtensionsInRemote', + title: { value: localize('install remote in local', "Install Remote Extensions Locally..."), original: 'Install Remote Extensions Locally...' }, + category: localize({ key: 'remote', comment: ['Remote as in remote machine'] }, "Remote"), + f1: true + }); + } + run(accessor: ServicesAccessor): Promise { + return accessor.get(IInstantiationService).createInstance(InstallRemoteExtensionsInLocalAction, 'workbench.extensions.actions.installLocalExtensionsInRemote').run(); + } + })); } } diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 231aa8f4..013e981b 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -7,12 +7,12 @@ import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService } from 'vs/platf import { distinct, flatten } from 'vs/base/common/arrays'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IExtensionsConfigContent, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; -import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { IExtensionsConfigContent, IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; export class WorkspaceRecommendations extends ExtensionRecommendations { @@ -51,23 +51,28 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { this.notificationService.warn(`The ${invalidRecommendations.length} extension(s) below, in workspace recommendations have issues:\n${message}`); } + this._recommendations = []; this._ignoredRecommendations = []; for (const extensionsConfig of extensionsConfigs) { - for (const unwantedRecommendation of extensionsConfig.unwantedRecommendations) { - if (invalidRecommendations.indexOf(unwantedRecommendation) === -1) { - this._ignoredRecommendations.push(unwantedRecommendation); + if (extensionsConfig.unwantedRecommendations) { + for (const unwantedRecommendation of extensionsConfig.unwantedRecommendations) { + if (invalidRecommendations.indexOf(unwantedRecommendation) === -1) { + this._ignoredRecommendations.push(unwantedRecommendation); + } } } - for (const extensionId of extensionsConfig.recommendations) { - if (invalidRecommendations.indexOf(extensionId) === -1) { - this._recommendations.push({ - extensionId, - reason: { - reasonId: ExtensionRecommendationReason.Workspace, - reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") - } - }); + if (extensionsConfig.recommendations) { + for (const extensionId of extensionsConfig.recommendations) { + if (invalidRecommendations.indexOf(extensionId) === -1) { + this._recommendations.push({ + extensionId, + reason: { + reasonId: ExtensionRecommendationReason.Workspace, + reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") + } + }); + } } } } @@ -114,12 +119,8 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { } private async onDidChangeExtensionsConfigs(): Promise { - const oldWorkspaceRecommended = this._recommendations; await this.fetch(); - // Suggest only if at least one of the newly added recommendations was not suggested before - if (this._recommendations.some(current => oldWorkspaceRecommended.every(old => current.extensionId !== old.extensionId))) { - this._onDidChangeRecommendations.fire(); - } + this._onDidChangeRecommendations.fire(); } } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index f4586ae3..3cfe2796 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -18,7 +18,8 @@ import { IViewPaneContainer } from 'vs/workbench/common/views'; export const VIEWLET_ID = 'workbench.view.extensions'; export interface IExtensionsViewPaneContainer extends IViewPaneContainer { - search(text: string, refresh?: boolean): void; + search(text: string): void; + refresh(): Promise; } export const enum ExtensionState { @@ -96,14 +97,12 @@ export interface IExtensionsWorkbenchService { export const ConfigurationKey = 'extensions'; export const AutoUpdateConfigurationKey = 'extensions.autoUpdate'; export const AutoCheckUpdatesConfigurationKey = 'extensions.autoCheckUpdates'; -export const ShowRecommendationsOnlyOnDemandKey = 'extensions.showRecommendationsOnlyOnDemand'; export const CloseExtensionDetailsOnViewChangeKey = 'extensions.closeExtensionDetailsOnViewChange'; export interface IExtensionsConfiguration { autoUpdate: boolean; autoCheckUpdates: boolean; ignoreRecommendations: boolean; - showRecommendationsOnlyOnDemand: boolean; closeExtensionDetailsOnViewChange: boolean; } @@ -131,10 +130,12 @@ export class ExtensionContainers extends Disposable { for (const container of this.containers) { if (extension && container.extension) { if (areSameExtensions(container.extension.identifier, extension.identifier)) { - if (!container.extension.server || !extension.server || container.extension.server === extension.server) { + if (container.extension.server && extension.server && container.extension.server !== extension.server) { + if (container.updateWhenCounterExtensionChanges) { + container.update(); + } + } else { container.extension = extension; - } else if (container.updateWhenCounterExtensionChanges) { - container.update(); } } } else { diff --git a/src/vs/workbench/contrib/extensions/common/extensionsInput.ts b/src/vs/workbench/contrib/extensions/common/extensionsInput.ts index b1ba1de3..eef81202 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsInput.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsInput.ts @@ -9,6 +9,7 @@ import { localize } from 'vs/nls'; import { EditorInput } from 'vs/workbench/common/editor'; import { IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { join } from 'vs/base/common/path'; export class ExtensionsInput extends EditorInput { @@ -17,7 +18,7 @@ export class ExtensionsInput extends EditorInput { get resource() { return URI.from({ scheme: Schemas.extension, - path: this.extension.identifier.id + path: join(this.extension.identifier.id, 'extension') }); } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts b/src/vs/workbench/contrib/extensions/common/runtimeExtensionsInput.ts similarity index 100% rename from src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput.ts rename to src/vs/workbench/contrib/extensions/common/runtimeExtensionsInput.ts diff --git a/src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts b/src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts new file mode 100644 index 00000000..bd3ea488 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Action } from 'vs/base/common/actions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { randomPort } from 'vs/base/node/ports'; + +export class DebugExtensionHostAction extends Action { + static readonly ID = 'workbench.extensions.action.debugExtensionHost'; + static readonly LABEL = nls.localize('debugExtensionHost', "Start Debugging Extension Host"); + static readonly CSS_CLASS = 'debug-extension-host'; + + constructor( + @IDebugService private readonly _debugService: IDebugService, + @INativeHostService private readonly _nativeHostService: INativeHostService, + @IDialogService private readonly _dialogService: IDialogService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IProductService private readonly productService: IProductService + ) { + super(DebugExtensionHostAction.ID, DebugExtensionHostAction.LABEL, DebugExtensionHostAction.CSS_CLASS); + } + + async run(): Promise { + + const inspectPort = await this._extensionService.getInspectPort(false); + if (!inspectPort) { + const res = await this._dialogService.confirm({ + type: 'info', + message: nls.localize('restart1', "Profile Extensions"), + detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", this.productService.nameLong), + primaryButton: nls.localize('restart3', "&&Restart"), + secondaryButton: nls.localize('cancel', "&&Cancel") + }); + if (res.confirmed) { + await this._nativeHostService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] }); + } + + return; + } + + return this._debugService.startDebugging(undefined, { + type: 'node', + name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), + request: 'attach', + port: inspectPort + }); + } +} diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts index 34cdcea7..ff347a59 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts @@ -16,7 +16,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { randomPort } from 'vs/base/node/ports'; import { IProductService } from 'vs/platform/product/common/productService'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -56,9 +56,9 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio this._profileSession = null; this._setState(ProfileSessionState.None); - CommandsRegistry.registerCommand('workbench.action.extensionHostProfilder.stop', () => { + CommandsRegistry.registerCommand('workbench.action.extensionHostProfiler.stop', () => { this.stopProfiling(); - this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true }); + this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true, pinned: true }); }); } @@ -86,7 +86,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio showProgress: true, ariaLabel: nls.localize('profilingExtensionHost', "Profiling Extension Host"), tooltip: nls.localize('selectAndStartDebug', "Click to stop profiling."), - command: 'workbench.action.extensionHostProfilder.stop' + command: 'workbench.action.extensionHostProfiler.stop' }; const timeStarted = Date.now(); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index f7fb0e3b..a472654f 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -7,17 +7,18 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; +import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { RuntimeExtensionsEditor, ShowRuntimeExtensionsAction, IExtensionHostProfileService, DebugExtensionHostAction, StartExtensionHostProfileAction, StopExtensionHostProfileAction, CONTEXT_PROFILE_SESSION_STATE, SaveExtensionHostProfileAction, CONTEXT_EXTENSION_HOST_PROFILE_RECORDED } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor'; +import { RuntimeExtensionsEditor, IExtensionHostProfileService, StartExtensionHostProfileAction, StopExtensionHostProfileAction, CONTEXT_PROFILE_SESSION_STATE, CONTEXT_EXTENSION_HOST_PROFILE_RECORDED, SaveExtensionHostProfileAction } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor'; +import { DebugExtensionHostAction } from 'vs/workbench/contrib/extensions/electron-browser/debugExtensionHostAction'; import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ActiveEditorContext } from 'vs/workbench/common/editor'; import { ExtensionHostProfileService } from 'vs/workbench/contrib/extensions/electron-browser/extensionProfileService'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; @@ -26,6 +27,7 @@ import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensio import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { ExtensionRecommendationNotificationServiceChannel } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; +import { Codicon } from 'vs/base/common/codicons'; // Singletons registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); @@ -34,16 +36,11 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually); // Running Extensions Editor - -const runtimeExtensionsEditorDescriptor = EditorDescriptor.create( - RuntimeExtensionsEditor, - RuntimeExtensionsEditor.ID, - localize('runtimeExtension', "Running Extensions") +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create(RuntimeExtensionsEditor, RuntimeExtensionsEditor.ID, localize('runtimeExtension', "Running Extensions")), + [new SyncDescriptor(RuntimeExtensionsInput)] ); -Registry.as(EditorExtensions.Editors) - .registerEditor(runtimeExtensionsEditorDescriptor, [new SyncDescriptor(RuntimeExtensionsInput)]); - class RuntimeExtensionsInputFactory implements IEditorInputFactory { canSerialize(editorInput: EditorInput): boolean { return true; @@ -62,8 +59,6 @@ Registry.as(EditorInputExtensions.EditorInputFactor // Global actions const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExtensionsAction), 'Show Running Extensions', CATEGORIES.Developer.value); - class ExtensionsContributions implements IWorkbenchContribution { constructor( @@ -72,10 +67,8 @@ class ExtensionsContributions implements IWorkbenchContribution { @ISharedProcessService sharedProcessService: ISharedProcessService, ) { sharedProcessService.registerChannel('IExtensionRecommendationNotificationService', new ExtensionRecommendationNotificationServiceChannel(extensionRecommendationNotificationService)); - if (environmentService.extensionsPath) { - const openExtensionsFolderActionDescriptor = SyncActionDescriptor.from(OpenExtensionsFolderAction); - actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); - } + const openExtensionsFolderActionDescriptor = SyncActionDescriptor.from(OpenExtensionsFolderAction); + actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); } } @@ -109,9 +102,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: DebugExtensionHostAction.ID, title: DebugExtensionHostAction.LABEL, - icon: { - id: 'codicon/debug-start' - } + icon: Codicon.debugStart }, group: 'navigation', when: ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID) @@ -121,9 +112,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: StartExtensionHostProfileAction.ID, title: StartExtensionHostProfileAction.LABEL, - icon: { - id: 'codicon/circle-filled' - } + icon: Codicon.circleFilled }, group: 'navigation', when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.notEqualsTo('running')) @@ -133,9 +122,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: StopExtensionHostProfileAction.ID, title: StopExtensionHostProfileAction.LABEL, - icon: { - id: 'codicon/debug-stop' - } + icon: Codicon.debugStop }, group: 'navigation', when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID), CONTEXT_PROFILE_SESSION_STATE.isEqualTo('running')) @@ -145,9 +132,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SaveExtensionHostProfileAction.ID, title: SaveExtensionHostProfileAction.LABEL, - icon: { - id: 'codicon/save-all' - }, + icon: Codicon.saveAll, precondition: CONTEXT_EXTENSION_HOST_PROFILE_RECORDED }, group: 'navigation', diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts index 226a0bc8..d8f182cf 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler.ts @@ -16,10 +16,10 @@ import { IExtensionHostProfileService } from 'vs/workbench/contrib/extensions/el import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; +import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions'; +import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; @@ -187,7 +187,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont ), [{ label: localize('show', 'Show Extensions'), - run: () => this._editorService.openEditor(RuntimeExtensionsInput.instance) + run: () => this._editorService.openEditor(RuntimeExtensionsInput.instance, { pinned: true }) }, action ], diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts similarity index 100% rename from src/vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions.ts rename to src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts diff --git a/src/vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction.ts b/src/vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction.ts new file mode 100644 index 00000000..faf606a7 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Action } from 'vs/base/common/actions'; +import { IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; + +export class ReportExtensionIssueAction extends Action { + + private static readonly _id = 'workbench.extensions.action.reportExtensionIssue'; + private static readonly _label = nls.localize('reportExtensionIssue', "Report Issue"); + + private _url: string | undefined; + + constructor( + private extension: { + description: IExtensionDescription; + marketplaceInfo: IExtension; + status?: IExtensionsStatus; + unresponsiveProfile?: IExtensionHostProfile + }, + @IOpenerService private readonly openerService: IOpenerService, + @IClipboardService private readonly clipboardService: IClipboardService, + @IProductService private readonly productService: IProductService, + @INativeHostService private readonly nativeHostService: INativeHostService + ) { + super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); + this.enabled = !!extension.description.repository && !!extension.description.repository.url; + } + + async run(): Promise { + if (!this._url) { + this._url = await this._generateNewIssueUrl(this.extension); + } + this.openerService.open(URI.parse(this._url)); + } + + private async _generateNewIssueUrl(extension: { + description: IExtensionDescription; + marketplaceInfo: IExtension; + status?: IExtensionsStatus; + unresponsiveProfile?: IExtensionHostProfile + }): Promise { + let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined; + if (!!baseUrl) { + baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`; + } else { + baseUrl = this.productService.reportIssueUrl!; + } + + let reason = 'Bug'; + let title = 'Extension issue'; + let message = ':warning: We have written the needed data into your clipboard. Please paste! :warning:'; + this.clipboardService.writeText('```json \n' + JSON.stringify(extension.status, null, '\t') + '\n```'); + + const os = await this.nativeHostService.getOSProperties(); + const osVersion = `${os.type} ${os.arch} ${os.release}`; + const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; + const body = encodeURIComponent( + `- Issue Type: \`${reason}\` +- Extension Name: \`${extension.description.name}\` +- Extension Version: \`${extension.description.version}\` +- OS Version: \`${osVersion}\` +- VSCode version: \`${this.productService.version}\`\n\n${message}` + ); + + return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`; + } +} diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 2a81e43f..8aba9dd1 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -3,50 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/runtimeExtensionsEditor'; import * as nls from 'vs/nls'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { Action, IAction, Separator } from 'vs/base/common/actions'; -import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; -import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; -import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { append, $, reset, Dimension, clearNode } from 'vs/base/browser/dom'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionService, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { memoize } from 'vs/base/common/decorators'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; -import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { randomPort } from 'vs/base/node/ports'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ILabelService } from 'vs/platform/label/common/label'; -import { renderCodicons } from 'vs/base/browser/codicons'; -import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { Schemas } from 'vs/base/common/network'; -import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; -import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { domEvent } from 'vs/base/browser/event'; -import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IFileService } from 'vs/platform/files/common/files'; +import { ReportExtensionIssueAction } from 'vs/workbench/contrib/extensions/electron-browser/reportExtensionIssueAction'; +import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor'; import { VSBuffer } from 'vs/base/common/buffer'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -75,64 +53,31 @@ export interface IExtensionHostProfileService { setUnresponsiveProfile(extensionId: ExtensionIdentifier, profile: IExtensionHostProfile): void; } -interface IExtensionProfileInformation { - /** - * segment when the extension was running. - * 2*i = segment start time - * 2*i+1 = segment end time - */ - segments: number[]; - /** - * total time when the extension was running. - * (sum of all segment lengths). - */ - totalTime: number; -} +export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor { -interface IRuntimeExtension { - originalIndex: number; - description: IExtensionDescription; - marketplaceInfo: IExtension; - status: IExtensionsStatus; - profileInfo?: IExtensionProfileInformation; - unresponsiveProfile?: IExtensionHostProfile; -} - -export class RuntimeExtensionsEditor extends EditorPane { - - public static readonly ID: string = 'workbench.editor.runtimeExtensions'; - - private _list: WorkbenchList | null; private _profileInfo: IExtensionHostProfile | null; - - private _elements: IRuntimeExtension[] | null; - private _extensionsDescriptions: IExtensionDescription[]; - private _updateSoon: RunOnceScheduler; - private _profileSessionState: IContextKey; private _extensionsHostRecorded: IContextKey; + private _profileSessionState: IContextKey; constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IContextKeyService contextKeyService: IContextKeyService, - @IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionService private readonly _extensionService: IExtensionService, - @INotificationService private readonly _notificationService: INotificationService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionService extensionService: IExtensionService, + @INotificationService notificationService: INotificationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IInstantiationService instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, - @ILabelService private readonly _labelService: ILabelService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @IOpenerService private readonly _openerService: IOpenerService, - @IClipboardService private readonly _clipboardService: IClipboardService, - @IProductService private readonly _productService: IProductService, - @INativeHostService private readonly _nativeHostService: INativeHostService + @ILabelService labelService: ILabelService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService, ) { - super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); - - this._list = null; + super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService); this._profileInfo = this._extensionHostProfileService.lastProfile; + this._extensionsHostRecorded = CONTEXT_EXTENSION_HOST_PROFILE_RECORDED.bindTo(contextKeyService); + this._profileSessionState = CONTEXT_PROFILE_SESSION_STATE.bindTo(contextKeyService); + this._register(this._extensionHostProfileService.onDidChangeLastProfile(() => { this._profileInfo = this._extensionHostProfileService.lastProfile; this._extensionsHostRecorded.set(!!this._profileInfo); @@ -142,486 +87,39 @@ export class RuntimeExtensionsEditor extends EditorPane { const state = this._extensionHostProfileService.state; this._profileSessionState.set(ProfileSessionState[state].toLowerCase()); })); - - this._elements = null; - - this._extensionsDescriptions = []; - this._updateExtensions(); - - this._profileSessionState = CONTEXT_PROFILE_SESSION_STATE.bindTo(contextKeyService); - this._extensionsHostRecorded = CONTEXT_EXTENSION_HOST_PROFILE_RECORDED.bindTo(contextKeyService); - - this._updateSoon = this._register(new RunOnceScheduler(() => this._updateExtensions(), 200)); - - this._extensionService.getExtensions().then((extensions) => { - // We only deal with extensions with source code! - this._extensionsDescriptions = extensions.filter((extension) => { - return Boolean(extension.main) || Boolean(extension.browser); - }); - this._updateExtensions(); - }); - this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateSoon.schedule())); } - private async _updateExtensions(): Promise { - this._elements = await this._resolveExtensions(); - if (this._list) { - this._list.splice(0, this._list.length, this._elements); - } + protected _getProfileInfo(): IExtensionHostProfile | null { + return this._profileInfo; } - private async _resolveExtensions(): Promise { - let marketplaceMap: { [id: string]: IExtension; } = Object.create(null); - const marketPlaceExtensions = await this._extensionsWorkbenchService.queryLocal(); - for (let extension of marketPlaceExtensions) { - marketplaceMap[ExtensionIdentifier.toKey(extension.identifier.id)] = extension; - } - - let statusMap = this._extensionService.getExtensionsStatus(); - - // group profile segments by extension - let segments: { [id: string]: number[]; } = Object.create(null); - - if (this._profileInfo) { - let currentStartTime = this._profileInfo.startTime; - for (let i = 0, len = this._profileInfo.deltas.length; i < len; i++) { - const id = this._profileInfo.ids[i]; - const delta = this._profileInfo.deltas[i]; - - let extensionSegments = segments[ExtensionIdentifier.toKey(id)]; - if (!extensionSegments) { - extensionSegments = []; - segments[ExtensionIdentifier.toKey(id)] = extensionSegments; - } - - extensionSegments.push(currentStartTime); - currentStartTime = currentStartTime + delta; - extensionSegments.push(currentStartTime); - } - } - - let result: IRuntimeExtension[] = []; - for (let i = 0, len = this._extensionsDescriptions.length; i < len; i++) { - const extensionDescription = this._extensionsDescriptions[i]; - - let profileInfo: IExtensionProfileInformation | null = null; - if (this._profileInfo) { - let extensionSegments = segments[ExtensionIdentifier.toKey(extensionDescription.identifier)] || []; - let extensionTotalTime = 0; - for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) { - const startTime = extensionSegments[2 * j]; - const endTime = extensionSegments[2 * j + 1]; - extensionTotalTime += (endTime - startTime); - } - profileInfo = { - segments: extensionSegments, - totalTime: extensionTotalTime - }; - } - - result[i] = { - originalIndex: i, - description: extensionDescription, - marketplaceInfo: marketplaceMap[ExtensionIdentifier.toKey(extensionDescription.identifier)], - status: statusMap[extensionDescription.identifier.value], - profileInfo: profileInfo || undefined, - unresponsiveProfile: this._extensionHostProfileService.getUnresponsiveProfile(extensionDescription.identifier) - }; - } - - result = result.filter(element => element.status.activationTimes); - - // bubble up extensions that have caused slowness - - const isUnresponsive = (extension: IRuntimeExtension): boolean => - extension.unresponsiveProfile === this._profileInfo; - - const profileTime = (extension: IRuntimeExtension): number => - extension.profileInfo?.totalTime ?? 0; - - const activationTime = (extension: IRuntimeExtension): number => - (extension.status.activationTimes?.codeLoadingTime ?? 0) + - (extension.status.activationTimes?.activateCallTime ?? 0); - - result = result.sort((a, b) => { - if (isUnresponsive(a) || isUnresponsive(b)) { - return +isUnresponsive(b) - +isUnresponsive(a); - } else if (profileTime(a) || profileTime(b)) { - return profileTime(b) - profileTime(a); - } else if (activationTime(a) || activationTime(b)) { - return activationTime(b) - activationTime(a); - } - return a.originalIndex - b.originalIndex; - }); - - return result; + protected _getUnresponsiveProfile(extensionId: ExtensionIdentifier): IExtensionHostProfile | undefined { + return this._extensionHostProfileService.getUnresponsiveProfile(extensionId); } - protected createEditor(parent: HTMLElement): void { - parent.classList.add('runtime-extensions-editor'); - - const TEMPLATE_ID = 'runtimeExtensionElementTemplate'; - - const delegate = new class implements IListVirtualDelegate{ - getHeight(element: IRuntimeExtension): number { - return 62; - } - getTemplateId(element: IRuntimeExtension): string { - return TEMPLATE_ID; - } - }; - - interface IRuntimeExtensionTemplateData { - root: HTMLElement; - element: HTMLElement; - icon: HTMLImageElement; - name: HTMLElement; - version: HTMLElement; - msgContainer: HTMLElement; - actionbar: ActionBar; - activationTime: HTMLElement; - profileTime: HTMLElement; - disposables: IDisposable[]; - elementDisposables: IDisposable[]; + protected _createSlowExtensionAction(element: IRuntimeExtension): Action | null { + if (element.unresponsiveProfile) { + return this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile); } - - const renderer: IListRenderer = { - templateId: TEMPLATE_ID, - renderTemplate: (root: HTMLElement): IRuntimeExtensionTemplateData => { - const element = append(root, $('.extension')); - const iconContainer = append(element, $('.icon-container')); - const icon = append(iconContainer, $('img.icon')); - - const desc = append(element, $('div.desc')); - const headerContainer = append(desc, $('.header-container')); - const header = append(headerContainer, $('.header')); - const name = append(header, $('div.name')); - const version = append(header, $('span.version')); - - const msgContainer = append(desc, $('div.msg')); - - const actionbar = new ActionBar(desc, { animated: false }); - actionbar.onDidRun(({ error }) => error && this._notificationService.error(error)); - - - const timeContainer = append(element, $('.time')); - const activationTime = append(timeContainer, $('div.activation-time')); - const profileTime = append(timeContainer, $('div.profile-time')); - - const disposables = [actionbar]; - - return { - root, - element, - icon, - name, - version, - actionbar, - activationTime, - profileTime, - msgContainer, - disposables, - elementDisposables: [], - }; - }, - - renderElement: (element: IRuntimeExtension, index: number, data: IRuntimeExtensionTemplateData): void => { - - data.elementDisposables = dispose(data.elementDisposables); - - data.root.classList.toggle('odd', index % 2 === 1); - - const onError = Event.once(domEvent(data.icon, 'error')); - onError(() => data.icon.src = element.marketplaceInfo.iconUrlFallback, null, data.elementDisposables); - data.icon.src = element.marketplaceInfo.iconUrl; - - if (!data.icon.complete) { - data.icon.style.visibility = 'hidden'; - data.icon.onload = () => data.icon.style.visibility = 'inherit'; - } else { - data.icon.style.visibility = 'inherit'; - } - data.name.textContent = element.marketplaceInfo.displayName; - data.version.textContent = element.description.version; - - const activationTimes = element.status.activationTimes!; - let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime; - data.activationTime.textContent = activationTimes.activationReason.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`; - - data.actionbar.clear(); - if (element.unresponsiveProfile) { - data.actionbar.push(this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile), { icon: true, label: true }); - } - if (isNonEmptyArray(element.status.runtimeErrors)) { - data.actionbar.push(new ReportExtensionIssueAction(element, this._openerService, this._clipboardService, this._productService, this._nativeHostService), { icon: true, label: true }); - } - - let title: string; - const activationId = activationTimes.activationReason.extensionId.value; - const activationEvent = activationTimes.activationReason.activationEvent; - if (activationEvent === '*') { - title = nls.localize('starActivation', "Activated by {0} on start-up", activationId); - } else if (/^workspaceContains:/.test(activationEvent)) { - let fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); - if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { - title = nls.localize({ - key: 'workspaceContainsGlobActivation', - comment: [ - '{0} will be a glob pattern' - ] - }, "Activated by {1} because a file matching {1} exists in your workspace", fileNameOrGlob, activationId); - } else { - title = nls.localize({ - key: 'workspaceContainsFileActivation', - comment: [ - '{0} will be a file name' - ] - }, "Activated by {1} because file {0} exists in your workspace", fileNameOrGlob, activationId); - } - } else if (/^workspaceContainsTimeout:/.test(activationEvent)) { - const glob = activationEvent.substr('workspaceContainsTimeout:'.length); - title = nls.localize({ - key: 'workspaceContainsTimeout', - comment: [ - '{0} will be a glob pattern' - ] - }, "Activated by {1} because searching for {0} took too long", glob, activationId); - } else if (activationEvent === 'onStartupFinished') { - title = nls.localize({ - key: 'startupFinishedActivation', - comment: [ - 'This refers to an extension. {0} will be an activation event.' - ] - }, "Activated by {0} after start-up finished", activationId); - } else if (/^onLanguage:/.test(activationEvent)) { - let language = activationEvent.substr('onLanguage:'.length); - title = nls.localize('languageActivation', "Activated by {1} because you opened a {0} file", language, activationId); - } else { - title = nls.localize({ - key: 'workspaceGenericActivation', - comment: [ - 'The {0} placeholder will be an activation event, like e.g. \'language:typescript\', \'debug\', etc.' - ] - }, "Activated by {1} on {0}", activationEvent, activationId); - } - data.activationTime.title = title; - - clearNode(data.msgContainer); - - if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.identifier)) { - const el = $('span', undefined, ...renderCodicons(` $(alert) Unresponsive`)); - el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); - data.msgContainer.appendChild(el); - } - - if (isNonEmptyArray(element.status.runtimeErrors)) { - const el = $('span', undefined, ...renderCodicons(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`)); - data.msgContainer.appendChild(el); - } - - if (element.status.messages && element.status.messages.length > 0) { - const el = $('span', undefined, ...renderCodicons(`$(alert) ${element.status.messages[0].message}`)); - data.msgContainer.appendChild(el); - } - - if (element.description.extensionLocation.scheme !== Schemas.file) { - const el = $('span', undefined, ...renderCodicons(`$(remote) ${element.description.extensionLocation.authority}`)); - data.msgContainer.appendChild(el); - - const hostLabel = this._labelService.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority); - if (hostLabel) { - reset(el, ...renderCodicons(`$(remote) ${hostLabel}`)); - } - } - - if (this._profileInfo && element.profileInfo) { - data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`; - } else { - data.profileTime.textContent = ''; - } - - }, - - disposeTemplate: (data: IRuntimeExtensionTemplateData): void => { - data.disposables = dispose(data.disposables); - } - }; - - this._list = >this._instantiationService.createInstance(WorkbenchList, - 'RuntimeExtensions', - parent, delegate, [renderer], { - multipleSelectionSupport: false, - setRowLineHeight: false, - horizontalScrolling: false, - overrideStyles: { - listBackground: editorBackground - }, - accessibilityProvider: new RuntimeExtensionsEditorAccessibilityProvider() - }); - - this._list.splice(0, this._list.length, this._elements || undefined); - - this._list.onContextMenu((e) => { - if (!e.element) { - return; - } - - const actions: IAction[] = []; - - actions.push(new ReportExtensionIssueAction(e.element, this._openerService, this._clipboardService, this._productService, this._nativeHostService)); - actions.push(new Separator()); - - actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledWorkspace))); - actions.push(new Action('runtimeExtensionsEditor.action.disable', nls.localize('disable', "Disable"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo, EnablementState.DisabledGlobally))); - actions.push(new Separator()); - - const state = this._extensionHostProfileService.state; - if (state === ProfileSessionState.Running) { - actions.push(this._instantiationService.createInstance(StopExtensionHostProfileAction, StopExtensionHostProfileAction.ID, StopExtensionHostProfileAction.LABEL)); - } else { - actions.push(this._instantiationService.createInstance(StartExtensionHostProfileAction, StartExtensionHostProfileAction.ID, StartExtensionHostProfileAction.LABEL)); - } - actions.push(this.saveExtensionHostProfileAction); - - this._contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => actions - }); - }); + return null; } - @memoize - private get saveExtensionHostProfileAction(): IAction { + protected _createReportExtensionIssueAction(element: IRuntimeExtension): Action | null { + return this._instantiationService.createInstance(ReportExtensionIssueAction, element); + } + + protected _createSaveExtensionHostProfileAction(): Action | null { return this._instantiationService.createInstance(SaveExtensionHostProfileAction, SaveExtensionHostProfileAction.ID, SaveExtensionHostProfileAction.LABEL); } - public layout(dimension: Dimension): void { - if (this._list) { - this._list.layout(dimension.height); - } - } -} - -export class ShowRuntimeExtensionsAction extends Action { - static readonly ID = 'workbench.action.showRuntimeExtensions'; - static readonly LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions"); - - constructor( - id: string, label: string, - @IEditorService private readonly _editorService: IEditorService - ) { - super(id, label); - } - - public async run(e?: any): Promise { - await this._editorService.openEditor(RuntimeExtensionsInput.instance, { revealIfOpened: true }); - } -} - -export class ReportExtensionIssueAction extends Action { - - private static readonly _id = 'workbench.extensions.action.reportExtensionIssue'; - private static readonly _label = nls.localize('reportExtensionIssue', "Report Issue"); - - private _url: string | undefined; - - constructor( - private extension: { - description: IExtensionDescription; - marketplaceInfo: IExtension; - status?: IExtensionsStatus; - unresponsiveProfile?: IExtensionHostProfile - }, - @IOpenerService private readonly openerService: IOpenerService, - @IClipboardService private readonly clipboardService: IClipboardService, - @IProductService private readonly productService: IProductService, - @INativeHostService private readonly nativeHostService: INativeHostService - ) { - super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); - this.enabled = extension.marketplaceInfo - && extension.marketplaceInfo.type === ExtensionType.User - && !!extension.description.repository && !!extension.description.repository.url; - } - - async run(): Promise { - if (!this._url) { - this._url = await this._generateNewIssueUrl(this.extension); - } - this.openerService.open(URI.parse(this._url)); - } - - private async _generateNewIssueUrl(extension: { - description: IExtensionDescription; - marketplaceInfo: IExtension; - status?: IExtensionsStatus; - unresponsiveProfile?: IExtensionHostProfile - }): Promise { - let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined; - if (!!baseUrl) { - baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`; - } else { - baseUrl = this.productService.reportIssueUrl!; - } - - let reason = 'Bug'; - let title = 'Extension issue'; - let message = ':warning: We have written the needed data into your clipboard. Please paste! :warning:'; - this.clipboardService.writeText('```json \n' + JSON.stringify(extension.status, null, '\t') + '\n```'); - - const os = await this.nativeHostService.getOSProperties(); - const osVersion = `${os.type} ${os.arch} ${os.release}`; - const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; - const body = encodeURIComponent( - `- Issue Type: \`${reason}\` -- Extension Name: \`${extension.description.name}\` -- Extension Version: \`${extension.description.version}\` -- OS Version: \`${osVersion}\` -- VSCode version: \`${this.productService.version}\`\n\n${message}` + protected _createProfileAction(): Action | null { + const state = this._extensionHostProfileService.state; + const profileAction = ( + state === ProfileSessionState.Running + ? this._instantiationService.createInstance(StopExtensionHostProfileAction, StopExtensionHostProfileAction.ID, StopExtensionHostProfileAction.LABEL) + : this._instantiationService.createInstance(StartExtensionHostProfileAction, StartExtensionHostProfileAction.ID, StartExtensionHostProfileAction.LABEL) ); - - return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`; - } -} - -export class DebugExtensionHostAction extends Action { - static readonly ID = 'workbench.extensions.action.debugExtensionHost'; - static readonly LABEL = nls.localize('debugExtensionHost', "Start Debugging Extension Host"); - static readonly CSS_CLASS = 'debug-extension-host'; - - constructor( - @IDebugService private readonly _debugService: IDebugService, - @INativeHostService private readonly _nativeHostService: INativeHostService, - @IDialogService private readonly _dialogService: IDialogService, - @IExtensionService private readonly _extensionService: IExtensionService, - @IProductService private readonly productService: IProductService - ) { - super(DebugExtensionHostAction.ID, DebugExtensionHostAction.LABEL, DebugExtensionHostAction.CSS_CLASS); - } - - async run(): Promise { - - const inspectPort = await this._extensionService.getInspectPort(false); - if (!inspectPort) { - const res = await this._dialogService.confirm({ - type: 'info', - message: nls.localize('restart1', "Profile Extensions"), - detail: nls.localize('restart2', "In order to profile extensions a restart is required. Do you want to restart '{0}' now?", this.productService.nameLong), - primaryButton: nls.localize('restart3', "&&Restart"), - secondaryButton: nls.localize('cancel', "&&Cancel") - }); - if (res.confirmed) { - await this._nativeHostService.relaunch({ addArgs: [`--inspect-extensions=${randomPort()}`] }); - } - - return; - } - - return this._debugService.startDebugging(undefined, { - type: 'node', - name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), - request: 'attach', - port: inspectPort - }); + return profileAction; } } @@ -707,7 +205,6 @@ export class SaveExtensionHostProfileAction extends Action { // absolute filenames because we don't want to reveal anything // about users. We also append the `.txt` suffix to make it // easier to attach these files to GH issues - let tmp = profiler.rewriteAbsolutePaths({ profile: dataToWrite as any }, 'piiRemoved'); dataToWrite = tmp.profile; @@ -717,13 +214,3 @@ export class SaveExtensionHostProfileAction extends Action { return this._fileService.writeFile(URI.file(savePath), VSBuffer.fromString(JSON.stringify(profileInfo ? profileInfo.data : {}, null, '\t'))); } } - -class RuntimeExtensionsEditorAccessibilityProvider implements IListAccessibilityProvider { - getWidgetAriaLabel(): string { - return nls.localize('runtimeExtensions', "Runtime Extensions"); - } - - getAriaLabel(element: IRuntimeExtension): string | null { - return element.description.name; - } -} diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts index 369d6a1b..99cd8d05 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionsActions.ts @@ -27,20 +27,18 @@ export class OpenExtensionsFolderAction extends Action { } async run(): Promise { - if (this.environmentService.extensionsPath) { - const extensionsHome = URI.file(this.environmentService.extensionsPath); - const file = await this.fileService.resolve(extensionsHome); + const extensionsHome = URI.file(this.environmentService.extensionsPath); + const file = await this.fileService.resolve(extensionsHome); - let itemToShow: URI; - if (file.children && file.children.length > 0) { - itemToShow = file.children[0].resource; - } else { - itemToShow = extensionsHome; - } + let itemToShow: URI; + if (file.children && file.children.length > 0) { + itemToShow = file.children[0].resource; + } else { + itemToShow = extensionsHome; + } - if (itemToShow.scheme === Schemas.file) { - return this.nativeHostService.showItemInFolder(itemToShow.fsPath); - } + if (itemToShow.scheme === Schemas.file) { + return this.nativeHostService.showItemInFolder(itemToShow.fsPath); } } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 3c38a676..ed8a92a9 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -42,7 +42,7 @@ import { INotificationService, Severity, IPromptChoice, IPromptOptions } from 'v import { NativeURLService } from 'vs/platform/url/common/urlService'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -55,7 +55,6 @@ import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-s import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; import { NoOpWorkspaceTagsService } from 'vs/workbench/contrib/tags/browser/workspaceTagsService'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkpsaceExtensionsConfigService, WorkspaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; @@ -222,7 +221,6 @@ suite('ExtensionRecommendationsService Test', () => { instantiationService.stub(IWorkspaceTagsService, new NoOpWorkspaceTagsService()); instantiationService.stub(IStorageService, new TestStorageService()); instantiationService.stub(ILogService, new NullLogService()); - instantiationService.stub(IStorageKeysSyncRegistryService, new StorageKeysSyncRegistryService()); instantiationService.stub(IProductService, >{ extensionTips: { 'ms-dotnettools.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', @@ -273,7 +271,7 @@ suite('ExtensionRecommendationsService Test', () => { instantiationService.stub(INotificationService, new TestNotificationService2()); - testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: false, showRecommendationsOnlyOnDemand: false }); + testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: false }); instantiationService.stub(IModelService, { getModels(): any { return []; }, onModelAdded: onModelAddedEvent.event @@ -306,14 +304,14 @@ suite('ExtensionRecommendationsService Test', () => { }, null, '\t')); const myWorkspace = testWorkspace(URI.from({ scheme: 'file', path: folderDir })); + const fileService = new FileService(new NullLogService()); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); + instantiationService.stub(IFileService, fileService); workspaceService = new TestContextService(myWorkspace); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkpsaceExtensionsConfigService, instantiationService.createInstance(WorkspaceExtensionsConfigService)); instantiationService.stub(IExtensionIgnoredRecommendationsService, instantiationService.createInstance(ExtensionIgnoredRecommendationsService)); instantiationService.stub(IExtensionRecommendationNotificationService, instantiationService.createInstance(ExtensionRecommendationNotificationService)); - const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - instantiationService.stub(IFileService, fileService); } function testNoPromptForValidRecommendations(recommendations: string[]) { @@ -383,6 +381,16 @@ suite('ExtensionRecommendationsService Test', () => { return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set', () => { + testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: true }); + return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { + testObject = instantiationService.createInstance(ExtensionRecommendationsService); + return testObject.activationPromise.then(() => { + assert.ok(!prompted); + }); + }); + }); + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if showRecommendationsOnlyOnDemand is set', () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { showRecommendationsOnlyOnDemand: true }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { @@ -394,14 +402,14 @@ suite('ExtensionRecommendationsService Test', () => { }); test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set for current workspace', () => { - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); + instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); test('ExtensionRecommendationsService: No Recommendations of globally ignored recommendations', () => { - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]', StorageScope.GLOBAL); - instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', '["ms-dotnettools.csharp", "mockpublisher2.mockextension2"]', StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]', StorageScope.GLOBAL, StorageTarget.MACHINE); + instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', '["ms-dotnettools.csharp", "mockpublisher2.mockextension2"]', StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -418,8 +426,8 @@ suite('ExtensionRecommendationsService Test', () => { test('ExtensionRecommendationsService: No Recommendations of workspace ignored recommendations', () => { const ignoredRecommendations = ['ms-dotnettools.csharp', 'mockpublisher2.mockextension2']; // ignore a stored recommendation and a workspace recommendation. const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, ignoredRecommendations).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -433,15 +441,15 @@ suite('ExtensionRecommendationsService Test', () => { }); }); - test('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', async () => { + test.skip('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', async () => { const storageService = instantiationService.get(IStorageService); const workspaceIgnoredRecommendations = ['ms-dotnettools.csharp']; // ignore a stored recommendation and a workspace recommendation. const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. - storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); - storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations); testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -458,9 +466,9 @@ suite('ExtensionRecommendationsService Test', () => { const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. - storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); - storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions); const extensionIgnoredRecommendationsService = instantiationService.get(IExtensionIgnoredRecommendationsService); @@ -492,8 +500,8 @@ suite('ExtensionRecommendationsService Test', () => { const changeHandlerTarget = sinon.spy(); const ignoredExtensionId = 'Some.Extension'; - storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - storageService.store('extensionsAssistant/ignored_recommendations', '["ms-vscode.vscode"]', StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + storageService.store('extensionsAssistant/ignored_recommendations', '["ms-vscode.vscode"]', StorageScope.GLOBAL, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', []); testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -508,7 +516,7 @@ suite('ExtensionRecommendationsService Test', () => { test('ExtensionRecommendationsService: Get file based recommendations from storage (old format)', () => { const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); @@ -527,7 +535,7 @@ suite('ExtensionRecommendationsService Test', () => { const now = Date.now(); const tenDaysOld = 10 * milliSecondsInADay; const storedRecommendations = `{"ms-dotnettools.csharp": ${now}, "ms-python.python": ${now}, "ms-vscode.vscode-typescript-tslint-plugin": ${now}, "lukehoban.Go": ${tenDaysOld}}`; - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionRecommendationsService); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index c928821f..40cf65cc 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -38,12 +38,10 @@ import { ExtensionIdentifier, IExtensionContributions, ExtensionType, IExtension import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ILabelService, IFormatterChangeEvent } from 'vs/platform/label/common/label'; -import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { ProgressService } from 'vs/workbench/services/progress/browser/progressService'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; @@ -78,7 +76,6 @@ async function setupTest() { instantiationService.stub(IWorkspaceContextService, new TestContextService()); instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IProgressService, ProgressService); - instantiationService.stub(IStorageKeysSyncRegistryService, new StorageKeysSyncRegistryService()); instantiationService.stub(IProductService, {}); instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); @@ -101,14 +98,18 @@ async function setupTest() { instantiationService.stub(IRemoteAgentService, RemoteAgentService); - instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { - #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; - constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService), instantiationService); + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + instantiationService.stub(IExtensionManagementServerService, >{ + get localExtensionManagementServer(): IExtensionManagementServer { + return localExtensionManagementServer; + }, + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null { + if (extension.location.scheme === Schemas.file) { + return localExtensionManagementServer; + } + throw new Error(`Invalid Extension ${extension.location}`); } - get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } - set localExtensionManagementServer(server: IExtensionManagementServer) { } - }()); + }); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ILabelService, { onDidChangeFormatters: new Emitter().event }); @@ -418,7 +419,7 @@ suite('ExtensionsActions', () => { .then(extensions => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -433,7 +434,7 @@ suite('ExtensionsActions', () => { .then(page => { testObject.extension = page.firstPage[0]; assert.ok(!testObject.enabled); - assert.equal('extension-action icon manage codicon-gear hide', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage hide', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -450,7 +451,7 @@ suite('ExtensionsActions', () => { installEvent.fire({ identifier: gallery.identifier, gallery }); assert.ok(!testObject.enabled); - assert.equal('extension-action icon manage codicon-gear hide', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage hide', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -468,7 +469,7 @@ suite('ExtensionsActions', () => { didInstallEvent.fire({ identifier: gallery.identifier, gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }); assert.ok(testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -483,7 +484,7 @@ suite('ExtensionsActions', () => { .then(extensions => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('', testObject.tooltip); }); }); @@ -500,7 +501,7 @@ suite('ExtensionsActions', () => { uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); - assert.equal('extension-action icon manage codicon-gear', testObject.class); + assert.equal('extension-action icon manage codicon codicon-extensions-manage', testObject.class); assert.equal('Uninstalling', testObject.tooltip); }); }); @@ -817,25 +818,19 @@ suite('ExtensionsActions', () => { }); }); - test('Test DisableDropDownAction when there is no extension', () => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, []); - - assert.ok(!testObject.enabled); - }); - - test('Test DisableDropDownAction when extension is installed and enabled', () => { + test('Test DisableGloballyAction when extension is installed and enabled', () => { const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = extensions[0]; assert.ok(testObject.enabled); }); }); - test('Test DisableDropDownAction when extension is installed and disabled globally', () => { + test('Test DisableGloballyAction when extension is installed and disabled globally', () => { const local = aLocalExtension('a'); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally) .then(() => { @@ -843,47 +838,32 @@ suite('ExtensionsActions', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = extensions[0]; assert.ok(!testObject.enabled); }); }); }); - test('Test DisableDropDownAction when extension is installed and disabled for workspace', () => { - const local = aLocalExtension('a'); - return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledWorkspace) - .then(() => { - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); - - return instantiationService.get(IExtensionsWorkbenchService).queryLocal() - .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); - testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); - }); - }); - }); - - test('Test DisableDropDownAction when extension is uninstalled', () => { + test('Test DisableGloballyAction when extension is uninstalled', () => { const gallery = aGalleryExtension('a'); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None) .then(page => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = page.firstPage[0]; assert.ok(!testObject.enabled); }); }); - test('Test DisableDropDownAction when extension is installing', () => { + test('Test DisableGloballyAction when extension is installing', () => { const gallery = aGalleryExtension('a'); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); return instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None) .then(page => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = page.firstPage[0]; instantiationService.createInstance(ExtensionContainers, [testObject]); installEvent.fire({ identifier: gallery.identifier, gallery }); @@ -891,13 +871,13 @@ suite('ExtensionsActions', () => { }); }); - test('Test DisableDropDownAction when extension is uninstalling', () => { + test('Test DisableGloballyAction when extension is uninstalling', () => { const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - const testObject: ExtensionsActions.DisableDropDownAction = instantiationService.createInstance(ExtensionsActions.DisableDropDownAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); + const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, [{ identifier: new ExtensionIdentifier('pub.a'), extensionLocation: URI.file('pub.a') }]); testObject.extension = extensions[0]; instantiationService.createInstance(ExtensionContainers, [testObject]); uninstallEvent.fire(local.identifier); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index bc816ccb..cde013f4 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -19,7 +19,7 @@ import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/ex import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -36,15 +36,14 @@ import { SinonStub } from 'sinon'; import { IExperimentService, ExperimentState, ExperimentActionType, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; -import { ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; -import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IMenuService } from 'vs/platform/actions/common/actions'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { Schemas } from 'vs/base/common/network'; suite('ExtensionsListView Tests', () => { @@ -101,14 +100,18 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IMenuService, new TestMenuService()); - instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService { - #localExtensionManagementServer: IExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; - constructor() { - super(instantiationService.get(ISharedProcessService), instantiationService.get(IRemoteAgentService), instantiationService.get(ILabelService), instantiationService); + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + instantiationService.stub(IExtensionManagementServerService, >{ + get localExtensionManagementServer(): IExtensionManagementServer { + return localExtensionManagementServer; + }, + getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null { + if (extension.location.scheme === Schemas.file) { + return localExtensionManagementServer; + } + throw new Error(`Invalid Extension ${extension.location}`); } - get localExtensionManagementServer(): IExtensionManagementServer { return this.#localExtensionManagementServer; } - set localExtensionManagementServer(server: IExtensionManagementServer) { } - }()); + }); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -165,7 +168,8 @@ suite('ExtensionsListView Tests', () => { } }); - instantiationService.stub(IExtensionService, { + instantiationService.stub(IExtensionService, >{ + onDidChangeExtensions: Event.None, getExtensions: (): Promise => { return Promise.resolve([ toExtensionDescription(localEnabledTheme), @@ -180,7 +184,7 @@ suite('ExtensionsListView Tests', () => { await (instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledLanguage], EnablementState.DisabledGlobally); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); - testableView = instantiationService.createInstance(ExtensionsListView, {}); + testableView = instantiationService.createInstance(ExtensionsListView, {}, {}); }); teardown(() => { @@ -482,7 +486,7 @@ suite('ExtensionsListView Tests', () => { }]); testableView.dispose(); - testableView = instantiationService.createInstance(ExtensionsListView, {}); + testableView = instantiationService.createInstance(ExtensionsListView, {}, {}); return testableView.show('search-me').then(result => { const options: IQueryOptions = queryTarget.args[0][0]; @@ -509,7 +513,7 @@ suite('ExtensionsListView Tests', () => { const queryTarget = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...realResults)); testableView.dispose(); - testableView = instantiationService.createInstance(ExtensionsListView, {}); + testableView = instantiationService.createInstance(ExtensionsListView, {}, {}); return testableView.show('search-me @sort:installs').then(result => { const options: IQueryOptions = queryTarget.args[0][0]; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index dc1e3e16..f4f5d8ee 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -39,7 +39,6 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; -import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IProductService } from 'vs/platform/product/common/productService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; @@ -68,7 +67,6 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(ILogService, NullLogService); instantiationService.stub(IProgressService, ProgressService); - instantiationService.stub(IStorageKeysSyncRegistryService, new StorageKeysSyncRegistryService()); instantiationService.stub(IProductService, {}); instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); @@ -224,8 +222,8 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal('1.1.0', actual.version); assert.equal('1.1.0', actual.latestVersion); assert.equal('localDescription1', actual.description); - assert.equal('file:///localPath1/localIcon1', actual.iconUrl); - assert.equal('file:///localPath1/localIcon1', actual.iconUrlFallback); + assert.ok(actual.iconUrl === 'file:///localPath1/localIcon1' || actual.iconUrl === 'vscode-file://vscode-app/localPath1/localIcon1'); + assert.ok(actual.iconUrlFallback === 'file:///localPath1/localIcon1' || actual.iconUrlFallback === 'vscode-file://vscode-app/localPath1/localIcon1'); assert.equal(null, actual.licenseUrl); assert.equal(ExtensionState.Installed, actual.state); assert.equal(null, actual.installCount); @@ -687,6 +685,11 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + options!.onCancel!(); + } + }); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) @@ -697,6 +700,28 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); + test('test disable extension disables all dependents when chosen to disable all', async () => { + const extensionA = aLocalExtension('a', { extensionDependencies: ['pub.b'] }); + const extensionB = aLocalExtension('b'); + const extensionC = aLocalExtension('c'); + + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + choices[0].run(); + } + }); + return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) + .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) + .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) + .then(async () => { + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); + testObject = await aWorkbenchService(); + await testObject.setEnablement(testObject.local[1], EnablementState.DisabledGlobally); + assert.equal(testObject.local[0].enablementState, EnablementState.DisabledGlobally); + assert.equal(testObject.local[1].enablementState, EnablementState.DisabledGlobally); + }); + }); + test('test disable extension when extension is part of a pack', async () => { const extensionA = aLocalExtension('a', { extensionPack: ['pub.b'] }); const extensionB = aLocalExtension('b'); @@ -802,6 +827,11 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.a'] }); const extensionC = aLocalExtension('c'); + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + options!.onCancel!(); + } + }); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) @@ -837,6 +867,11 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.c'] }); const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.a'] }); + instantiationService.stub(INotificationService, >{ + prompt(severity, message, choices, options) { + options!.onCancel!(); + } + }); return instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionA], EnablementState.EnabledGlobally) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionB], EnablementState.EnabledGlobally)) .then(() => instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([extensionC], EnablementState.EnabledGlobally)) diff --git a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts index 7b38b373..14840190 100644 --- a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts @@ -12,14 +12,13 @@ import { ITerminalService as IIntegratedTerminalService } from 'vs/workbench/con import { ResourceContextKey } from 'vs/workbench/common/resources'; import { IFileService } from 'vs/platform/files/common/files'; import { IListService } from 'vs/platform/list/browser/listService'; -import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; +import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { Schemas } from 'vs/base/common/network'; import { distinct } from 'vs/base/common/arrays'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { optional } from 'vs/platform/instantiation/common/instantiation'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/externalTerminal/node/TerminalHelper.scpt b/src/vs/workbench/contrib/externalTerminal/node/TerminalHelper.scpt index 145345e0..a151118b 100644 Binary files a/src/vs/workbench/contrib/externalTerminal/node/TerminalHelper.scpt and b/src/vs/workbench/contrib/externalTerminal/node/TerminalHelper.scpt differ diff --git a/src/vs/workbench/contrib/externalTerminal/node/iTermHelper.scpt b/src/vs/workbench/contrib/externalTerminal/node/iTermHelper.scpt index 6ef0afa6..f4341af8 100644 Binary files a/src/vs/workbench/contrib/externalTerminal/node/iTermHelper.scpt and b/src/vs/workbench/contrib/externalTerminal/node/iTermHelper.scpt differ diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 4a013f41..7a6e14dd 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -277,7 +277,7 @@ export class FeedbackDropdown extends Dropdown { this.sendButton = new Button(buttonsContainer); this.sendButton.enabled = false; this.sendButton.label = nls.localize('tweet', "Tweet"); - dom.prepend(this.sendButton.element, dom.$('span.codicon.codicon-twitter')); + dom.prepend(this.sendButton.element, dom.$('span' + Codicon.twitter.cssSelector)); this.sendButton.element.classList.add('send'); this.sendButton.element.title = nls.localize('tweetFeedback', "Tweet Feedback"); disposables.add(attachButtonStyler(this.sendButton, this.themeService)); @@ -288,7 +288,7 @@ export class FeedbackDropdown extends Dropdown { if (this.feedbackForm) { this.feedbackForm.style.backgroundColor = colors.editorWidgetBackground ? colors.editorWidgetBackground.toString() : ''; this.feedbackForm.style.color = colors.editorWidgetForeground ? colors.editorWidgetForeground.toString() : ''; - this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px ${colors.widgetShadow}` : ''; + this.feedbackForm.style.boxShadow = colors.widgetShadow ? `0 0 8px 2px ${colors.widgetShadow}` : ''; } if (this.feedbackDescriptionInput) { this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : ''; diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index 3bbf80a0..5f4f0b99 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -28,7 +28,7 @@ class TwitterFeedbackService implements IFeedbackDelegate { } submitFeedback(feedback: IFeedback, openerService: IOpenerService): void { - const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : null}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`; + const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : ''}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`; const url = TwitterFeedbackService.TWITTER_URL + queryString; openerService.open(URI.parse(url)); diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index ad397db1..ec837dc4 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -9,7 +9,7 @@ import { isFunction, assertIsDefined } from 'vs/base/common/types'; import { isValidBasename } from 'vs/base/common/extpath'; import { basename } from 'vs/base/common/resources'; import { Action } from 'vs/base/common/actions'; -import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { VIEWLET_ID, TEXT_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorOptions, TextEditorOptions, IEditorInput, IEditorOpenContext } from 'vs/workbench/common/editor'; @@ -30,6 +30,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; /** * An implementation of editor for file system resources. @@ -60,12 +61,16 @@ export class TextFileEditor extends BaseTextEditor { // Move view state for moved files this._register(this.fileService.onDidRunOperation(e => this.onDidRunOperation(e))); + + // Listen to file system provider changes + this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onDidFileSystemProviderChange(e.scheme))); + this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onDidFileSystemProviderChange(e.scheme))); } private onDidFilesChange(e: FileChangesEvent): void { const deleted = e.getDeleted(); if (deleted?.length) { - this.clearTextEditorViewState(deleted.map(d => d.resource)); + this.clearTextEditorViewState(deleted.map(({ resource }) => resource)); } } @@ -75,6 +80,14 @@ export class TextFileEditor extends BaseTextEditor { } } + private onDidFileSystemProviderChange(scheme: string): void { + const control = this.getControl(); + const input = this.input; + if (control && input?.resource.scheme === scheme) { + control.updateOptions({ readOnly: input.isReadonly() }); + } + } + protected onWillCloseEditorInGroup(editor: IEditorInput): void { // React to editors closing to preserve or clear view state. This needs to happen diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 023992ae..6873c2f1 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -23,7 +23,7 @@ import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileE import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; import { INotificationService, INotificationHandle, INotificationActions, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; import { IProductService } from 'vs/platform/product/common/productService'; import { Event } from 'vs/base/common/event'; @@ -32,7 +32,6 @@ import { isWindows } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { SaveReason } from 'vs/workbench/common/editor'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext'; export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; @@ -56,13 +55,9 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa @ITextModelService textModelService: ITextModelService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, version: 1 }); - const provider = this._register(instantiationService.createInstance(TextFileContentProvider)); this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider)); @@ -225,7 +220,7 @@ class DoNotShowResolveConflictLearnMoreAction extends Action { } async run(notification: IDisposable): Promise { - this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL); + this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL, StorageTarget.USER); // Hide notification notification.dispose(); @@ -239,13 +234,9 @@ class ResolveSaveConflictAction extends Action { @IEditorService private readonly editorService: IEditorService, @INotificationService private readonly notificationService: INotificationService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IProductService private readonly productService: IProductService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + @IProductService private readonly productService: IProductService ) { super('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare")); - - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, version: 1 }); } async run(): Promise { diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/browser/explorerService.ts similarity index 68% rename from src/vs/workbench/contrib/files/common/explorerService.ts rename to src/vs/workbench/contrib/files/browser/explorerService.ts index a5069cb0..a1a53757 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/browser/explorerService.ts @@ -6,32 +6,29 @@ import { Event } from 'vs/base/common/event'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IExplorerService, IFilesConfiguration, SortOrder, IExplorerView } from 'vs/workbench/contrib/files/common/files'; +import { IFilesConfiguration, SortOrder } from 'vs/workbench/contrib/files/common/files'; import { ExplorerItem, ExplorerModel } from 'vs/workbench/contrib/files/common/explorerModel'; import { URI } from 'vs/base/common/uri'; -import { FileOperationEvent, FileOperation, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files'; +import { FileOperationEvent, FileOperation, IFileService, FileChangesEvent, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files'; import { dirname } from 'vs/base/common/resources'; -import { memoize } from 'vs/base/common/decorators'; -import { ResourceGlobMatcher } from 'vs/workbench/common/resources'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { IExpression } from 'vs/base/common/glob'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditableData } from 'vs/workbench/common/views'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IBulkEditService, ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; +import { UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo'; +import { IExplorerView, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { RunOnceScheduler } from 'vs/base/common/async'; -function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { - const scope = root ? { resource: root } : undefined; - const configuration = scope ? configurationService.getValue(scope) : configurationService.getValue(); - - return configuration?.files?.exclude || Object.create(null); -} +export const UNDO_REDO_SOURCE = new UndoRedoSource(); export class ExplorerService implements IExplorerService { declare readonly _serviceBrand: undefined; - private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first + private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 1000; // delay in ms to react to file changes to give our internal events a chance to react first private readonly disposables = new DisposableStore(); private editable: { stat: ExplorerItem, data: IEditableData } | undefined; @@ -39,22 +36,54 @@ export class ExplorerService implements IExplorerService { private cutItems: ExplorerItem[] | undefined; private view: IExplorerView | undefined; private model: ExplorerModel; + private onFileChangesScheduler: RunOnceScheduler; + private fileChangeEvents: FileChangesEvent[] = []; constructor( @IFileService private fileService: IFileService, - @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IClipboardService private clipboardService: IClipboardService, @IEditorService private editorService: IEditorService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IBulkEditService private readonly bulkEditService: IBulkEditService, + @IProgressService private readonly progressService: IProgressService ) { this._sortOrder = this.configurationService.getValue('explorer.sortOrder'); this.model = new ExplorerModel(this.contextService, this.uriIdentityService, this.fileService); this.disposables.add(this.model); this.disposables.add(this.fileService.onDidRunOperation(e => this.onDidRunOperation(e))); - this.disposables.add(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e))); + + this.onFileChangesScheduler = new RunOnceScheduler(async () => { + const events = this.fileChangeEvents; + this.fileChangeEvents = []; + + // Filter to the ones we care + const types = [FileChangeType.ADDED, FileChangeType.DELETED]; + if (this._sortOrder === SortOrder.Modified) { + types.push(FileChangeType.UPDATED); + } + + let shouldRefresh = false; + this.roots.forEach(r => { + if (this.view && !shouldRefresh) { + shouldRefresh = doesFileEventAffect(r, this.view, events, types); + } + }); + + if (shouldRefresh) { + await this.refresh(false); + } + + }, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY); + + this.disposables.add(this.fileService.onDidFilesChange(e => { + this.fileChangeEvents.push(e); + if (!this.onFileChangesScheduler.isScheduled()) { + this.onFileChangesScheduler.schedule(); + } + })); this.disposables.add(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); this.disposables.add(Event.any<{ scheme: string }>(this.fileService.onDidChangeFileSystemProviderRegistrations, this.fileService.onDidChangeFileSystemProviderCapabilities)(async e => { let affected = false; @@ -96,16 +125,27 @@ export class ExplorerService implements IExplorerService { return this.view.getContext(respectMultiSelection); } - // Memoized locals - @memoize private get fileEventsFilter(): ResourceGlobMatcher { - const fileEventsFilter = this.instantiationService.createInstance( - ResourceGlobMatcher, - (root?: URI) => getFileEventsExcludes(this.configurationService, root), - (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) - ); - this.disposables.add(fileEventsFilter); + async applyBulkEdit(edit: ResourceFileEdit[], options: { undoLabel: string, progressLabel: string }): Promise { + const cancellationTokenSource = new CancellationTokenSource(); + const promise = this.progressService.withProgress({ + location: ProgressLocation.Window, + delay: 500, + title: options.progressLabel, + cancellable: edit.length > 1 // Only allow cancellation when there is more than one edit. Since cancelling will not actually stop the current edit that is in progress. + }, async progress => { + await this.bulkEditService.apply(edit, { + undoRedoSource: UNDO_REDO_SOURCE, + label: options.undoLabel, + progress, + token: cancellationTokenSource.token + }); + }, () => cancellationTokenSource.cancel()); + await this.progressService.withProgress({ location: ProgressLocation.Explorer, delay: 500 }, () => promise); + cancellationTokenSource.dispose(); + } - return fileEventsFilter; + hasViewFocus(): boolean { + return !!this.view && this.view.hasFocus(); } // IExplorerService methods @@ -288,100 +328,6 @@ export class ExplorerService implements IExplorerService { } } - private onDidFilesChange(e: FileChangesEvent): void { - // Check if an explorer refresh is necessary (delayed to give internal events a chance to react first) - // Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might - // be fired first over the other or not at all. - setTimeout(async () => { - // Filter to the ones we care - const shouldRefresh = () => { - e = this.filterToViewRelevantEvents(e); - // Handle added files/folders - const added = e.getAdded(); - if (added.length) { - - // Check added: Refresh if added file/folder is not part of resolved root and parent is part of it - const ignoredPaths: Set = new Set(); - for (let i = 0; i < added.length; i++) { - const change = added[i]; - - // Find parent - const parent = dirname(change.resource); - - // Continue if parent was already determined as to be ignored - if (ignoredPaths.has(parent.toString())) { - continue; - } - - // Compute if parent is visible and added file not yet part of it - const parentStat = this.model.findClosest(parent); - if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { - return true; - } - - // Keep track of path that can be ignored for faster lookup - if (!parentStat || !parentStat.isDirectoryResolved) { - ignoredPaths.add(parent.toString()); - } - } - } - - // Handle deleted files/folders - const deleted = e.getDeleted(); - if (deleted.length) { - - // Check deleted: Refresh if deleted file/folder part of resolved root - for (let j = 0; j < deleted.length; j++) { - const del = deleted[j]; - const item = this.model.findClosest(del.resource); - if (item && item.parent) { - return true; - } - } - } - - // Handle updated files/folders if we sort by modified - if (this._sortOrder === SortOrder.Modified) { - const updated = e.getUpdated(); - - // Check updated: Refresh if updated file/folder part of resolved root - for (let j = 0; j < updated.length; j++) { - const upd = updated[j]; - const item = this.model.findClosest(upd.resource); - - if (item && item.parent) { - return true; - } - } - } - - return false; - }; - - if (shouldRefresh()) { - await this.refresh(false); - } - }, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY); - } - - private filterToViewRelevantEvents(e: FileChangesEvent): FileChangesEvent { - return e.filter(change => { - if (change.type === FileChangeType.UPDATED && this._sortOrder !== SortOrder.Modified) { - return false; // we only are about updated if we sort by modified time - } - - if (!this.contextService.isInsideWorkspace(change.resource)) { - return false; // exclude changes for resources outside of workspace - } - - if (this.fileEventsFilter.matches(change.resource)) { - return false; // excluded via files.exclude setting - } - - return true; - }); - } - private async onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): Promise { const configSortOrder = configuration?.explorer?.sortOrder || 'default'; if (this._sortOrder !== configSortOrder) { @@ -397,3 +343,20 @@ export class ExplorerService implements IExplorerService { this.disposables.dispose(); } } + +function doesFileEventAffect(item: ExplorerItem, view: IExplorerView, events: FileChangesEvent[], types: FileChangeType[]): boolean { + if (events.some(e => e.affects(item.resource, ...types))) { + return true; + } + for (let [_name, child] of item.children) { + if (view.isItemVisible(child)) { + if (child.isDirectory && child.isDirectoryResolved) { + if (doesFileEventAffect(child, view, events, types)) { + return true; + } + } + } + } + + return false; +} diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 54912b10..9745ec52 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -38,6 +38,9 @@ import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { AddRootFolderAction, OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { isMacintosh } from 'vs/base/common/platform'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +const explorerViewIcon = registerIcon('explorer-view-icon', Codicon.files, localize('explorerViewIcon', 'View icon of the explorer view.')); export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution { @@ -108,7 +111,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor id: OpenEditorsView.ID, name: OpenEditorsView.NAME, ctorDescriptor: new SyncDescriptor(OpenEditorsView), - containerIcon: 'codicon-files', + containerIcon: explorerViewIcon, order: 0, when: OpenEditorsVisibleContext, canToggleVisibility: true, @@ -125,7 +128,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: EmptyView.ID, name: EmptyView.NAME, - containerIcon: Codicon.files.classNames, + containerIcon: explorerViewIcon, ctorDescriptor: new SyncDescriptor(EmptyView), order: 1, canToggleVisibility: true, @@ -139,7 +142,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: VIEW_ID, name: localize('folders', "Folders"), - containerIcon: Codicon.files.classNames, + containerIcon: explorerViewIcon, ctorDescriptor: new SyncDescriptor(ExplorerView), order: 1, canToggleVisibility: false, @@ -267,7 +270,7 @@ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewC name: localize('explore', "Explorer"), ctorDescriptor: new SyncDescriptor(ExplorerViewPaneContainer), storageId: 'workbench.explorer.views.state', - icon: Codicon.files.classNames, + icon: explorerViewIcon, alwaysUseContainerInfo: true, order: 0 }, ViewContainerLocation.Sidebar, true); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index fd399160..80deb826 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -15,7 +15,7 @@ import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/c import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { isMacintosh } from 'vs/base/common/platform'; -import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; +import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands'; import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -29,6 +29,8 @@ import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAc import { ActiveEditorContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; +import { Codicon } from 'vs/base/common/codicons'; // Contribute Global Actions const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; @@ -182,8 +184,8 @@ export function appendEditorTitleContextMenuItem(id: string, title: string, when } // Editor Title Menu for Conflict Resolution -appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), { id: 'codicon/check' }, -10, acceptLocalChangesCommand); -appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), { id: 'codicon/discard' }, -9, revertLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), Codicon.check, -10, acceptLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), Codicon.discard, -9, revertLocalChangesCommand); function appendSaveConflictEditorTitleAction(id: string, title: string, icon: ThemeIcon, order: number, command: ICommandHandler): void { diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 4f042f28..46aacd31 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -12,9 +12,8 @@ import { URI } from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Action } from 'vs/base/common/actions'; import { DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { VIEWLET_ID, IExplorerService, IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { BinarySize, IFileService, IFileStatWithMetadata, IFileStreamContent } from 'vs/platform/files/common/files'; +import { VIEWLET_ID, IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; +import { ByteSize, IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; @@ -22,9 +21,9 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ITextModel } from 'vs/editor/common/model'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands'; +import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -53,26 +52,21 @@ import { IProgressService, IProgressStep, ProgressLocation } from 'vs/platform/p import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); - export const NEW_FOLDER_COMMAND_ID = 'explorer.newFolder'; export const NEW_FOLDER_LABEL = nls.localize('newFolder', "New Folder"); - export const TRIGGER_RENAME_LABEL = nls.localize('rename', "Rename"); - export const MOVE_FILE_TO_TRASH_LABEL = nls.localize('delete', "Delete"); - export const COPY_FILE_LABEL = nls.localize('copyFile', "Copy"); - export const PASTE_FILE_LABEL = nls.localize('pasteFile', "Paste"); - export const FileCopiedContext = new RawContextKey('fileCopied', false); - export const DOWNLOAD_LABEL = nls.localize('download', "Download..."); - const CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete'; +const MAX_UNDO_FILE_SIZE = 5000000; // 5mb function onError(notificationService: INotificationService, error: any): void { if (error.message === 'string') { @@ -123,7 +117,7 @@ export class NewFolderAction extends Action { } } -async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise { +async function deleteFiles(explorerService: IExplorerService, workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise { let primaryButton: string; if (useTrash) { primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash"); @@ -216,7 +210,7 @@ async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dial // Check for confirmation checkbox if (confirmation.confirmed && confirmation.checkboxChecked === true) { - await configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER); + await configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false); } // Check for confirmation @@ -226,7 +220,12 @@ async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dial // Call function try { - await workingCopyFileService.delete(distinctElements.map(e => e.resource), { useTrash, recursive: true }); + const resourceFileEdits = distinctElements.map(e => new ResourceFileEdit(e.resource, undefined, { recursive: true, folder: e.isDirectory, skipTrashBin: !useTrash, maxSize: MAX_UNDO_FILE_SIZE })); + const options = { + undoLabel: distinctElements.length > 1 ? nls.localize('deleteBulkEdit', "Delete {0} files", distinctElements.length) : nls.localize('deleteFileBulkEdit', "Delete {0}", distinctElements[0].name), + progressLabel: distinctElements.length > 1 ? nls.localize('deletingBulkEdit', "Deleting {0} files", distinctElements.length) : nls.localize('deletingFileBulkEdit', "Deleting {0}", distinctElements[0].name), + }; + await explorerService.applyBulkEdit(resourceFileEdits, options); } catch (error) { // Handle error to delete file(s) from a modal confirmation dialog @@ -256,7 +255,7 @@ async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dial skipConfirm = true; - return deleteFiles(workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm); + return deleteFiles(explorerService, workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm); } } } @@ -472,7 +471,7 @@ export class GlobalCompareResourcesAction extends Action { override: this.editorService.openEditor({ leftResource: activeResource, rightResource: resource, - options: { override: false } + options: { override: false, pinned: true } }) }; } @@ -482,7 +481,7 @@ export class GlobalCompareResourcesAction extends Action { return { override: this.editorService.openEditor({ resource: activeResource, - options: { override: false } + options: { override: false, pinned: true } }) }; } @@ -828,7 +827,11 @@ export class CompareWithClipboardAction extends Action { const name = resources.basename(resource); const editorLabel = nls.localize('clipboardComparisonLabel', "Clipboard ↔ {0}", name); - await this.editorService.openEditor({ leftResource: resource.with({ scheme }), rightResource: resource, label: editorLabel }).finally(() => { + await this.editorService.openEditor({ + leftResource: resource.with({ scheme }), + rightResource: resource, label: editorLabel, + options: { pinned: true } + }).finally(() => { dispose(this.registrationDisposal); this.registrationDisposal = undefined; }); @@ -870,13 +873,26 @@ function onErrorWithRetry(notificationService: INotificationService, error: unkn async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boolean): Promise { const explorerService = accessor.get(IExplorerService); const fileService = accessor.get(IFileService); - const textFileService = accessor.get(ITextFileService); const editorService = accessor.get(IEditorService); const viewsService = accessor.get(IViewsService); const notificationService = accessor.get(INotificationService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); + const commandService = accessor.get(ICommandService); - await viewsService.openView(VIEW_ID, true); + const wasHidden = !viewsService.isViewVisible(VIEW_ID); + const view = await viewsService.openView(VIEW_ID, true); + if (wasHidden) { + // Give explorer some time to resolve itself #111218 + await timeout(500); + } + if (!view) { + // Can happen in empty workspace case (https://github.com/microsoft/vscode/issues/100604) + + if (isFolder) { + throw new Error('Open a folder or workspace first.'); + } + + return commandService.executeCommand(NEW_UNTITLED_FILE_COMMAND_ID); + } const stats = explorerService.getContext(false); const stat = stats.length > 0 ? stats[0] : undefined; @@ -896,12 +912,18 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole const onSuccess = async (value: string): Promise => { try { - const created = isFolder ? await workingCopyFileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value)); + const resourceToCreate = resources.joinPath(folder.resource, value); + await explorerService.applyBulkEdit([new ResourceFileEdit(undefined, resourceToCreate, { folder: isFolder })], { + undoLabel: nls.localize('createBulkEdit', "Create {0}", value), + progressLabel: nls.localize('creatingBulkEdit', "Creating {0}", value) + }); await refreshIfSeparator(value, explorerService); - isFolder ? - await explorerService.select(created.resource, true) : - await editorService.openEditor({ resource: created.resource, options: { pinned: true } }); + if (isFolder) { + await explorerService.select(resourceToCreate, true); + } else { + await editorService.openEditor({ resource: resourceToCreate, options: { pinned: true } }); + } } catch (error) { onErrorWithRetry(notificationService, error, () => onSuccess(value)); } @@ -935,7 +957,6 @@ CommandsRegistry.registerCommand({ export const renameHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); const notificationService = accessor.get(INotificationService); const stats = explorerService.getContext(false); @@ -952,7 +973,10 @@ export const renameHandler = async (accessor: ServicesAccessor) => { const targetResource = resources.joinPath(parentResource, value); if (stat.resource.toString() !== targetResource.toString()) { try { - await workingCopyFileService.move([{ source: stat.resource, target: targetResource }]); + await explorerService.applyBulkEdit([new ResourceFileEdit(stat.resource, targetResource)], { + undoLabel: nls.localize('renameBulkEdit', "Rename {0} to {1}", stat.name, value), + progressLabel: nls.localize('renamingBulkEdit', "Renaming {0} to {1}", stat.name, value), + }); await refreshIfSeparator(value, explorerService); } catch (e) { notificationService.error(e); @@ -968,7 +992,7 @@ export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); const stats = explorerService.getContext(true).filter(s => !s.isRoot); if (stats.length) { - await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true); + await deleteFiles(accessor.get(IExplorerService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true); } }; @@ -977,7 +1001,7 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => { const stats = explorerService.getContext(true).filter(s => !s.isRoot); if (stats.length) { - await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false); + await deleteFiles(accessor.get(IExplorerService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false); } }; @@ -1001,10 +1025,9 @@ export const cutFileHandler = async (accessor: ServicesAccessor) => { }; export const DOWNLOAD_COMMAND_ID = 'explorer.download'; -const downloadFileHandler = (accessor: ServicesAccessor) => { +const downloadFileHandler = async (accessor: ServicesAccessor) => { const logService = accessor.get(ILogService); const fileService = accessor.get(IFileService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); const fileDialogService = accessor.get(IFileDialogService); const explorerService = accessor.get(IExplorerService); const progressService = accessor.get(IProgressService); @@ -1014,7 +1037,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { const cts = new CancellationTokenSource(); - const downloadPromise = progressService.withProgress({ + await progressService.withProgress({ location: ProgressLocation.Window, delay: 800, cancellable: isWeb, @@ -1034,7 +1057,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { return; } - const maxBlobDownloadSize = 32 * BinarySize.MB; // avoid to download via blob-trick >32MB to avoid memory pressure + const maxBlobDownloadSize = 32 * ByteSize.MB; // avoid to download via blob-trick >32MB to avoid memory pressure const preferFileSystemAccessWebApis = stat.isDirectory || stat.size > maxBlobDownloadSize; // Folder: use FS APIs to download files and folders if available and preferred @@ -1051,9 +1074,15 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { fileBytesDownloaded: number; } - async function pipeContents(name: string, source: IFileStreamContent, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { + async function downloadFileBuffered(resource: URI, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { + const contents = await fileService.readFileStream(resource); + if (cts.token.isCancellationRequested) { + target.close(); + return; + } + return new Promise((resolve, reject) => { - const sourceStream = source.value; + const sourceStream = contents.value; const disposables = new DisposableStore(); disposables.add(toDisposable(() => target.close())); @@ -1069,7 +1098,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { sourceStream.on('data', data => { if (!disposed) { target.write(data.buffer); - reportProgress(name, source.size, data.byteLength, operation); + reportProgress(contents.name, contents.size, data.byteLength, operation); } }); @@ -1085,18 +1114,34 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { }); } - async function downloadFile(targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, name: string, resource: URI, operation: IDownloadOperation): Promise { + async function downloadFileUnbuffered(resource: URI, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { + const contents = await fileService.readFile(resource); + if (!cts.token.isCancellationRequested) { + target.write(contents.value.buffer); + reportProgress(contents.name, contents.size, contents.value.byteLength, operation); + } + + target.close(); + } + + async function downloadFile(targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, file: IFileStatWithMetadata, operation: IDownloadOperation): Promise { // Report progress operation.filesDownloaded++; operation.fileBytesDownloaded = 0; // reset for this file - reportProgress(name, 0, 0, operation); + reportProgress(file.name, 0, 0, operation); // Start to download - const targetFile = await targetFolder.getFileHandle(name, { create: true }); + const targetFile = await targetFolder.getFileHandle(file.name, { create: true }); const targetFileWriter = await targetFile.createWritable(); - return pipeContents(name, await fileService.readFileStream(resource), targetFileWriter, operation); + // For large files, write buffered using streams + if (file.size > ByteSize.MB) { + return downloadFileBuffered(file.resource, targetFileWriter, operation); + } + + // For small files prefer to write unbuffered to reduce overhead + return downloadFileUnbuffered(file.resource, targetFileWriter, operation); } async function downloadFolder(folder: IFileStatWithMetadata, targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, operation: IDownloadOperation): Promise { @@ -1109,7 +1154,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { } if (child.isFile) { - await downloadFile(targetFolder, child.name, child.resource, operation); + await downloadFile(targetFolder, child, operation); } else { const childFolder = await targetFolder.getDirectoryHandle(child.name, { create: true }); const resolvedChildFolder = await fileService.resolve(child.resource, { resolveMetadata: true }); @@ -1128,17 +1173,17 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { // Small file let message: string; - if (fileSize < BinarySize.MB) { + if (fileSize < ByteSize.MB) { if (operation.filesTotal === 1) { message = name; } else { - message = nls.localize('downloadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesDownloaded, operation.filesTotal, BinarySize.formatSize(bytesDownloadedPerSecond)); + message = nls.localize('downloadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesDownloaded, operation.filesTotal, ByteSize.formatSize(bytesDownloadedPerSecond)); } } // Large file else { - message = nls.localize('downloadProgressLarge', "{0} ({1} of {2}, {3}/s)", name, BinarySize.formatSize(operation.fileBytesDownloaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesDownloadedPerSecond)); + message = nls.localize('downloadProgressLarge', "{0} ({1} of {2}, {3}/s)", name, ByteSize.formatSize(operation.fileBytesDownloaded), ByteSize.formatSize(fileSize), ByteSize.formatSize(bytesDownloadedPerSecond)); } // Report progress but limit to update only once per second @@ -1162,7 +1207,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { const targetFolder = await parentFolder.getDirectoryHandle(stat.name, { create: true }); await downloadFolder(stat, targetFolder, operation); } else { - await downloadFile(parentFolder, stat.name, stat.resource, operation); + await downloadFile(parentFolder, stat, operation); } operation.progressScheduler.dispose(); @@ -1191,10 +1236,8 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { else { progress.report({ message: explorerItem.name }); - let defaultUri = explorerItem.isDirectory ? fileDialogService.defaultFolderPath(Schemas.file) : fileDialogService.defaultFilePath(Schemas.file); - if (defaultUri) { - defaultUri = resources.joinPath(defaultUri, explorerItem.name); - } + let defaultUri = explorerItem.isDirectory ? await fileDialogService.defaultFolderPath(Schemas.file) : await fileDialogService.defaultFilePath(Schemas.file); + defaultUri = resources.joinPath(defaultUri, explorerItem.name); const destination = await fileDialogService.showSaveDialog({ availableFileSystems: [Schemas.file], @@ -1204,16 +1247,16 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { }); if (destination) { - await workingCopyFileService.copy([{ source: explorerItem.resource, target: destination }], { overwrite: true }); + await explorerService.applyBulkEdit([new ResourceFileEdit(explorerItem.resource, destination, { overwrite: true, copy: true })], { + undoLabel: nls.localize('downloadBulkEdit', "Download {0}", explorerItem.name), + progressLabel: nls.localize('downloadingBulkEdit', "Downloading {0}", explorerItem.name), + }); } else { cts.cancel(); // User canceled a download. In case there were multiple files selected we should cancel the remainder of the prompts #86100 } } })); }, () => cts.dispose(true)); - - // Also indicate progress in the files view - progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => downloadPromise); }; CommandsRegistry.registerCommand({ @@ -1225,7 +1268,6 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { const clipboardService = accessor.get(IClipboardService); const explorerService = accessor.get(IExplorerService); const fileService = accessor.get(IFileService); - const workingCopyFileService = accessor.get(IWorkingCopyFileService); const notificationService = accessor.get(INotificationService); const editorService = accessor.get(IEditorService); const configurationService = accessor.get(IConfigurationService); @@ -1258,21 +1300,31 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { return { source: fileToPaste, target: targetFile }; })); - // Move/Copy File - let stats: IFileStatWithMetadata[] = []; - if (pasteShouldMove) { - stats = await workingCopyFileService.move(sourceTargetPairs); - } else { - stats = await workingCopyFileService.copy(sourceTargetPairs); - } - - if (stats.length >= 1) { - const stat = stats[0]; - if (stat && !stat.isDirectory && stats.length === 1) { - await editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); + if (sourceTargetPairs.length >= 1) { + // Move/Copy File + if (pasteShouldMove) { + const resourceFileEdits = sourceTargetPairs.map(pair => new ResourceFileEdit(pair.source, pair.target)); + const options = { + progressLabel: sourceTargetPairs.length > 1 ? nls.localize('movingBulkEdit', "Moving {0} files", sourceTargetPairs.length) : nls.localize('movingFileBulkEdit', "Moving {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)), + undoLabel: sourceTargetPairs.length > 1 ? nls.localize('moveBulkEdit', "Move {0} files", sourceTargetPairs.length) : nls.localize('moveFileBulkEdit', "Move {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)) + }; + await explorerService.applyBulkEdit(resourceFileEdits, options); + } else { + const resourceFileEdits = sourceTargetPairs.map(pair => new ResourceFileEdit(pair.source, pair.target, { copy: true })); + const options = { + progressLabel: sourceTargetPairs.length > 1 ? nls.localize('copyingBulkEdit', "Copying {0} files", sourceTargetPairs.length) : nls.localize('copyingFileBulkEdit', "Copying {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)), + undoLabel: sourceTargetPairs.length > 1 ? nls.localize('copyBulkEdit', "Copy {0} files", sourceTargetPairs.length) : nls.localize('copyFileBulkEdit', "Copy {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)) + }; + await explorerService.applyBulkEdit(resourceFileEdits, options); } - if (stat) { - await explorerService.select(stat.resource); + + const pair = sourceTargetPairs[0]; + await explorerService.select(pair.target); + if (sourceTargetPairs.length === 1) { + const item = explorerService.findClosest(pair.target); + if (item && !item.isDirectory) { + await editorService.openEditor({ resource: item.resource, options: { pinned: true, preserveFocus: true } }); + } } } } catch (e) { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index f8fc0ee1..8e1badc5 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -11,7 +11,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ExplorerFocusCondition, TextFileContentProvider, VIEWLET_ID, IExplorerService, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, FilesExplorerFocusCondition, ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files'; +import { ExplorerFocusCondition, TextFileContentProvider, VIEWLET_ID, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, FilesExplorerFocusCondition, ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files'; import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -23,7 +23,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { isWindows } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection } from 'vs/workbench/contrib/files/browser/files'; +import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Schemas } from 'vs/base/common/network'; @@ -44,6 +44,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; // Commands @@ -134,10 +135,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const untitledResources = resources.filter(resource => resource.scheme === Schemas.untitled); const fileResources = resources.filter(resource => resource.scheme !== Schemas.untitled); - const resolved = await fileService.resolveAll(fileResources.map(resource => ({ resource }))); - const editors = resolved.filter(r => r.stat && r.success && !r.stat.isDirectory).map(r => ({ - resource: r.stat!.resource - })).concat(...untitledResources.map(untitledResource => ({ resource: untitledResource }))); + const items = await Promise.all(fileResources.map(async resource => { + const item = explorerService.findClosest(resource); + if (item) { + // Explorer already resolved the item, no need to go to the file service #109780 + return item; + } + + return await fileService.resolve(resource); + })); + const files = items.filter(i => !i.isDirectory); + const editors = files.map(f => ({ + resource: f.resource, + options: { pinned: true } + })).concat(...untitledResources.map(untitledResource => ({ resource: untitledResource, options: { pinned: true } }))); await editorService.openEditors(editors, SIDE_GROUP); } @@ -146,7 +157,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib + 10, - when: ContextKeyExpr.and(ExplorerFocusCondition, ExplorerFolderContext.toNegated()), + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerFolderContext.toNegated()), primary: KeyCode.Enter, mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow @@ -157,7 +168,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const resources = explorerService.getContext(true); if (resources.length) { - await editorService.openEditors(resources.map(r => ({ resource: r.resource, options: { preserveFocus: false } }))); + await editorService.openEditors(resources.map(r => ({ resource: r.resource, options: { preserveFocus: false, pinned: true } }))); } } }); @@ -192,7 +203,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const editorLabel = nls.localize('modifiedLabel', "{0} (in file) ↔ {1}", name, name); try { - await TextFileContentProvider.open(uri, COMPARE_WITH_SAVED_SCHEMA, editorLabel, editorService); + await TextFileContentProvider.open(uri, COMPARE_WITH_SAVED_SCHEMA, editorLabel, editorService, { pinned: true }); // Dispose once no more diff editor is opened with the scheme if (registerEditorListener) { providerDisposables.push(editorService.onDidVisibleEditorsChange(() => { @@ -233,7 +244,8 @@ CommandsRegistry.registerCommand({ if (resources.length === 2) { return editorService.openEditor({ leftResource: resources[0], - rightResource: resources[1] + rightResource: resources[1], + options: { pinned: true } }); } @@ -251,7 +263,8 @@ CommandsRegistry.registerCommand({ if (globalResourceToCompare && rightResource) { editorService.openEditor({ leftResource: globalResourceToCompare, - rightResource + rightResource, + options: { pinned: true } }); } } @@ -432,7 +445,9 @@ async function doSaveEditors(accessor: ServicesAccessor, editors: IEditorIdentif try { await editorService.save(editors, options); } catch (error) { - notificationService.error(nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); + if (!isPromiseCanceledError(error)) { + notificationService.error(nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); + } } } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 160acf7b..7ea77d33 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -14,12 +14,12 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; import { AutoSaveConfiguration, HotExitConfiguration, FILES_EXCLUDE_CONFIG, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; -import { VIEWLET_ID, SortOrder, FILE_EDITOR_INPUT_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { VIEWLET_ID, SortOrder, FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files'; import { TextFileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/textFileEditorTracker'; import { TextFileSaveErrorHandler } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { BinaryFileEditor } from 'vs/workbench/contrib/files/browser/editors/binaryFileEditor'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -33,13 +33,16 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ExplorerService } from 'vs/workbench/contrib/files/common/explorerService'; +import { ExplorerService, UNDO_REDO_SOURCE } from 'vs/workbench/contrib/files/browser/explorerService'; import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/encoding'; import { Schemas } from 'vs/base/common/network'; import { WorkspaceWatcher } from 'vs/workbench/contrib/files/common/workspaceWatcher'; import { editorConfigurationBaseNode } from 'vs/editor/common/config/commonEditorConfig'; import { DirtyFilesIndicator } from 'vs/workbench/contrib/files/common/dirtyFilesIndicator'; import { isEqual } from 'vs/base/common/resources'; +import { UndoCommand, RedoCommand } from 'vs/editor/browser/editorExtensions'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; // Viewlet Action export class OpenExplorerViewletAction extends ShowViewletAction { @@ -102,8 +105,9 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register default file input factory Registry.as(EditorInputExtensions.EditorInputFactories).registerFileEditorInputFactory({ - createFileEditorInput: (resource, preferredResource, encoding, mode, instantiationService): IFileEditorInput => { - return instantiationService.createInstance(FileEditorInput, resource, preferredResource, encoding, mode); + + createFileEditorInput: (resource, preferredResource, preferredName, preferredDescription, preferredEncoding, preferredMode, instantiationService): IFileEditorInput => { + return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, preferredEncoding, preferredMode); }, isFileEditorInput: (obj): obj is IFileEditorInput => { @@ -114,6 +118,8 @@ Registry.as(EditorInputExtensions.EditorInputFactor interface ISerializedFileEditorInput { resourceJSON: UriComponents; preferredResourceJSON?: UriComponents; + name?: string; + description?: string; encoding?: string; modeId?: string; } @@ -132,6 +138,8 @@ class FileEditorInputFactory implements IEditorInputFactory { const serializedFileEditorInput: ISerializedFileEditorInput = { resourceJSON: resource.toJSON(), preferredResourceJSON: isEqual(resource, preferredResource) ? undefined : preferredResource, // only storing preferredResource if it differs from the resource + name: fileEditorInput.getPreferredName(), + description: fileEditorInput.getPreferredDescription(), encoding: fileEditorInput.getEncoding(), modeId: fileEditorInput.getPreferredMode() // only using the preferred user associated mode here if available to not store redundant data }; @@ -144,10 +152,12 @@ class FileEditorInputFactory implements IEditorInputFactory { const serializedFileEditorInput: ISerializedFileEditorInput = JSON.parse(serializedEditorInput); const resource = URI.revive(serializedFileEditorInput.resourceJSON); const preferredResource = URI.revive(serializedFileEditorInput.preferredResourceJSON); + const name = serializedFileEditorInput.name; + const description = serializedFileEditorInput.description; const encoding = serializedFileEditorInput.encoding; const mode = serializedFileEditorInput.modeId; - const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, encoding, mode, forceFile: true }) as FileEditorInput; + const fileEditorInput = accessor.get(IEditorService).createEditorInput({ resource, label: name, description, encoding, mode, forceFile: true }) as FileEditorInput; if (preferredResource) { fileEditorInput.setPreferredResource(preferredResource); } @@ -393,6 +403,16 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize({ key: 'openEditorsVisible', comment: ['Open is an adjective'] }, "Number of editors shown in the Open Editors pane. Setting this to 0 hides the Open Editors pane."), 'default': 9 }, + 'explorer.openEditors.sortOrder': { + 'type': 'string', + 'enum': ['editorOrder', 'alphabetical'], + 'description': nls.localize({ key: 'openEditorsSortOrder', comment: ['Open is an adjective'] }, "Controls the sorting order of editors in the Open Editors pane."), + 'enumDescriptions': [ + nls.localize('sortOrder.editorOrder', 'Editors are ordered in the same order editor tabs are shown.'), + nls.localize('sortOrder.alphabetical', 'Editors are ordered in alphabetical order inside each editor group.') + ], + 'default': 'editorOrder' + }, 'explorer.autoReveal': { 'type': ['boolean', 'string'], 'enum': [true, false, 'focusNoScroll'], @@ -406,7 +426,7 @@ configurationRegistry.registerConfiguration({ }, 'explorer.enableDragAndDrop': { 'type': 'boolean', - 'description': nls.localize('enableDragAndDrop', "Controls whether the explorer should allow to move files and folders via drag and drop."), + 'description': nls.localize('enableDragAndDrop', "Controls whether the explorer should allow to move files and folders via drag and drop. This setting only effects drag and drop from inside the explorer."), 'default': true }, 'explorer.confirmDragAndDrop': { @@ -468,3 +488,25 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { }, order: 1 }); + +UndoCommand.addImplementation(110, (accessor: ServicesAccessor) => { + const undoRedoService = accessor.get(IUndoRedoService); + const explorerService = accessor.get(IExplorerService); + if (explorerService.hasViewFocus()) { + undoRedoService.undo(UNDO_REDO_SOURCE); + return true; + } + + return false; +}); + +RedoCommand.addImplementation(110, (accessor: ServicesAccessor) => { + const undoRedoService = accessor.get(IUndoRedoService); + const explorerService = accessor.get(IExplorerService); + if (explorerService.hasViewFocus()) { + undoRedoService.redo(UNDO_REDO_SOURCE); + return true; + } + + return false; +}); diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index ff2019d0..95e137cb 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { IListService } from 'vs/platform/list/browser/listService'; -import { OpenEditor, IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { OpenEditor, SortOrder } from 'vs/workbench/contrib/files/common/files'; import { EditorResourceAccessor, SideBySideEditor, IEditorIdentifier } from 'vs/workbench/common/editor'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -13,6 +13,51 @@ import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditableData } from 'vs/workbench/common/views'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; + + +export interface IExplorerService { + readonly _serviceBrand: undefined; + readonly roots: ExplorerItem[]; + readonly sortOrder: SortOrder; + + getContext(respectMultiSelection: boolean): ExplorerItem[]; + hasViewFocus(): boolean; + setEditable(stat: ExplorerItem, data: IEditableData | null): Promise; + getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined; + getEditableData(stat: ExplorerItem): IEditableData | undefined; + // If undefined is passed checks if any element is currently being edited. + isEditable(stat: ExplorerItem | undefined): boolean; + findClosest(resource: URI): ExplorerItem | null; + refresh(): Promise; + setToCopy(stats: ExplorerItem[], cut: boolean): Promise; + isCut(stat: ExplorerItem): boolean; + applyBulkEdit(edit: ResourceFileEdit[], options: { undoLabel: string, progressLabel: string }): Promise; + + /** + * Selects and reveal the file element provided by the given resource if its found in the explorer. + * Will try to resolve the path in case the explorer is not yet expanded to the file yet. + */ + select(resource: URI, reveal?: boolean | string): Promise; + + registerView(contextAndRefreshProvider: IExplorerView): void; +} + +export const IExplorerService = createDecorator('explorerService'); + +export interface IExplorerView { + getContext(respectMultiSelection: boolean): ExplorerItem[]; + refresh(recursive: boolean, item?: ExplorerItem): Promise; + selectResource(resource: URI | undefined, reveal?: boolean | string): Promise; + setTreeInput(): Promise; + itemsCopied(tats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void; + setEditable(stat: ExplorerItem, isEditing: boolean): Promise; + focusNeighbourIfItemFocused(item: ExplorerItem): void; + isItemVisible(item: ExplorerItem): boolean; + hasFocus(): boolean; +} function getFocus(listService: IListService): unknown | undefined { let list = listService.lastFocusedList; @@ -36,7 +81,7 @@ function getFocus(listService: IListService): unknown | undefined { return undefined; } -// Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding +// Commands can get executed from a command palette, from a context menu or from some list using a keybinding // To cover all these cases we need to properly compute the resource on which the command is being executed export function getResourceForCommand(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): URI | undefined { if (URI.isUri(resource)) { diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 7257c3e6..8d645cca 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -76,7 +76,6 @@ } .explorer-viewlet .explorer-item .monaco-icon-name-container.multiple > .label-name > .monaco-highlighted-label { - padding: 1px; border-radius: 3px; } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts b/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts index d3a6797c..8816edd6 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts @@ -10,9 +10,9 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { listInvalidItemForeground, listDeemphasizedForeground } from 'vs/platform/theme/common/colorRegistry'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { explorerRootErrorEmitter } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; export function provideDecorations(fileStat: ExplorerItem): IDecorationData | undefined { if (fileStat.isRoot && fileStat.isError) { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 3db50a8a..a9309b46 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -8,9 +8,8 @@ import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; -import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, ExplorerResourceAvailableEditorIdsContext } from 'vs/workbench/contrib/files/common/files'; import { NewFolderAction, NewFileAction, FileCopiedContext, RefreshExplorerView, CollapseExplorerView } from 'vs/workbench/contrib/files/browser/fileActions'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import * as DOM from 'vs/base/browser/dom'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ExplorerDecorationsProvider } from 'vs/workbench/contrib/files/browser/views/explorerDecorationsProvider'; @@ -31,13 +30,13 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { ExplorerDelegate, ExplorerDataSource, FilesRenderer, ICompressedNavigationController, FilesFilter, FileSorter, FileDragAndDrop, ExplorerCompressionDelegate, isCompressedFolderName } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { ITreeContextMenuEvent, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { ResourceLabels } from 'vs/workbench/browser/labels'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -51,6 +50,8 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; interface IExplorerViewColors extends IColorMapping { listDropBackground?: ColorValue | undefined; @@ -134,6 +135,7 @@ export class ExplorerView extends ViewPane { private styleElement!: HTMLStyleElement; private treeContainer!: HTMLElement; + private container!: HTMLElement; private compressedFocusContext: IContextKey; private compressedFocusFirstContext: IContextKey; private compressedFocusLastContext: IContextKey; @@ -245,6 +247,7 @@ export class ExplorerView extends ViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); + this.container = container; this.treeContainer = DOM.append(container, DOM.$('.explorer-folders-view')); this.styleElement = DOM.createStyleSheet(this.treeContainer); @@ -303,10 +306,18 @@ export class ExplorerView extends ViewPane { } } + hasFocus(): boolean { + return DOM.isAncestor(document.activeElement, this.container); + } + getContext(respectMultiSelection: boolean): ExplorerItem[] { return getContext(this.tree.getFocus(), this.tree.getSelection(), respectMultiSelection, this.renderer); } + isItemVisible(item: ExplorerItem): boolean { + return this.filter.filter(item, TreeVisibility.Visible); + } + async setEditable(stat: ExplorerItem, isEditing: boolean): Promise { if (isEditing) { this.horizontalScrolling = this.tree.options.horizontalScrolling; @@ -337,7 +348,8 @@ export class ExplorerView extends ViewPane { private selectActiveFile(reveal = this.autoReveal): void { if (this.autoReveal) { - const activeFile = this.getActiveFile(); + const activeFile = EditorResourceAccessor.getCanonicalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); + if (activeFile) { const focus = this.tree.getFocus(); const selection = this.tree.getSelection(); @@ -458,7 +470,7 @@ export class ExplorerView extends ViewPane { // save view state this._register(this.storageService.onWillSaveState(() => { - this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE); + this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE, StorageTarget.MACHINE); })); } @@ -679,18 +691,6 @@ export class ExplorerView extends ViewPane { } } - private getActiveFile(): URI | undefined { - const input = this.editorService.activeEditor; - - // ignore diff editor inputs (helps to get out of diffing when returning to explorer) - if (input instanceof DiffEditorInput) { - return undefined; - } - - // check for files - return input?.resource; - } - public async selectResource(resource: URI | undefined, reveal = this.autoReveal, retry = 0): Promise { // do no retry more than once to prevent inifinite loops in cases of inconsistent model if (retry === 2) { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 9377aea4..9e8330b3 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -9,17 +9,17 @@ import * as glob from 'vs/base/common/glob'; import { IListVirtualDelegate, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IProgressService, ProgressLocation, IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IFileService, FileKind, FileOperationError, FileOperationResult, FileSystemProviderCapabilities, BinarySize } from 'vs/platform/files/common/files'; +import { IFileService, FileKind, FileOperationError, FileOperationResult, FileSystemProviderCapabilities, ByteSize } from 'vs/platform/files/common/files'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, Disposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IFileLabelOptions, IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; -import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource, ITreeSorter, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeFilter, TreeVisibility, IAsyncDataSource, ITreeSorter, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IFilesConfiguration, IExplorerService, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; import { dirname, joinPath, basename, distinctParents } from 'vs/base/common/resources'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { localize } from 'vs/nls'; @@ -37,11 +37,10 @@ import { Schemas } from 'vs/base/common/network'; import { NativeDragAndDropData, ExternalElementsDragAndDropData, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { IDialogService, IConfirmation, getFileNamesMessage } from 'vs/platform/dialogs/common/dialogs'; -import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { URI } from 'vs/base/common/uri'; -import { ITask, RunOnceWorker, sequence } from 'vs/base/common/async'; +import { RunOnceWorker } from 'vs/base/common/async'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { findValidPasteFileTarget } from 'vs/workbench/contrib/files/browser/fileActions'; @@ -58,6 +57,8 @@ import { IEditableData } from 'vs/workbench/common/views'; import { IEditorInput } from 'vs/workbench/common/editor'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -532,7 +533,7 @@ interface CachedParsedExpression { */ export class FilesFilter implements ITreeFilter { private hiddenExpressionPerRoot: Map; - private hiddenUris = new Set(); + private uriVisibilityMap = new Map(); private editorsAffectingFilter = new Set(); private _onDidChange = new Emitter(); private toDispose: IDisposable[] = []; @@ -554,13 +555,15 @@ export class FilesFilter implements ITreeFilter { this.toDispose.push(this.editorService.onDidVisibleEditorsChange(() => { const editors = this.editorService.visibleEditors; let shouldFire = false; - this.hiddenUris.forEach(u => { - editors.forEach(e => { - if (e.resource && this.uriIdentityService.extUri.isEqualOrParent(e.resource, u)) { - // A filtered resource suddenly became visible since user opened an editor - shouldFire = true; - } - }); + this.uriVisibilityMap.forEach((visible, uri) => { + if (!visible) { + editors.forEach(e => { + if (e.resource && this.uriIdentityService.extUri.isEqualOrParent(e.resource, uri)) { + // A filtered resource suddenly became visible since user opened an editor + shouldFire = true; + } + }); + } }); this.editorsAffectingFilter.forEach(e => { @@ -571,7 +574,7 @@ export class FilesFilter implements ITreeFilter { }); if (shouldFire) { this.editorsAffectingFilter.clear(); - this.hiddenUris.clear(); + this.uriVisibilityMap.clear(); this._onDidChange.fire(); } })); @@ -600,19 +603,20 @@ export class FilesFilter implements ITreeFilter { if (shouldFire) { this.editorsAffectingFilter.clear(); - this.hiddenUris.clear(); + this.uriVisibilityMap.clear(); this._onDidChange.fire(); } } - filter(stat: ExplorerItem, parentVisibility: TreeVisibility): TreeFilterResult { - const isVisible = this.isVisible(stat, parentVisibility); - if (isVisible) { - this.hiddenUris.delete(stat.resource); - } else { - this.hiddenUris.add(stat.resource); + filter(stat: ExplorerItem, parentVisibility: TreeVisibility): boolean { + const cachedVisibility = this.uriVisibilityMap.get(stat.resource); + if (typeof cachedVisibility === 'boolean') { + return cachedVisibility; } + const isVisible = this.isVisible(stat, parentVisibility); + this.uriVisibilityMap.set(stat.resource, isVisible); + return isVisible; } @@ -806,7 +810,6 @@ export class FileDragAndDrop implements ITreeDragAndDrop { @IFileService private fileService: IFileService, @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private instantiationService: IInstantiationService, - @IWorkingCopyFileService private workingCopyFileService: IWorkingCopyFileService, @IHostService private hostService: IHostService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, @IProgressService private readonly progressService: IProgressService, @@ -868,7 +871,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Native DND if (isNative) { - if (!containsDragType(originalEvent, DataTransfers.FILES, CodeDataTransfers.FILES)) { + if (!containsDragType(originalEvent, DataTransfers.FILES, CodeDataTransfers.FILES, DataTransfers.RESOURCES)) { return false; } } @@ -972,14 +975,14 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // The only custom data transfer we set from the explorer is a file transfer // to be able to DND between multiple code file explorers across windows - const fileResources = items.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); + const fileResources = items.filter(s => s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); if (fileResources.length) { originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); } } } - drop(data: IDragAndDropData, target: ExplorerItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + async drop(data: IDragAndDropData, target: ExplorerItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): Promise { this.compressedDropTargetDisposable.dispose(); // Find compressed target @@ -1010,26 +1013,29 @@ export class FileDragAndDrop implements ITreeDragAndDrop { if (data instanceof NativeDragAndDropData) { const cts = new CancellationTokenSource(); - // Indicate progress globally - const dropPromise = this.progressService.withProgress({ - location: ProgressLocation.Window, - delay: 800, - cancellable: true, - title: isWeb ? localize('uploadingFiles', "Uploading") : localize('copyingFiles', "Copying") - }, async progress => { - try { - if (isWeb) { - await this.handleWebExternalDrop(data, resolvedTarget, originalEvent, progress, cts.token); - } else { - await this.handleExternalDrop(data, resolvedTarget, originalEvent, progress, cts.token); + if (isWeb) { + // Indicate progress globally + const dropPromise = this.progressService.withProgress({ + location: ProgressLocation.Window, + delay: 800, + cancellable: true, + title: localize('uploadingFiles', "Uploading") + }, async progress => { + try { + await this.handleWebExternalDrop(resolvedTarget, originalEvent, progress, cts.token); + } catch (error) { + this.notificationService.warn(error); } + }, () => cts.dispose(true)); + // Also indicate progress in the files view + this.progressService.withProgress({ location: VIEW_ID, delay: 500 }, () => dropPromise); + } else { + try { + await this.handleExternalDrop(resolvedTarget, originalEvent, cts.token); } catch (error) { this.notificationService.warn(error); } - }, () => cts.dispose(true)); - - // Also indicate progress in the files view - this.progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => dropPromise); + } } // In-Explorer DND (Move/Copy file) else { @@ -1037,7 +1043,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } } - private async handleWebExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { + private async handleWebExternalDrop(target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { const items = (originalEvent.dataTransfer as unknown as IWebkitDataTransfer).items; // Somehow the items thing is being modified at random, maybe as a security @@ -1071,7 +1077,10 @@ export class FileDragAndDrop implements ITreeDragAndDrop { continue; } - await this.workingCopyFileService.delete([joinPath(target.resource, entry.name)], { recursive: true }); + await this.explorerService.applyBulkEdit([new ResourceFileEdit(joinPath(target.resource, entry.name), undefined, { recursive: true })], { + undoLabel: localize('overwrite', "Overwrite {0}", entry.name), + progressLabel: localize('overwriting', "Overwriting {0}", entry.name), + }); if (token.isCancellationRequested) { break; @@ -1109,17 +1118,17 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Small file let message: string; - if (fileSize < BinarySize.MB) { + if (fileSize < ByteSize.MB) { if (operation.filesTotal === 1) { message = `${entry.name}`; } else { - message = localize('uploadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesUploaded, operation.filesTotal, BinarySize.formatSize(bytesUploadedPerSecond)); + message = localize('uploadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesUploaded, operation.filesTotal, ByteSize.formatSize(bytesUploadedPerSecond)); } } // Large file else { - message = localize('uploadProgressLarge', "{0} ({1} of {2}, {3}/s)", entry.name, BinarySize.formatSize(fileBytesUploaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesUploadedPerSecond)); + message = localize('uploadProgressLarge', "{0} ({1} of {2}, {3}/s)", entry.name, ByteSize.formatSize(fileBytesUploaded), ByteSize.formatSize(fileSize), ByteSize.formatSize(bytesUploadedPerSecond)); } // Report progress but limit to update only once per second @@ -1137,12 +1146,13 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return undefined; } - // Chrome/Edge/Firefox support stream method - if (typeof file.stream === 'function') { + // Chrome/Edge/Firefox support stream method, but only use it for + // larger files to reduce the overhead of the streaming approach + if (typeof file.stream === 'function' && file.size > ByteSize.MB) { await this.doUploadWebFileEntryBuffered(resource, file, reportProgress, token); } - // Fallback to unbuffered upload for other browsers + // Fallback to unbuffered upload for other browsers or small files else { await this.doUploadWebFileEntryUnbuffered(resource, file, reportProgress); } @@ -1258,7 +1268,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - private async handleExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { + private async handleExternalDrop(target: ExplorerItem, originalEvent: DragEvent, token: CancellationToken): Promise { // Check for dropped external files to be folders const droppedResources = extractResources(originalEvent, true); @@ -1271,9 +1281,9 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Pass focus to window this.hostService.focus(); - // Handle folders by adding to workspace if we are in workspace context + // Handle folders by adding to workspace if we are in workspace context and if dropped on top const folders = result.filter(r => r.success && r.stat && r.stat.isDirectory).map(result => ({ uri: result.stat!.resource })); - if (folders.length > 0) { + if (folders.length > 0 && target.isRoot) { const buttons = [ folders.length > 1 ? localize('copyFolders', "&&Copy Folders") : localize('copyFolder', "&&Copy Folder"), localize('cancel', "Cancel") @@ -1292,7 +1302,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return this.workspaceEditingService.addFolders(folders); } if (choice === buttons.length - 2) { - return this.addResources(target, droppedResources.map(res => res.resource), progress, token); + return this.addResources(target, droppedResources.map(res => res.resource), token); } return undefined; @@ -1300,11 +1310,11 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Handle dropped files (only support FileStat as target) else if (target instanceof ExplorerItem) { - return this.addResources(target, droppedResources.map(res => res.resource), progress, token); + return this.addResources(target, droppedResources.map(res => res.resource), token); } } - private async addResources(target: ExplorerItem, resources: URI[], progress: IProgress, token: CancellationToken): Promise { + private async addResources(target: ExplorerItem, resources: URI[], token: CancellationToken): Promise { if (resources && resources.length > 0) { // Resolve target to check for name collisions and ask user @@ -1323,36 +1333,33 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - // Run add in sequence - const addPromisesFactory: ITask>[] = []; - await Promise.all(resources.map(async resource => { + const resourcesFiltered = (await Promise.all(resources.map(async resource => { if (targetNames.has(caseSensitive ? basename(resource) : basename(resource).toLowerCase())) { const confirmationResult = await this.dialogService.confirm(getFileOverwriteConfirm(basename(resource))); if (!confirmationResult.confirmed) { - return; + return undefined; } } + return resource; + }))).filter(r => r instanceof URI) as URI[]; + const resourceFileEdits = resourcesFiltered.map(resource => { + const sourceFileName = basename(resource); + const targetFile = joinPath(target.resource, sourceFileName); + return new ResourceFileEdit(resource, targetFile, { overwrite: true, copy: true }); + }); - addPromisesFactory.push(async () => { - if (token.isCancellationRequested) { - return; - } + await this.explorerService.applyBulkEdit(resourceFileEdits, { + undoLabel: resourcesFiltered.length === 1 ? localize('copyFile', "Copy {0}", basename(resourcesFiltered[0])) : localize('copynFile', "Copy {0} resources", resourcesFiltered.length), + progressLabel: resourcesFiltered.length === 1 ? localize('copyingFile', "Copying {0}", basename(resourcesFiltered[0])) : localize('copyingnFile', "Copying {0} resources", resourcesFiltered.length) + }); - const sourceFile = resource; - const sourceFileName = basename(sourceFile); - const targetFile = joinPath(target.resource, sourceFileName); - - progress.report({ message: sourceFileName }); - - const stat = (await this.workingCopyFileService.copy([{ source: sourceFile, target: targetFile }], { overwrite: true }))[0]; - // if we only add one file, just open it directly - if (resources.length === 1 && !stat.isDirectory) { - this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); - } - }); - })); - - await sequence(addPromisesFactory); + // if we only add one file, just open it directly + if (resourceFileEdits.length === 1) { + const item = this.explorerService.findClosest(resourceFileEdits[0].newResource!); + if (item && !item.isDirectory) { + this.editorService.openEditor({ resource: item.resource, options: { pinned: true } }); + } + } } } @@ -1386,7 +1393,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Check for confirmation checkbox if (confirmation.checkboxChecked === true) { - await this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false, ConfigurationTarget.USER); + await this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false); } } @@ -1436,9 +1443,17 @@ export class FileDragAndDrop implements ITreeDragAndDrop { private async doHandleExplorerDropOnCopy(sources: ExplorerItem[], target: ExplorerItem): Promise { // Reuse duplicate action when user copies const incrementalNaming = this.configurationService.getValue().explorer.incrementalNaming; - const sourceTargetPairs = sources.map(({ resource, isDirectory }) => ({ source: resource, target: findValidPasteFileTarget(this.explorerService, target, { resource, isDirectory, allowOverwrite: false }, incrementalNaming) })); - const stats = await this.workingCopyFileService.copy(sourceTargetPairs); - const editors = stats.filter(stat => !stat.isDirectory).map(({ resource }) => ({ resource, options: { pinned: true } })); + const resourceFileEdits = sources.map(({ resource, isDirectory }) => (new ResourceFileEdit(resource, findValidPasteFileTarget(this.explorerService, target, { resource, isDirectory, allowOverwrite: false }, incrementalNaming), { copy: true }))); + const labelSufix = getFileOrFolderLabelSufix(sources); + await this.explorerService.applyBulkEdit(resourceFileEdits, { + undoLabel: localize('copy', "Copy {0}", labelSufix), + progressLabel: localize('copying', "Copying {0}", labelSufix), + }); + + const editors = resourceFileEdits.filter(edit => { + const item = edit.newResource ? this.explorerService.findClosest(edit.newResource) : undefined; + return item && !item.isDirectory; + }).map(edit => ({ resource: edit.newResource, options: { pinned: true } })); await this.editorService.openEditors(editors); } @@ -1446,18 +1461,23 @@ export class FileDragAndDrop implements ITreeDragAndDrop { private async doHandleExplorerDropOnMove(sources: ExplorerItem[], target: ExplorerItem): Promise { // Do not allow moving readonly items - const sourceTargetPairs = sources.filter(source => !source.isReadonly).map(source => ({ source: source.resource, target: joinPath(target.resource, source.name) })); + const resourceFileEdits = sources.filter(source => !source.isReadonly).map(source => new ResourceFileEdit(source.resource, joinPath(target.resource, source.name))); + const labelSufix = getFileOrFolderLabelSufix(sources); + const options = { + undoLabel: localize('move', "Move {0}", labelSufix), + progressLabel: localize('moving', "Moving {0}", labelSufix) + }; try { - await this.workingCopyFileService.move(sourceTargetPairs); + await this.explorerService.applyBulkEdit(resourceFileEdits, options); } catch (error) { // Conflict if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { const overwrites: URI[] = []; - for (const { target } of sourceTargetPairs) { - if (await this.fileService.exists(target)) { - overwrites.push(target); + for (const edit of resourceFileEdits) { + if (edit.newResource && await this.fileService.exists(edit.newResource)) { + overwrites.push(edit.newResource); } } @@ -1466,7 +1486,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const { confirmed } = await this.dialogService.confirm(confirm); if (confirmed) { try { - await this.workingCopyFileService.move(sourceTargetPairs, { overwrite: true }); + await this.explorerService.applyBulkEdit(resourceFileEdits.map(re => new ResourceFileEdit(re.oldResource, re.newResource, { overwrite: true })), options); } catch (error) { this.notificationService.error(error); } @@ -1550,3 +1570,18 @@ export class ExplorerCompressionDelegate implements ITreeCompressionDelegate i.isDirectory)) { + return `${items.length} folders`; + } + if (items.every(i => !i.isDirectory)) { + return `${items.length} files`; + } + + return `${items.length} files and folders`; +} diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 5c1e9e59..3535108c 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -48,6 +48,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { compareFileNamesDefault } from 'vs/base/common/comparers'; const $ = dom.$; @@ -64,6 +65,8 @@ export class OpenEditorsView extends ViewPane { private listLabels: ResourceLabels | undefined; private contributedContextMenu!: IMenu; private needsRefresh = false; + private elements: (OpenEditor | IEditorGroup)[] = []; + private sortOrder: 'editorOrder' | 'alphabetical'; private resourceContext!: ResourceContextKey; private groupFocusedContext!: IContextKey; private dirtyEditorFocusedContext!: IContextKey; @@ -91,13 +94,14 @@ export class OpenEditorsView extends ViewPane { this.structuralRefreshDelay = 0; this.listRefreshScheduler = new RunOnceScheduler(() => { const previousLength = this.list.length; - this.list.splice(0, this.list.length, this.elements); + this.list.splice(0, this.list.length, this.getElements()); this.focusActiveEditor(); if (previousLength !== this.list.length) { this.updateSize(); } this.needsRefresh = false; }, this.structuralRefreshDelay); + this.sortOrder = configurationService.getValue('explorer.openEditors.sortOrder'); this.registerUpdateEvents(); @@ -132,7 +136,7 @@ export class OpenEditorsView extends ViewPane { const index = this.getIndex(group, e.editor); switch (e.kind) { case GroupChangeKind.GROUP_INDEX: { - if (this.showGroups) { + if (index >= 0) { this.list.splice(index, 1, [group]); } break; @@ -149,19 +153,10 @@ export class OpenEditorsView extends ViewPane { this.list.splice(index, 1, [new OpenEditor(e.editor!, group)]); break; } - case GroupChangeKind.EDITOR_OPEN: { - this.list.splice(index, 0, [new OpenEditor(e.editor!, group)]); - setTimeout(() => this.updateSize(), this.structuralRefreshDelay); - break; - } - case GroupChangeKind.EDITOR_CLOSE: { - const previousIndex = this.getIndex(group, undefined) + (e.editorIndex || 0) + (this.showGroups ? 1 : 0); - this.list.splice(previousIndex, 1); - this.updateSize(); - break; - } + case GroupChangeKind.EDITOR_OPEN: + case GroupChangeKind.EDITOR_CLOSE: case GroupChangeKind.EDITOR_MOVE: { - this.listRefreshScheduler.schedule(); + updateWholeList(); break; } } @@ -272,20 +267,16 @@ export class OpenEditorsView extends ViewPane { })); const resourceNavigator = this._register(new ListResourceNavigator(this.list, { configurationService: this.configurationService })); this._register(resourceNavigator.onDidOpen(e => { - if (typeof e.element !== 'number') { + if (!e.element) { return; - } - - const element = this.list.element(e.element); - - if (element instanceof OpenEditor) { + } else if (e.element instanceof OpenEditor) { if (e.browserEvent instanceof MouseEvent && e.browserEvent.button === 1) { return; // middle click already handled above: closes the editor } - this.openEditor(element, { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned, sideBySide: e.sideBySide }); + this.openEditor(e.element, { preserveFocus: e.editorOptions.preserveFocus, pinned: e.editorOptions.pinned, sideBySide: e.sideBySide }); } else { - this.editorGroupService.activateGroup(element); + this.editorGroupService.activateGroup(e.element); } })); @@ -331,33 +322,28 @@ export class OpenEditorsView extends ViewPane { return this.editorGroupService.groups.length > 1; } - private get elements(): Array { - const result: Array = []; + private getElements(): Array { + this.elements = []; this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(g => { if (this.showGroups) { - result.push(g); + this.elements.push(g); } - result.push(...g.editors.map(ei => new OpenEditor(ei, g))); + let editors = g.editors.map(ei => new OpenEditor(ei, g)); + if (this.sortOrder === 'alphabetical') { + editors = editors.sort((first, second) => compareFileNamesDefault(first.editor.getName(), second.editor.getName())); + } + this.elements.push(...editors); }); - return result; + return this.elements; } private getIndex(group: IEditorGroup, editor: IEditorInput | undefined | null): number { - let index = editor ? group.getIndexOfEditor(editor) : 0; - if (!this.showGroups) { - return index; + if (!editor) { + return this.elements.findIndex(e => !(e instanceof OpenEditor) && e.id === group.id); } - for (let g of this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)) { - if (g.id === group.id) { - return index + (!!editor ? 1 : 0); - } else { - index += g.count + 1; - } - } - - return -1; + return this.elements.findIndex(e => e instanceof OpenEditor && e.editor === editor && e.group.id === group.id); } private openEditor(element: OpenEditor, options: { preserveFocus?: boolean; pinned?: boolean; sideBySide?: boolean; }): void { @@ -384,7 +370,7 @@ export class OpenEditorsView extends ViewPane { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions, - getActionsContext: () => element instanceof OpenEditor ? { groupId: element.groupId, editorIndex: element.editorIndex } : { groupId: element.id }, + getActionsContext: () => element instanceof OpenEditor ? { groupId: element.groupId, editorIndex: element.group.getIndexOfEditor(element.editor) } : { groupId: element.id }, onHide: () => dispose(actionsDisposable) }); } @@ -393,9 +379,13 @@ export class OpenEditorsView extends ViewPane { if (this.list.length && this.editorGroupService.activeGroup) { const index = this.getIndex(this.editorGroupService.activeGroup, this.editorGroupService.activeGroup.activeEditor); if (index >= 0) { - this.list.setFocus([index]); - this.list.setSelection([index]); - this.list.reveal(index); + try { + this.list.setFocus([index]); + this.list.setSelection([index]); + this.list.reveal(index); + } catch (e) { + // noop list updated in the meantime + } return; } } @@ -408,9 +398,9 @@ export class OpenEditorsView extends ViewPane { if (event.affectsConfiguration('explorer.openEditors')) { this.updateSize(); } - - // Trigger a 'repaint' when decoration settings change - if (event.affectsConfiguration('explorer.decorations')) { + // Trigger a 'repaint' when decoration settings change or the sort order changed + if (event.affectsConfiguration('explorer.decorations') || event.affectsConfiguration('explorer.openEditors.sortOrder')) { + this.sortOrder = this.configurationService.getValue('explorer.openEditors.sortOrder'); this.listRefreshScheduler.schedule(); } } @@ -500,7 +490,7 @@ class OpenEditorActionRunner extends ActionRunner { return; } - return super.run(action, { groupId: this.editor.groupId, editorIndex: this.editor.editorIndex }); + return super.run(action, { groupId: this.editor.groupId, editorIndex: this.editor.group.getIndexOfEditor(this.editor.editor) }); } } @@ -687,7 +677,7 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop; - getEditable(): { stat: ExplorerItem, data: IEditableData } | undefined; - getEditableData(stat: ExplorerItem): IEditableData | undefined; - // If undefined is passed checks if any element is currently being edited. - isEditable(stat: ExplorerItem | undefined): boolean; - findClosest(resource: URI): ExplorerItem | null; - refresh(): Promise; - setToCopy(stats: ExplorerItem[], cut: boolean): Promise; - isCut(stat: ExplorerItem): boolean; - - /** - * Selects and reveal the file element provided by the given resource if its found in the explorer. - * Will try to resolve the path in case the explorer is not yet expanded to the file yet. - */ - select(resource: URI, reveal?: boolean | string): Promise; - - registerView(contextAndRefreshProvider: IExplorerView): void; -} - -export interface IExplorerView { - getContext(respectMultiSelection: boolean): ExplorerItem[]; - refresh(recursive: boolean, item?: ExplorerItem): Promise; - selectResource(resource: URI | undefined, reveal?: boolean | string): Promise; - setTreeInput(): Promise; - itemsCopied(tats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void; - setEditable(stat: ExplorerItem, isEditing: boolean): Promise; - focusNeighbourIfItemFocused(item: ExplorerItem): void; -} - -export const IExplorerService = createDecorator('explorerService'); - /** * Context Keys to use with keybindings for the Explorer and Open Editors view */ @@ -116,6 +76,7 @@ export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkb explorer: { openEditors: { visible: number; + sortOrder: 'editorOrder' | 'alphabetical'; }; autoReveal: boolean | 'focusNoScroll'; enableDragAndDrop: boolean; @@ -230,18 +191,17 @@ export class TextFileContentProvider extends Disposable implements ITextModelCon export class OpenEditor implements IEditorIdentifier { + private id: number; + private static COUNTER = 0; + constructor(private _editor: IEditorInput, private _group: IEditorGroup) { - // noop + this.id = OpenEditor.COUNTER++; } get editor() { return this._editor; } - get editorIndex() { - return this._group.getIndexOfEditor(this.editor); - } - get group() { return this._group; } @@ -251,7 +211,7 @@ export class OpenEditor implements IEditorIdentifier { } getId(): string { - return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`; + return `openeditor:${this.groupId}:${this.id}`; } isPreview(): boolean { diff --git a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts index 086d9c9d..72211dd0 100644 --- a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts @@ -14,18 +14,19 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; +import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { IListService } from 'vs/platform/list/browser/listService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { revealResourcesInOS } from 'vs/workbench/contrib/files/electron-sandbox/fileCommands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { appendToCommandPalette, appendEditorTitleContextMenuItem } from 'vs/workbench/contrib/files/browser/fileActions.contribution'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { SideBySideEditor, EditorResourceAccessor } from 'vs/workbench/common/editor'; +import { ContextKeyOrExpr } from 'vs/platform/contextkey/common/contextkey'; const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS'; const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"); +const REVEAL_IN_OS_WHEN_CONTEXT = ContextKeyOrExpr.create([ResourceContextKey.Scheme.isEqualTo(Schemas.file), ResourceContextKey.Scheme.isEqualTo(Schemas.userData)]); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: REVEAL_IN_OS_COMMAND_ID, @@ -57,19 +58,19 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); +appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, REVEAL_IN_OS_WHEN_CONTEXT); // Menu registration - open editors const revealInOsCommand = { id: REVEAL_IN_OS_COMMAND_ID, - title: isWindows ? nls.localize('revealInWindows', "Reveal in File Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder") + title: REVEAL_IN_OS_LABEL }; MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', order: 20, command: revealInOsCommand, - when: ResourceContextKey.IsFileSystemResource + when: REVEAL_IN_OS_WHEN_CONTEXT }); // Menu registration - explorer @@ -78,10 +79,10 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: 'navigation', order: 20, command: revealInOsCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) + when: REVEAL_IN_OS_WHEN_CONTEXT }); // Command Palette const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; -appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in File Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); +appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in File Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category, REVEAL_IN_OS_WHEN_CONTEXT); diff --git a/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts b/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts index c6655f26..d310e987 100644 --- a/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/textFileEditor.ts @@ -21,9 +21,9 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; /** * An implementation of editor for file system resources. diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index 8e0e190b..0df2ed5d 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -24,12 +24,16 @@ suite('Files - FileEditorInput', () => { let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; + function createFileInput(resource: URI, preferredResource?: URI, preferredMode?: string, preferredName?: string, preferredDescription?: string): FileEditorInput { + return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, undefined, preferredMode); + } + setup(() => { instantiationService = workbenchInstantiationService({ editorService: () => { return new class extends TestEditorService { createEditorInput(input: IResourceEditorInput) { - return instantiationService.createInstance(FileEditorInput, input.resource, undefined, undefined, undefined); + return createFileInput(input.resource); } }; } @@ -39,9 +43,9 @@ suite('Files - FileEditorInput', () => { }); test('Basics', async function () { - let input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); - const otherInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/otherfile.js'), undefined, undefined, undefined); - const otherInputSame = instantiationService.createInstance(FileEditorInput, toResource.call(this, 'foo/bar/file.js'), undefined, undefined, undefined); + let input = createFileInput(toResource.call(this, '/foo/bar/file.js')); + const otherInput = createFileInput(toResource.call(this, 'foo/bar/otherfile.js')); + const otherInputSame = createFileInput(toResource.call(this, 'foo/bar/file.js')); assert(input.matches(input)); assert(input.matches(otherInputSame)); @@ -56,10 +60,10 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(toResource.call(this, '/foo/bar/file.js').fsPath, input.resource.fsPath); assert(input.resource instanceof URI); - input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar.html'), undefined, undefined, undefined); + input = createFileInput(toResource.call(this, '/foo/bar.html')); - const inputToResolve: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); - const sameOtherInput: FileEditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); + const inputToResolve: FileEditorInput = createFileInput(toResource.call(this, '/foo/bar/file.js')); + const sameOtherInput: FileEditorInput = createFileInput(toResource.call(this, '/foo/bar/file.js')); let resolved = await inputToResolve.resolve(); assert.ok(inputToResolve.isResolved()); @@ -97,11 +101,11 @@ suite('Files - FileEditorInput', () => { const resource = toResource.call(this, '/foo/bar/updatefile.js'); const preferredResource = toResource.call(this, '/foo/bar/UPDATEFILE.js'); - const inputWithoutPreferredResource = instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined); + const inputWithoutPreferredResource = createFileInput(resource); assert.equal(inputWithoutPreferredResource.resource.toString(), resource.toString()); assert.equal(inputWithoutPreferredResource.preferredResource.toString(), resource.toString()); - const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, resource, preferredResource, undefined, undefined); + const inputWithPreferredResource = createFileInput(resource, preferredResource); assert.equal(inputWithPreferredResource.resource.toString(), resource.toString()); assert.equal(inputWithPreferredResource.preferredResource.toString(), preferredResource.toString()); @@ -130,7 +134,7 @@ suite('Files - FileEditorInput', () => { id: mode, }); - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, mode); + const input = createFileInput(toResource.call(this, '/foo/bar/file.js'), undefined, mode); assert.equal(input.getPreferredMode(), mode); const model = await input.resolve() as TextFileEditorModel; @@ -140,7 +144,7 @@ suite('Files - FileEditorInput', () => { assert.equal(input.getPreferredMode(), 'text'); assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID); - const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined); + const input2 = createFileInput(toResource.call(this, '/foo/bar/file.js')); input2.setPreferredMode(mode); const model2 = await input2.resolve() as TextFileEditorModel; @@ -148,10 +152,10 @@ suite('Files - FileEditorInput', () => { }); test('matches', function () { - const input1 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); - const input2 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); - const input3 = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/other.js'), undefined, undefined, undefined); - const input2Upper = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/UPDATEFILE.js'), undefined, undefined, undefined); + const input1 = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); + const input2 = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); + const input3 = createFileInput(toResource.call(this, '/foo/bar/other.js')); + const input2Upper = createFileInput(toResource.call(this, '/foo/bar/UPDATEFILE.js')); assert.strictEqual(input1.matches(null), false); assert.strictEqual(input1.matches(input1), true); @@ -162,7 +166,7 @@ suite('Files - FileEditorInput', () => { }); test('getEncoding/setEncoding', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); input.setEncoding('utf16', EncodingMode.Encode); assert.equal(input.getEncoding(), 'utf16'); @@ -173,7 +177,7 @@ suite('Files - FileEditorInput', () => { }); test('save', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); const resolved = await input.resolve() as TextFileEditorModel; resolved.textEditorModel!.setValue('changed'); @@ -185,7 +189,7 @@ suite('Files - FileEditorInput', () => { }); test('revert', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); const resolved = await input.resolve() as TextFileEditorModel; resolved.textEditorModel!.setValue('changed'); @@ -201,7 +205,7 @@ suite('Files - FileEditorInput', () => { }); test('resolve handles binary files', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); accessor.textFileService.setResolveTextContentErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY)); @@ -211,7 +215,7 @@ suite('Files - FileEditorInput', () => { }); test('resolve handles too large files', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_TOO_LARGE)); @@ -221,7 +225,7 @@ suite('Files - FileEditorInput', () => { }); test('attaches to model when created and reports dirty', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); let listenerCount = 0; const listener = input.onDidChangeDirty(() => { @@ -241,7 +245,7 @@ suite('Files - FileEditorInput', () => { }); test('force open text/binary', async function () { - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); input.setForceOpenAsBinary(); let resolved = await input.resolve(); @@ -258,7 +262,7 @@ suite('Files - FileEditorInput', () => { test('file editor input factory', async function () { instantiationService.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, undefined); + const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); const factory = Registry.as(EditorExtensions.EditorInputFactories).getEditorInputFactory(input.getTypeId()); if (!factory) { @@ -276,7 +280,7 @@ suite('Files - FileEditorInput', () => { assert.equal(input.matches(inputDeserialized), true); const preferredResource = toResource.call(this, '/foo/bar/UPDATEfile.js'); - const inputWithPreferredResource = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), preferredResource, undefined, undefined); + const inputWithPreferredResource = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'), preferredResource); const inputWithPreferredResourceSerialized = factory.serialize(inputWithPreferredResource); if (!inputWithPreferredResourceSerialized) { @@ -287,4 +291,49 @@ suite('Files - FileEditorInput', () => { assert.equal(inputWithPreferredResource.resource.toString(), inputWithPreferredResourceDeserialized.resource.toString()); assert.equal(inputWithPreferredResource.preferredResource.toString(), inputWithPreferredResourceDeserialized.preferredResource.toString()); }); + + test('preferred name/description', async function () { + + // Works with custom file input + const customFileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js').with({ scheme: 'test-custom' }), undefined, undefined, 'My Name', 'My Description'); + + let didChangeLabelCounter = 0; + customFileInput.onDidChangeLabel(() => { + didChangeLabelCounter++; + }); + + assert.equal(customFileInput.getName(), 'My Name'); + assert.equal(customFileInput.getDescription(), 'My Description'); + + customFileInput.setPreferredName('My Name 2'); + customFileInput.setPreferredDescription('My Description 2'); + + assert.equal(customFileInput.getName(), 'My Name 2'); + assert.equal(customFileInput.getDescription(), 'My Description 2'); + + assert.equal(didChangeLabelCounter, 2); + + customFileInput.dispose(); + + // Disallowed with local file input + const fileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, 'My Name', 'My Description'); + + didChangeLabelCounter = 0; + fileInput.onDidChangeLabel(() => { + didChangeLabelCounter++; + }); + + assert.notEqual(fileInput.getName(), 'My Name'); + assert.notEqual(fileInput.getDescription(), 'My Description'); + + fileInput.setPreferredName('My Name 2'); + fileInput.setPreferredDescription('My Description 2'); + + assert.notEqual(fileInput.getName(), 'My Name 2'); + assert.notEqual(fileInput.getDescription(), 'My Description 2'); + + assert.equal(didChangeLabelCounter, 0); + + fileInput.dispose(); + }); }); diff --git a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts index 9355b4e6..1efff633 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts @@ -32,7 +32,7 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { alias: 'Format Document', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider.toNegated()), + kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib, @@ -55,6 +55,8 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { return commandService.executeCommand('editor.action.formatDocument.multiple'); } else if (formatterCount === 1) { return commandService.executeCommand('editor.action.formatDocument'); + } else if (model.isTooLargeForSyncing()) { + notificationService.prompt(Severity.Info, nls.localize('too.large', "This file cannot be formatted because it is too large"), []); } else { const langName = model.getLanguageIdentifier().language; const message = nls.localize('no.provider', "There is no formatter for '{0}' files installed.", langName); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts index 88b20371..acd99472 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts @@ -10,7 +10,7 @@ import { SyncActionDescriptor, ICommandAction, MenuRegistry, MenuId } from 'vs/p import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions'; import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-sandbox/issueActions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { WorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issueService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IssueReporterData } from 'vs/platform/issue/common/issue'; diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts index 5285dbdb..44a39657 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueActions.ts @@ -6,7 +6,7 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IssueType } from 'vs/platform/issue/common/issue'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; export class OpenProcessExplorer extends Action { static readonly ID = 'workbench.action.openProcessExplorer'; diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts index ba04db21..f7beaf0f 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts @@ -11,11 +11,12 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getZoomLevel } from 'vs/base/browser/browser'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; export class WorkbenchIssueService implements IWorkbenchIssueService { declare readonly _serviceBrand: undefined; @@ -26,12 +27,13 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, - @IProductService private readonly productService: IProductService + @IProductService private readonly productService: IProductService, + @ITASExperimentService private readonly experimentService: ITASExperimentService ) { } async openReporter(dataOverrides: Partial = {}): Promise { const extensions = await this.extensionManagementService.getInstalled(); - const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension)); + const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension) || (dataOverrides.extensionId && extension.identifier.id === dataOverrides.extensionId)); const extensionData = enabledExtensions.map((extension): IssueReporterExtensionData => { const { manifest } = extension; const manifestKeys = manifest.contributes ? Object.keys(manifest.contributes) : []; @@ -49,11 +51,13 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { isBuiltin, }; }); + const experiments = await this.experimentService.getCurrentExperiments(); const theme = this.themeService.getColorTheme(); const issueReporterData: IssueReporterData = Object.assign({ styles: getIssueReporterStyles(theme), zoomLevel: getZoomLevel(), enabledExtensions: extensionData, + experiments: experiments?.join('\n') }, dataOverrides); return this.issueService.openReporter(issueReporterData); } diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 446ad446..f8b4c158 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -19,13 +19,12 @@ import Severity from 'vs/base/common/severity'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { minimumTranslatedStrings } from 'vs/workbench/contrib/localizations/browser/minimalTranslations'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; // Register action to configure locale and related settings const registry = Registry.as(Extensions.WorkbenchActions); @@ -44,12 +43,9 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IViewletService private readonly viewletService: IViewletService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); - storageKeysSyncRegistryService.registerStorageKey({ key: LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: 'langugage.update.donotask', version: 1 }); this.checkAndInstall(); this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e))); } @@ -175,7 +171,8 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo this.storageService.store( LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, JSON.stringify(languagePackSuggestionIgnoreList), - StorageScope.GLOBAL + StorageScope.GLOBAL, + StorageTarget.USER ); logUserReaction('neverShowAgain'); } diff --git a/src/vs/workbench/contrib/logs/common/logConstants.ts b/src/vs/workbench/contrib/logs/common/logConstants.ts index ca22b994..9ba3e7aa 100644 --- a/src/vs/workbench/contrib/logs/common/logConstants.ts +++ b/src/vs/workbench/contrib/logs/common/logConstants.ts @@ -9,3 +9,5 @@ export const rendererLogChannelId = 'rendererLog'; export const extHostLogChannelId = 'extHostLog'; export const telemetryLogChannelId = 'telemetryLog'; export const userDataSyncLogChannelId = 'userDataSyncLog'; + +export const showWindowLogActionId = 'workbench.action.showWindowLog'; diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 5d04d1b9..713e8198 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { join } from 'vs/base/common/path'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions, CATEGORIES } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { SetLogLevelAction, OpenWindowSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -20,8 +20,9 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { dirname } from 'vs/base/common/resources'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(SetLogLevelAction), 'Developer: Set Log Level...', CATEGORIES.Developer.value); @@ -54,6 +55,21 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { }; registerTelemetryChannel(this.logService.getLevel()); this.logService.onDidChangeLogLevel(registerTelemetryChannel); + + registerAction2(class ShowWindowLogAction extends Action2 { + constructor() { + super({ + id: Constants.showWindowLogActionId, + title: { value: nls.localize('show window log', "Show Window Log"), original: 'Show Window Log' }, + category: CATEGORIES.Developer, + f1: true + }); + } + async run(servicesAccessor: ServicesAccessor): Promise { + const outputService = servicesAccessor.get(IOutputService); + outputService.showChannel(Constants.rendererLogChannelId); + } + }); } private registerWebContributions(): void { diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index 625a8d78..f19eb8a8 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -94,7 +94,7 @@ export class OpenWindowSessionLogFileAction extends Action { placeHolder: nls.localize('log placeholder', "Select Log file") }); if (logFileResult) { - return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!) }).then(() => undefined); + return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!), options: { pinned: true } }).then(() => undefined); } } } diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index e0467322..208fea76 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -18,8 +18,7 @@ import { ShowProblemsPanelAction } from 'vs/workbench/contrib/markers/browser/ma import Constants from 'vs/workbench/contrib/markers/browser/constants'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IMarkersWorkbenchService, MarkersWorkbenchService, ActivityUpdater } from 'vs/workbench/contrib/markers/browser/markers'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ActivityUpdater } from 'vs/workbench/contrib/markers/browser/markers'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -33,8 +32,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; import { Codicon } from 'vs/base/common/codicons'; - -registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.MARKER_OPEN_ACTION_ID, @@ -124,11 +122,13 @@ class ToggleMarkersPanelAction extends ToggleViewAction { } } +const markersViewIcon = registerIcon('markers-view-icon', Codicon.warning, localize('markersViewIcon', 'View icon of the markers view.')); + // markers view container const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: Constants.MARKERS_CONTAINER_ID, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, - icon: Codicon.warning.classNames, + icon: markersViewIcon, hideIfEmpty: true, order: 0, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), @@ -142,7 +142,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewC Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: Constants.MARKERS_VIEW_ID, - containerIcon: Codicon.warning.classNames, + containerIcon: markersViewIcon, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, canToggleVisibility: false, canMoveView: true, diff --git a/src/vs/workbench/contrib/markers/browser/markers.ts b/src/vs/workbench/contrib/markers/browser/markers.ts index 383d52fc..19623546 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.ts @@ -3,55 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { MarkersModel, compareMarkersByUri } from './markersModel'; import { Disposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle'; -import { IMarkerService, MarkerSeverity, IMarker } from 'vs/platform/markers/common/markers'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { localize } from 'vs/nls'; import Constants from './constants'; -import { URI } from 'vs/base/common/uri'; -import { groupBy } from 'vs/base/common/arrays'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Event } from 'vs/base/common/event'; -import { ResourceMap } from 'vs/base/common/map'; - -export const IMarkersWorkbenchService = createDecorator('markersWorkbenchService'); - -export interface IMarkersWorkbenchService { - readonly _serviceBrand: undefined; - readonly markersModel: MarkersModel; -} - -export class MarkersWorkbenchService extends Disposable implements IMarkersWorkbenchService { - declare readonly _serviceBrand: undefined; - - readonly markersModel: MarkersModel; - - constructor( - @IMarkerService private readonly markerService: IMarkerService, - @IInstantiationService instantiationService: IInstantiationService, - ) { - super(); - this.markersModel = this._register(instantiationService.createInstance(MarkersModel)); - - this.markersModel.setResourceMarkers(groupBy(this.readMarkers(), compareMarkersByUri).map(group => [group[0].resource, group])); - this._register(Event.debounce>(markerService.onMarkerChanged, (resourcesMap, resources) => { - resourcesMap = resourcesMap ? resourcesMap : new ResourceMap(); - resources.forEach(resource => resourcesMap!.set(resource, resource)); - return resourcesMap; - }, 0)(resourcesMap => this.onMarkerChanged([...resourcesMap.values()]))); - } - - private onMarkerChanged(resources: URI[]): void { - this.markersModel.setResourceMarkers(resources.map(resource => [resource, this.readMarkers(resource)])); - } - - private readMarkers(resource?: URI): IMarker[] { - return this.markerService.read({ resource, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); - } - -} export class ActivityUpdater extends Disposable implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts b/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts index 393094a9..d6c633eb 100644 --- a/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersFilterOptions.ts @@ -4,10 +4,41 @@ *--------------------------------------------------------------------------------------------*/ import { IFilter, matchesFuzzy, matchesFuzzy2 } from 'vs/base/common/filters'; -import { IExpression, splitGlobAware, getEmptyExpression } from 'vs/base/common/glob'; +import { IExpression, splitGlobAware, getEmptyExpression, ParsedExpression, parse } from 'vs/base/common/glob'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { ResourceGlobMatcher } from 'vs/base/common/resources'; +import { relativePath } from 'vs/base/common/resources'; +import { TernarySearchTree } from 'vs/base/common/map'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; + +export class ResourceGlobMatcher { + + private readonly globalExpression: ParsedExpression; + private readonly expressionsByRoot: TernarySearchTree; + + constructor( + globalExpression: IExpression, + rootExpressions: { root: URI, expression: IExpression }[], + uriIdentityService: IUriIdentityService + ) { + this.globalExpression = parse(globalExpression); + this.expressionsByRoot = TernarySearchTree.forUris<{ root: URI, expression: ParsedExpression }>(uri => uriIdentityService.extUri.ignorePathCasing(uri)); + for (const expression of rootExpressions) { + this.expressionsByRoot.set(expression.root, { root: expression.root, expression: parse(expression.expression) }); + } + } + + matches(resource: URI): boolean { + const rootExpression = this.expressionsByRoot.findSubstr(resource); + if (rootExpression) { + const path = relativePath(rootExpression.root, resource); + if (path && !!rootExpression.expression(path)) { + return true; + } + } + return !!this.globalExpression(resource.path); + } +} export class FilterOptions { @@ -21,7 +52,16 @@ export class FilterOptions { readonly excludesMatcher: ResourceGlobMatcher; readonly includesMatcher: ResourceGlobMatcher; - constructor(readonly filter: string = '', filesExclude: { root: URI, expression: IExpression }[] | IExpression = [], showWarnings: boolean = false, showErrors: boolean = false, showInfos: boolean = false) { + static EMPTY(uriIdentityService: IUriIdentityService) { return new FilterOptions('', [], false, false, false, uriIdentityService); } + + constructor( + readonly filter: string, + filesExclude: { root: URI, expression: IExpression }[] | IExpression, + showWarnings: boolean, + showErrors: boolean, + showInfos: boolean, + uriIdentityService: IUriIdentityService + ) { filter = filter.trim(); this.showWarnings = showWarnings; this.showErrors = showErrors; @@ -43,8 +83,8 @@ export class FilterOptions { } } - this.excludesMatcher = new ResourceGlobMatcher(excludesExpression, filesExcludeByRoot); - this.includesMatcher = new ResourceGlobMatcher(includeExpression, []); + this.excludesMatcher = new ResourceGlobMatcher(excludesExpression, filesExcludeByRoot, uriIdentityService); + this.includesMatcher = new ResourceGlobMatcher(includeExpression, [], uriIdentityService); this.textFilter = this.textFilter.trim(); } diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index f8016d3d..a12e1658 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -12,6 +12,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { Emitter, Event } from 'vs/base/common/event'; import { Hasher } from 'vs/base/common/hash'; import { withUndefinedAsNull } from 'vs/base/common/types'; +import { splitLines } from 'vs/base/common/strings'; export function compareMarkersByUri(a: IMarker, b: IMarker) { @@ -95,7 +96,7 @@ export class Marker { private _lines: string[] | undefined; get lines(): string[] { if (!this._lines) { - this._lines = this.marker.message.split(/\r\n|\r|\n/g); + this._lines = splitLines(this.marker.message); } return this._lines; } @@ -150,6 +151,16 @@ export class MarkersModel { this.resourcesByUri = new Map(); } + reset(): void { + const removed = new Set(); + for (const resourceMarker of this.resourcesByUri.values()) { + removed.add(resourceMarker); + } + this.resourcesByUri.clear(); + this._total = 0; + this._onDidChange.fire({ removed, added: new Set(), updated: new Set() }); + } + private _total: number = 0; get total(): number { return this._total; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 67f945f0..6a344c8a 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -14,7 +14,7 @@ import { ResourceMarkers, Marker, RelatedInformation } from 'vs/workbench/contri import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { QuickFixAction, QuickFixActionViewItem } from 'vs/workbench/contrib/markers/browser/markersViewActions'; @@ -53,6 +53,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Progress } from 'vs/platform/progress/common/progress'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -255,9 +257,10 @@ export class MarkerRenderer implements ITreeRenderer 1; action.tooltip = multiline ? localize('single line', "Show message in single line") : localize('multi line', "Show message in multiple lines"); - action.class = multiline ? expandedClass : collapsedClass; + action.class = ThemeIcon.asClassName(multiline ? expandedIcon : collapsedIcon); action.run = () => { if (viewModel) { viewModel.multiline = !viewModel.multiline; } return Promise.resolve(); }; this.multilineActionbar.push([action], { icon: true, label: false }); } diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 2163578c..8836c837 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -11,7 +11,7 @@ import { IAction, IActionViewItem, Action, Separator } from 'vs/base/common/acti import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; -import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent } from 'vs/workbench/contrib/markers/browser/markersModel'; +import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri } from 'vs/workbench/contrib/markers/browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MarkersFilterActionViewItem, MarkersFilters, IMarkersFiltersChangeEvent, IMarkerFilterController } from 'vs/workbench/contrib/markers/browser/markersViewActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -19,8 +19,7 @@ import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IMarkersWorkbenchService } from 'vs/workbench/contrib/markers/browser/markers'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Iterable } from 'vs/base/common/iterator'; @@ -39,7 +38,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { domEvent } from 'vs/base/browser/event'; import { ResourceLabels } from 'vs/workbench/browser/labels'; -import { IMarker } from 'vs/platform/markers/common/markers'; +import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { MementoObject, Memento } from 'vs/workbench/common/memento'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -51,6 +50,11 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Codicon } from 'vs/base/common/codicons'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { groupBy } from 'vs/base/common/arrays'; +import { ResourceMap } from 'vs/base/common/map'; +import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { return Iterable.map(resourceMarkers.markers, m => { @@ -67,7 +71,9 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { private currentActiveResource: URI | null = null; private readonly rangeHighlightDecorations: RangeHighlightDecorations; + private readonly markersModel: MarkersModel; private readonly filter: Filter; + private readonly onVisibleDisposables = this._register(new DisposableStore()); private tree: MarkersTree | undefined; private filterActionBar: ActionBar | undefined; @@ -102,11 +108,12 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService telemetryService: ITelemetryService, - @IMarkersWorkbenchService private readonly markersWorkbenchService: IMarkersWorkbenchService, + @IMarkerService private readonly markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IContextMenuService contextMenuService: IContextMenuService, @IMenuService private readonly menuService: IMenuService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, @@ -114,17 +121,15 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.smallLayoutContextKey = Constants.MarkersViewSmallLayoutContextKey.bindTo(this.contextKeyService); - this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE); + this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.USER); + this.markersModel = this._register(instantiationService.createInstance(MarkersModel)); this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'])); - for (const resourceMarker of this.markersWorkbenchService.markersModel.resourceMarkers) { - resourceMarker.markers.forEach(marker => this.markersViewModel.add(marker)); - } - this._register(this.markersViewModel.onDidChange(marker => this.onDidChangeViewState(marker))); + this._register(this.onDidChangeVisibility(visible => this.onDidChangeMarkersViewVisibility(visible))); this.setCurrentActiveEditor(); - this.filter = new Filter(new FilterOptions()); + this.filter = new Filter(FilterOptions.EMPTY(uriIdentityService)); this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations)); // actions @@ -152,18 +157,9 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { this.createArialLabelElement(container); this.createMessageBox(container); this.createTree(container); - this.createListeners(); this.updateFilter(); - this._register(this.onDidChangeVisibility(visible => { - if (visible) { - this.refreshPanel(); - } else { - this.rangeHighlightDecorations.removeHighlightRange(); - } - })); - this.filterActionBar!.push(new Action(`workbench.actions.treeView.${this.id}.filter`)); this.renderContent(); } @@ -225,7 +221,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { group: 'navigation', order: Number.MAX_SAFE_INTEGER, }, - icon: { id: 'codicon/collapse-all' } + icon: Codicon.collapseAll }); } async run(): Promise { @@ -332,11 +328,15 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } private setTreeSelection(): void { - if (this.tree && this.tree.getSelection().length === 0) { - const firstMarker = this.markersWorkbenchService.markersModel.resourceMarkers[0]?.markers[0]; - if (firstMarker) { - this.tree.setFocus([firstMarker]); - this.tree.setSelection([firstMarker]); + if (this.tree && this.tree.isVisible() && this.tree.getSelection().length === 0) { + const firstVisibleElement = this.tree.firstVisibleElement; + const marker = firstVisibleElement ? + firstVisibleElement instanceof ResourceMarkers ? firstVisibleElement.markers[0] : + firstVisibleElement instanceof Marker ? firstVisibleElement : undefined + : undefined; + if (marker) { + this.tree.setFocus([marker]); + this.tree.setSelection([marker]); } } } @@ -352,20 +352,20 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { let resourceMarkers: ResourceMarkers[] = []; if (this.filters.activeFile) { if (this.currentActiveResource) { - const activeResourceMarkers = this.markersWorkbenchService.markersModel.getResourceMarkers(this.currentActiveResource); + const activeResourceMarkers = this.markersModel.getResourceMarkers(this.currentActiveResource); if (activeResourceMarkers) { resourceMarkers = [activeResourceMarkers]; } } } else { - resourceMarkers = this.markersWorkbenchService.markersModel.resourceMarkers; + resourceMarkers = this.markersModel.resourceMarkers; } this.tree.setChildren(null, Iterable.map(resourceMarkers, m => ({ element: m, children: createResourceMarkersIterator(m) }))); } private updateFilter() { this.cachedFilterStats = undefined; - this.filter.options = new FilterOptions(this.filters.filterText, this.getFilesExcludeExpressions(), this.filters.showWarnings, this.filters.showErrors, this.filters.showInfos); + this.filter.options = new FilterOptions(this.filters.filterText, this.getFilesExcludeExpressions(), this.filters.showWarnings, this.filters.showErrors, this.filters.showInfos, this.uriIdentityService); if (this.tree) { this.tree.refilter(); } @@ -497,6 +497,8 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } } })); + + this._register(this.tree.onDidChangeSelection(() => this.onSelected())); } private collapseAll(): void { @@ -509,18 +511,45 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } } - private createListeners(): void { - this._register(Event.any(this.markersWorkbenchService.markersModel.onDidChange, this.editorService.onDidActiveEditorChange)(changes => { + private onDidChangeMarkersViewVisibility(visible: boolean): void { + this.onVisibleDisposables.clear(); + if (visible) { + for (const disposable of this.reInitialize()) { + this.onVisibleDisposables.add(disposable); + } + this.refreshPanel(); + } + } + + private reInitialize(): IDisposable[] { + const disposables = []; + + // Markers Model + const readMarkers = (resource?: URI) => this.markerService.read({ resource, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); + this.markersModel.setResourceMarkers(groupBy(readMarkers(), compareMarkersByUri).map(group => [group[0].resource, group])); + disposables.push(Event.debounce>(this.markerService.onMarkerChanged, (resourcesMap, resources) => { + resourcesMap = resourcesMap || new ResourceMap(); + resources.forEach(resource => resourcesMap!.set(resource, resource)); + return resourcesMap; + }, 64)(resourcesMap => { + this.markersModel.setResourceMarkers([...resourcesMap.values()].map(resource => [resource, readMarkers(resource)])); + })); + disposables.push(Event.any(this.markersModel.onDidChange, this.editorService.onDidActiveEditorChange)(changes => { if (changes) { this.onDidChangeModel(changes); } else { this.onActiveEditorChanged(); } })); - if (this.tree) { - this._register(this.tree.onDidChangeSelection(() => this.onSelected())); - } - this._register(this.filters.onDidChange((event: IMarkersFiltersChangeEvent) => { + disposables.push(toDisposable(() => this.markersModel.reset())); + + // Markers View Model + this.markersModel.resourceMarkers.forEach(resourceMarker => resourceMarker.markers.forEach(marker => this.markersViewModel.add(marker))); + disposables.push(this.markersViewModel.onDidChange(marker => this.onDidChangeViewState(marker))); + disposables.push(toDisposable(() => this.markersModel.resourceMarkers.forEach(resourceMarker => this.markersViewModel.remove(resourceMarker.resource)))); + + // Markers Filters + disposables.push(this.filters.onDidChange((event: IMarkersFiltersChangeEvent) => { this.reportFilteringUsed(); if (event.activeFile) { this.refreshPanel(); @@ -528,14 +557,19 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { this.updateFilter(); } })); + disposables.push(toDisposable(() => { this.cachedFilterStats = undefined; })); + + disposables.push(toDisposable(() => this.rangeHighlightDecorations.removeHighlightRange())); + + return disposables; } - private onDidChangeModel(change: MarkerChangesEvent) { + private onDidChangeModel(change: MarkerChangesEvent): void { const resourceMarkers = [...change.added, ...change.removed, ...change.updated]; const resources: URI[] = []; for (const { resource } of resourceMarkers) { this.markersViewModel.remove(resource); - const resourceMarkers = this.markersWorkbenchService.markersModel.getResourceMarkers(resource); + const resourceMarkers = this.markersModel.getResourceMarkers(resource); if (resourceMarkers) { for (const marker of resourceMarkers.markers) { this.markersViewModel.add(marker); @@ -574,7 +608,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { private setCurrentActiveEditor(): void { const activeEditor = this.editorService.activeEditor; - this.currentActiveResource = activeEditor ? withUndefinedAsNull(activeEditor.resource) : null; + this.currentActiveResource = activeEditor ? withUndefinedAsNull(EditorResourceAccessor.getOriginalUri(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY })) : null; } private onSelected(): void { @@ -631,7 +665,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } private renderFilterMessageForActiveFile(container: HTMLElement): void { - if (this.currentActiveResource && this.markersWorkbenchService.markersModel.getResourceMarkers(this.currentActiveResource)) { + if (this.currentActiveResource && this.markersModel.getResourceMarkers(this.currentActiveResource)) { this.renderFilteredByFilterMessage(container); } else { this.renderNoProblemsMessageForActiveFile(container); @@ -716,7 +750,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } private getResourceForCurrentActiveResource(): ResourceMarkers | null { - return this.currentActiveResource ? this.markersWorkbenchService.markersModel.getResourceMarkers(this.currentActiveResource) : null; + return this.currentActiveResource ? this.markersModel.getResourceMarkers(this.currentActiveResource) : null; } private hasSelectedMarkerFor(resource: ResourceMarkers): boolean { @@ -844,7 +878,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } } - return { total: this.markersWorkbenchService.markersModel.total, filtered }; + return { total: this.markersModel.total, filtered }; } private getTelemetryData({ source, code }: IMarker): any { @@ -917,6 +951,10 @@ class MarkersTree extends WorkbenchObjectTree { this.container.classList.toggle('hidden', hide); } + isVisible(): boolean { + return !this.container.classList.contains('hidden'); + } + } registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 55fbbbb1..973e9e69 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -12,7 +12,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; -import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -28,6 +28,7 @@ import { IViewsService } from 'vs/workbench/common/views'; import { Codicon } from 'vs/base/common/codicons'; import { BaseActionViewItem, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export class ShowProblemsPanelAction extends Action { @@ -256,6 +257,9 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { } + +const filterIcon = registerIcon('markers-view-filter', Codicon.filter, localize('filterIcon', 'Icon for the filter configuration in the markers view.')); + export class MarkersFilterActionViewItem extends BaseActionViewItem { private delayedFilterUpdate: Delayer; @@ -279,7 +283,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); this._register(filterController.onDidFocusFilter(() => this.focus())); this._register(filterController.onDidClearFilterText(() => this.clearFilterText())); - this.filtersAction = new Action('markersFiltersAction', Messages.MARKERS_PANEL_ACTION_TOOLTIP_MORE_FILTERS, 'markers-filters codicon-filter'); + this.filtersAction = new Action('markersFiltersAction', Messages.MARKERS_PANEL_ACTION_TOOLTIP_MORE_FILTERS, 'markers-filters ' + ThemeIcon.asClassName(filterIcon)); this.filtersAction.checked = this.hasFiltersChanged(); this._register(filterController.filters.onDidChange(e => this.onDidFiltersChange(e))); } diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index 245f72a8..3232b047 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -22,7 +22,7 @@ export const CELL_TOP_MARGIN = 6; export const CELL_BOTTOM_MARGIN = 6; // Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight` -export const EDITOR_TOP_PADDING = 12; +// export const EDITOR_TOP_PADDING = 12; export const EDITOR_BOTTOM_PADDING = 4; export const EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR = 12; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 337cc9eb..acf32d1a 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -6,7 +6,7 @@ import * as glob from 'vs/base/common/glob'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -21,12 +21,13 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellEditType, CellKind, ICellEditOperation, ICellRange, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellEditOperation, ICellRange, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; // Notebook Commands const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute'; @@ -64,7 +65,6 @@ const SPLIT_CELL_COMMAND_ID = 'notebook.cell.split'; const JOIN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.joinAbove'; const JOIN_CELL_BELOW_COMMAND_ID = 'notebook.cell.joinBelow'; -const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute'; const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution'; const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow'; const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow'; @@ -103,7 +103,7 @@ const enum CellOverflowToolbarGroups { export interface INotebookActionContext { readonly cellTemplate?: BaseCellRenderTemplate; readonly cell?: ICellViewModel; - readonly notebookEditor: INotebookEditor; + readonly notebookEditor: IActiveNotebookEditor; readonly ui?: boolean; } @@ -162,6 +162,10 @@ abstract class NotebookAction extends Action2 { return; } + if (!editor.hasModel()) { + return; + } + const activeCell = editor.getActiveCell(); return { cell: activeCell, @@ -233,7 +237,7 @@ registerAction2(class extends NotebookCellAction { } ] }, - icon: { id: 'codicon/play' }, + icon: icons.executeIcon }); } @@ -267,7 +271,7 @@ registerAction2(class extends NotebookCellAction { super({ id: CANCEL_CELL_COMMAND_ID, title: localize('notebookActions.cancel', "Stop Cell Execution"), - icon: { id: 'codicon/primitive-square' }, + icon: icons.stopIcon, description: { description: localize('notebookActions.execute', "Execute Cell"), args: [ @@ -326,7 +330,7 @@ export class ExecuteCellAction extends MenuItemAction { { id: EXECUTE_CELL_COMMAND_ID, title: localize('notebookActions.executeCell', "Execute Cell"), - icon: { id: 'codicon/play' } + icon: icons.executeIcon }, undefined, { shouldForwardArgs: true }, @@ -344,7 +348,7 @@ export class CancelCellAction extends MenuItemAction { { id: CANCEL_CELL_COMMAND_ID, title: localize('notebookActions.CancelCell', "Cancel Execution"), - icon: { id: 'codicon/primitive-square' } + icon: icons.stopIcon }, undefined, { shouldForwardArgs: true }, @@ -362,7 +366,7 @@ export class DeleteCellAction extends MenuItemAction { { id: DELETE_CELL_COMMAND_ID, title: localize('notebookActions.deleteCell', "Delete Cell"), - icon: { id: 'codicon/trash' } + icon: icons.deleteCellIcon, }, undefined, { shouldForwardArgs: true }, @@ -385,7 +389,7 @@ registerAction2(class extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - const idx = context.notebookEditor.viewModel?.getCellIndex(context.cell); + const idx = context.notebookEditor.viewModel.getCellIndex(context.cell); if (typeof idx !== 'number') { return; } @@ -393,7 +397,7 @@ registerAction2(class extends NotebookCellAction { const executionP = runCell(accessor, context); // Try to select below, fall back on inserting - const nextCell = context.notebookEditor.viewModel?.viewCells[idx + 1]; + const nextCell = context.notebookEditor.viewModel.viewCells[idx + 1]; if (nextCell) { context.notebookEditor.focusNotebookCell(nextCell, 'container'); } else { @@ -471,7 +475,7 @@ registerAction2(class extends NotebookAction { }); function renderAllMarkdownCells(context: INotebookActionContext): void { - context.notebookEditor.viewModel!.viewCells.forEach(cell => { + context.notebookEditor.viewModel.viewCells.forEach(cell => { if (cell.cellKind === CellKind.Markdown) { cell.editState = CellEditState.Preview; } @@ -508,7 +512,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: EXECUTE_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.menu.executeNotebook', "Execute Notebook (Run all cells)"), - icon: { id: 'codicon/run-all' } + icon: icons.executeAllIcon, }, order: -1, group: 'navigation', @@ -519,7 +523,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CANCEL_NOTEBOOK_COMMAND_ID, title: localize('notebookActions.menu.cancelNotebook', "Stop Notebook Execution"), - icon: { id: 'codicon/primitive-square' } + icon: icons.stopIcon, }, order: -1, group: 'navigation', @@ -836,7 +840,7 @@ registerAction2(class extends NotebookCellAction { order: CellToolbarOrder.EditCell, group: CELL_TITLE_CELL_GROUP_ID }, - icon: { id: 'codicon/pencil' } + icon: icons.editIcon, }); } @@ -860,13 +864,15 @@ registerAction2(class extends NotebookCellAction { order: CellToolbarOrder.SaveCell, group: CELL_TITLE_CELL_GROUP_ID }, - icon: { id: 'codicon/check' }, + icon: icons.stopEditIcon, keybinding: { when: ContextKeyExpr.and( NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext, EditorContextKeys.hoverVisible.toNegated(), - EditorContextKeys.hasNonEmptySelection.toNegated()), + EditorContextKeys.hasNonEmptySelection.toNegated(), + EditorContextKeys.hasMultipleSelections.toNegated() + ), primary: KeyCode.Escape, weight: EDITOR_WIDGET_ACTION_WEIGHT - 5 }, @@ -900,19 +906,19 @@ registerAction2(class extends NotebookCellAction { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), weight: KeybindingWeight.WorkbenchContrib }, - icon: { id: 'codicon/trash' }, + icon: icons.deleteCellIcon }); } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const index = context.notebookEditor.viewModel!.getCellIndex(context.cell); + const index = context.notebookEditor.viewModel.getCellIndex(context.cell); const result = await context.notebookEditor.deleteNotebookCell(context.cell); if (result) { // deletion succeeds, move focus to the next cell - const nextCellIdx = index < context.notebookEditor.viewModel!.length ? index : context.notebookEditor.viewModel!.length - 1; + const nextCellIdx = index < context.notebookEditor.viewModel.length ? index : context.notebookEditor.viewModel.length - 1; if (nextCellIdx >= 0) { - context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel!.viewCells[nextCellIdx], 'container'); + context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel.viewCells[nextCellIdx], 'container'); } } } @@ -944,7 +950,7 @@ registerAction2(class extends NotebookCellAction { { id: MOVE_CELL_UP_COMMAND_ID, title: localize('notebookActions.moveCellUp', "Move Cell Up"), - icon: { id: 'codicon/arrow-up' }, + icon: icons.moveUpIcon, keybinding: { primary: KeyMod.Alt | KeyCode.UpArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -964,7 +970,7 @@ registerAction2(class extends NotebookCellAction { { id: MOVE_CELL_DOWN_COMMAND_ID, title: localize('notebookActions.moveCellDown', "Move Cell Down"), - icon: { id: 'codicon/arrow-down' }, + icon: icons.moveDownIcon, keybinding: { primary: KeyMod.Alt | KeyCode.DownArrow, when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext.toNegated()), @@ -1139,7 +1145,7 @@ registerAction2(class extends NotebookCellAction { return; } - const currCellIndex = viewModel.getCellIndex(context!.cell); + const currCellIndex = viewModel.getCellIndex(context.cell); let topPastedCell: CellViewModel | undefined = undefined; pasteCells.items.reverse().map(cell => { @@ -1237,12 +1243,12 @@ registerAction2(class extends NotebookCellAction { const editor = context.notebookEditor; const activeCell = context.cell; - const idx = editor.viewModel?.getCellIndex(activeCell); + const idx = editor.viewModel.getCellIndex(activeCell); if (typeof idx !== 'number') { return; } - const newCell = editor.viewModel?.viewCells[idx + 1]; + const newCell = editor.viewModel.viewCells[idx + 1]; if (!newCell) { return; @@ -1276,7 +1282,7 @@ registerAction2(class extends NotebookCellAction { const editor = context.notebookEditor; const activeCell = context.cell; - const idx = editor.viewModel?.getCellIndex(activeCell); + const idx = editor.viewModel.getCellIndex(activeCell); if (typeof idx !== 'number') { return; } @@ -1286,7 +1292,7 @@ registerAction2(class extends NotebookCellAction { return; } - const newCell = editor.viewModel?.viewCells[idx - 1]; + const newCell = editor.viewModel.viewCells[idx - 1]; if (!newCell) { return; @@ -1407,7 +1413,7 @@ registerAction2(class extends NotebookCellAction { primary: KeyMod.Alt | KeyCode.Delete, weight: KeybindingWeight.WorkbenchContrib }, - icon: { id: 'codicon/clear-all' }, + icon: icons.clearIcon }); } @@ -1427,7 +1433,7 @@ registerAction2(class extends NotebookCellAction { editor.viewModel.notebookDocument.applyEdits(editor.viewModel.notebookDocument.versionId, [{ editType: CellEditType.Output, index, outputs: [] }], true, undefined, () => undefined, undefined); if (context.cell.metadata && context.cell.metadata?.runState !== NotebookCellRunState.Running) { - context.notebookEditor.viewModel!.notebookDocument.applyEdits(context.notebookEditor.viewModel!.notebookDocument.versionId, [{ + context.notebookEditor.viewModel.notebookDocument.applyEdits(context.notebookEditor.viewModel.notebookDocument.versionId, [{ editType: CellEditType.Metadata, index, metadata: { ...context.cell.metadata, runState: NotebookCellRunState.Idle, @@ -1466,7 +1472,7 @@ export class ChangeCellLanguageAction extends NotebookCellAction { const modelService = accessor.get(IModelService); const quickInputService = accessor.get(IQuickInputService); - const providerLanguages = [...context.notebookEditor.viewModel!.notebookDocument.resolvedLanguages, 'markdown']; + const providerLanguages = [...context.notebookEditor.viewModel.notebookDocument.resolvedLanguages, 'markdown']; providerLanguages.forEach(languageId => { let description: string; if (context.cell.cellKind === CellKind.Markdown ? (languageId === 'markdown') : (languageId === context.cell.language)) { @@ -1515,9 +1521,9 @@ export class ChangeCellLanguageAction extends NotebookCellAction { } else if (selection.languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId); } else { - const index = context.notebookEditor.viewModel!.notebookDocument.cells.indexOf(context.cell.model); - context.notebookEditor.viewModel!.notebookDocument.applyEdits( - context.notebookEditor.viewModel!.notebookDocument.versionId, + const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); + context.notebookEditor.viewModel.notebookDocument.applyEdits( + context.notebookEditor.viewModel.notebookDocument.versionId, [{ editType: CellEditType.CellLanguage, index, language: selection.languageId }], true, undefined, () => undefined, undefined ); @@ -1557,7 +1563,7 @@ registerAction2(class extends NotebookAction { group: 'navigation', order: 0 }, - icon: { id: 'codicon/clear-all' }, + icon: icons.clearIcon }); } @@ -1589,7 +1595,7 @@ registerAction2(class extends NotebookAction { } }).filter(edit => !!edit) as ICellEditOperation[]; if (clearExecutionMetadataEdits.length) { - context.notebookEditor.viewModel!.notebookDocument.applyEdits(context.notebookEditor.viewModel!.notebookDocument.versionId, clearExecutionMetadataEdits, true, undefined, () => undefined, undefined); + context.notebookEditor.viewModel.notebookDocument.applyEdits(context.notebookEditor.viewModel.notebookDocument.versionId, clearExecutionMetadataEdits, true, undefined, () => undefined, undefined); } } }); @@ -1609,7 +1615,7 @@ registerAction2(class extends NotebookCellAction { title: localize('notebookActions.splitCell', "Split Cell"), menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), order: CellToolbarOrder.SplitCell, group: CELL_TITLE_CELL_GROUP_ID, // alt: { @@ -1617,9 +1623,9 @@ registerAction2(class extends NotebookCellAction { // title: localize('notebookActions.joinCellBelow', "Join with Next Cell") // } }, - icon: { id: 'codicon/split-vertical' }, + icon: icons.splitCellIcon, keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH), weight: KeybindingWeight.WorkbenchContrib }, @@ -1713,7 +1719,7 @@ registerAction2(class extends NotebookCellAction { abstract class ChangeNotebookCellMetadataAction extends NotebookCellAction { async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { const cell = context.cell; - const textModel = context.notebookEditor.viewModel?.notebookDocument; + const textModel = context.notebookEditor.viewModel.notebookDocument; if (!textModel) { return; } @@ -1840,6 +1846,10 @@ registerAction2(class extends Action2 { return; } + if (!editor.hasModel()) { + return; + } + const activeCell = editor.getActiveCell(); return { cell: activeCell, @@ -1851,7 +1861,7 @@ registerAction2(class extends Action2 { const activeEditorContext = this.getActiveEditorContext(accessor); if (activeEditorContext) { - const viewModel = activeEditorContext.notebookEditor.viewModel!; + const viewModel = activeEditorContext.notebookEditor.viewModel; console.log('--- notebook ---'); console.log(viewModel.layoutInfo); console.log('--- cells ---'); @@ -1865,6 +1875,19 @@ registerAction2(class extends Action2 { } }); +// Revisit once we have a story for trusted workspace +CommandsRegistry.registerCommand('notebook.trust', (accessor, args) => { + const uri = URI.revive(args as UriComponents); + const notebookService = accessor.get(INotebookService); + + + const document = notebookService.listNotebookDocuments().find(document => document.uri.toString() === uri.toString()); + + if (document) { + document.applyEdits(document.versionId, [{ editType: CellEditType.DocumentMetadata, metadata: { ...document.metadata, ...{ trusted: true } } }], true, undefined, () => undefined, undefined, false); + } +}); + CommandsRegistry.registerCommand('_resolveNotebookContentProvider', (accessor, args): { viewType: string; displayName: string; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts index 71f74948..2c2c46b8 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findController.ts @@ -43,6 +43,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote private _currentMatchDecorations: ICellModelDecorations[] = []; private _showTimeout: number | null = null; private _hideTimeout: number | null = null; + private _previousFocusElement?: HTMLElement; constructor( private readonly _notebookEditor: INotebookEditor, @@ -65,6 +66,10 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote this._register(this._state.onFindReplaceStateChange(() => { this.onInputChanged(); })); + + this._register(DOM.addDisposableListener(this.getDomNode(), DOM.EventType.FOCUS, e => { + this._previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; + }, true)); } private _onFindInputKeyDown(e: IKeyboardEvent): void { @@ -114,7 +119,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote if (!this._findMatchesStarts) { this.set(this._findMatches, true); } else { - const totalVal = this._findMatchesStarts!.getTotalValue(); + const totalVal = this._findMatchesStarts.getTotalValue(); const nextVal = (this._currentMatch + (previous ? -1 : 1) + totalVal) % totalVal; this._currentMatch = nextVal; } @@ -167,6 +172,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } protected onFocusTrackerBlur() { + this._previousFocusElement = undefined; this._findWidgetFocused.reset(); } @@ -324,6 +330,11 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote } else { // no op } + + if (this._previousFocusElement && this._previousFocusElement.offsetParent) { + this._previousFocusElement.focus(); + this._previousFocusElement = undefined; + } } clear() { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 0e4f30a0..0235b443 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -116,7 +116,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont const target = e.event.target as HTMLElement; - if (target.classList.contains('codicon-chevron-down') || target.classList.contains('codicon-chevron-right')) { + if (target.classList.contains('codicon-notebook-collapsed') || target.classList.contains('codicon-notebook-expanded')) { const parent = target.parentElement as HTMLElement; if (!parent.classList.contains('notebook-folding-indicator')) { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index 3675729c..777b97b7 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -48,7 +48,11 @@ export class FoldingModel extends Disposable { })); this._viewModelStore.add(this._viewModel.onDidChangeSelection(() => { - const selectionHandles = this._viewModel!.selectionHandles; + if (!this._viewModel) { + return; + } + + const selectionHandles = this._viewModel.selectionHandles; const indexes = selectionHandles.map(handle => this._viewModel!.getCellIndex(this._viewModel!.getCellByHandle(handle)!) ); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts b/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts deleted file mode 100644 index 670a805c..00000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/scm/scm.ts +++ /dev/null @@ -1,163 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { INotebookEditorContribution, INotebookEditor } from '../../notebookBrowser'; -import { registerNotebookContribution } from '../../notebookEditorExtensions'; -import { ISCMService } from 'vs/workbench/contrib/scm/common/scm'; -import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; -import { first, ThrottledDelayer } from 'vs/base/common/async'; -import { INotebookService } from '../../../common/notebookService'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { IFileService } from 'vs/platform/files/common/files'; -import { URI } from 'vs/base/common/uri'; - -export class SCMController extends Disposable implements INotebookEditorContribution { - static id: string = 'workbench.notebook.findController'; - private _lastDecorationId: string[] = []; - private _localDisposable = new DisposableStore(); - private _originalDocument: NotebookTextModel | undefined = undefined; - private _originalResourceDisposableStore = new DisposableStore(); - private _diffDelayer = new ThrottledDelayer(200); - - private _lastVersion = -1; - - - constructor( - private readonly _notebookEditor: INotebookEditor, - @IFileService private readonly _fileService: FileService, - @ISCMService private readonly _scmService: ISCMService, - @INotebookService private readonly _notebookService: INotebookService - - ) { - super(); - - if (!this._notebookEditor.isEmbedded) { - this._register(this._notebookEditor.onDidChangeModel(() => { - this._localDisposable.clear(); - this._originalResourceDisposableStore.clear(); - this._diffDelayer.cancel(); - this.update(); - - if (this._notebookEditor.textModel) { - this._localDisposable.add(this._notebookEditor.textModel.onDidChangeContent((e) => { - this.update(); - })); - } - })); - - this._register(this._notebookEditor.onWillDispose(() => { - this._localDisposable.clear(); - this._originalResourceDisposableStore.clear(); - })); - - this.update(); - } - } - - private async _resolveNotebookDocument(uri: URI, viewType: string) { - const providers = this._scmService.repositories.map(r => r.provider); - const rootedProviders = providers.filter(p => !!p.rootUri); - - rootedProviders.sort(createProviderComparer(uri)); - - const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri))); - - if (!result) { - this._originalDocument = undefined; - this._originalResourceDisposableStore.clear(); - return; - } - - if (result.toString() === this._originalDocument?.uri.toString()) { - // original document not changed - return; - } - - this._originalResourceDisposableStore.add(this._fileService.watch(result)); - this._originalResourceDisposableStore.add(this._fileService.onDidFilesChange(e => { - if (e.contains(result)) { - this._originalDocument = undefined; - this._originalResourceDisposableStore.clear(); - this.update(); - } - })); - - const originalDocument = await this._notebookService.resolveNotebook(viewType, result, false); - this._originalResourceDisposableStore.add({ - dispose: () => { - this._originalDocument?.dispose(); - this._originalDocument = undefined; - } - }); - - this._originalDocument = originalDocument; - } - - async update() { - if (!this._diffDelayer) { - return; - } - - await this._diffDelayer - .trigger(async () => { - const modifiedDocument = this._notebookEditor.textModel; - if (!modifiedDocument) { - return; - } - - if (this._lastVersion >= modifiedDocument.versionId) { - return; - } - - this._lastVersion = modifiedDocument.versionId; - await this._resolveNotebookDocument(modifiedDocument.uri, modifiedDocument.viewType); - - if (!this._originalDocument) { - this._clear(); - return; - } - - // const diff = new LcsDiff(new CellSequence(this._originalDocument), new CellSequence(modifiedDocument)); - // const diffResult = diff.ComputeDiff(false); - - // const decorations: INotebookDeltaDecoration[] = []; - // diffResult.changes.forEach(change => { - // if (change.originalLength === 0) { - // // doesn't exist in original - // for (let i = 0; i < change.modifiedLength; i++) { - // decorations.push({ - // handle: modifiedDocument.cells[change.modifiedStart + i].handle, - // options: { gutterClassName: 'nb-gutter-cell-inserted' } - // }); - // } - // } else { - // if (change.modifiedLength === 0) { - // // diff.deleteCount - // // removed from original - // } else { - // // modification - // for (let i = 0; i < change.modifiedLength; i++) { - // decorations.push({ - // handle: modifiedDocument.cells[change.modifiedStart + i].handle, - // options: { gutterClassName: 'nb-gutter-cell-changed' } - // }); - // } - // } - // } - // }); - - - // this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations); - }); - } - - private _clear() { - this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, []); - } -} - -registerNotebookContribution(SCMController.id, SCMController); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index 7d7abed7..2f32225c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -21,6 +21,8 @@ import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/l import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { configureKernelIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; registerAction2(class extends Action2 { @@ -30,14 +32,13 @@ registerAction2(class extends Action2 { category: NOTEBOOK_ACTIONS_CATEGORY, title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' }, precondition: NOTEBOOK_IS_ACTIVE_EDITOR, - icon: { id: 'codicon/server-environment' }, + icon: selectKernelIcon, f1: true }); } async run(accessor: ServicesAccessor, context?: INotebookActionContext): Promise { const editorService = accessor.get(IEditorService); - const notebookService = accessor.get(INotebookService); const quickInputService = accessor.get(IQuickInputService); const configurationService = accessor.get(IConfigurationService); @@ -48,8 +49,14 @@ registerAction2(class extends Action2 { const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; const activeKernel = editor.activeKernel; + const picker = quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); + picker.placeholder = nls.localize('notebook.runCell.selectKernel', "Select a notebook kernel to run this notebook"); + picker.matchOnDetail = true; + picker.show(); + picker.busy = true; + const tokenSource = new CancellationTokenSource(); - const availableKernels2 = await notebookService.getContributedNotebookKernels2(editor.viewModel!.viewType, editor.viewModel!.uri, tokenSource.token); + const availableKernels2 = await editor.beginComputeContributedKernels(); const picks: QuickPickInput[] = [...availableKernels2].map((a) => { return { id: a.id, @@ -68,17 +75,15 @@ registerAction2(class extends Action2 { a.resolve(editor.uri!, editor.getId(), tokenSource.token); }, buttons: [{ - iconClass: 'codicon-settings-gear', + iconClass: ThemeIcon.asClassName(configureKernelIcon), tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default kernel provider for '{0}'", editor.viewModel!.viewType) }] }; }); - const picker = quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); picker.items = picks; + picker.busy = false; picker.activeItems = picks.filter(pick => (pick as IQuickPickItem).picked) as (IQuickPickItem & { run(): void; kernelProviderId?: string; })[]; - picker.placeholder = nls.localize('pickAction', "Select Action"); - picker.matchOnDetail = true; const pickedItem = await new Promise<(IQuickPickItem & { run(): void; kernelProviderId?: string; }) | undefined>(resolve => { picker.onDidAccept(() => { @@ -113,7 +118,6 @@ registerAction2(class extends Action2 { } }); - picker.show(); }); tokenSource.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index a53d3536..f526080b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -9,10 +9,9 @@ import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/edit import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellDiffViewModel, PropertyFoldingState } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/common'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { renderCodicons } from 'vs/base/browser/codicons'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { format } from 'vs/base/common/jsonFormatter'; @@ -22,12 +21,16 @@ import { hash } from 'vs/base/common/hash'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; +import { getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { renderCodicons } from 'vs/base/browser/codicons'; const fixedEditorOptions: IEditorOptions = { padding: { @@ -62,7 +65,8 @@ const fixedDiffEditorOptions: IDiffEditorOptions = { glyphMargin: true, enableSplitViewResizing: false, renderIndicators: false, - readOnly: false + readOnly: false, + isInEmbeddedEditor: true }; @@ -123,11 +127,13 @@ class PropertyHeader extends Disposable { return undefined; } }); + this._register(this._toolbar); this._toolbar.context = { cell: this.cell }; this._menu = this.menuService.createMenu(this.accessor.menuId, this.contextKeyService); + this._register(this._menu); if (metadataChanged) { const actions: IAction[] = []; @@ -142,7 +148,7 @@ class PropertyHeader extends Disposable { const target = e.event.target as HTMLElement; - if (target.classList.contains('codicon-chevron-down') || target.classList.contains('codicon-chevron-right')) { + if (target.classList.contains('codicon-notebook-collapsed') || target.classList.contains('codicon-notebook-expanded')) { const parent = target.parentElement as HTMLElement; if (!parent) { @@ -194,9 +200,9 @@ class PropertyHeader extends Disposable { private _updateFoldingIcon() { if (this.accessor.getFoldingState(this.cell) === PropertyFoldingState.Collapsed) { - DOM.reset(this._foldingIndicator, ...renderCodicons('$(chevron-right)')); + DOM.reset(this._foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(collapsedIcon))); } else { - DOM.reset(this._foldingIndicator, ...renderCodicons('$(chevron-down)')); + DOM.reset(this._foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(expandedIcon))); } } } @@ -228,6 +234,7 @@ abstract class AbstractCellRenderer extends Disposable { outputHeight: number; bodyMargin: number; }; + protected _isDisposed: boolean; constructor( readonly notebookEditor: INotebookTextDiffEditor, @@ -247,6 +254,7 @@ abstract class AbstractCellRenderer extends Disposable { ) { super(); // init + this._isDisposed = false; this._layoutInfo = { editorHeight: 0, editorMargin: 0, @@ -528,6 +536,7 @@ abstract class AbstractCellRenderer extends Disposable { originalEditable: false, ignoreTrimWhitespace: false }); + this._register(this._metadataEditor); this._metadataEditorContainer?.classList.add('diff'); @@ -583,6 +592,7 @@ abstract class AbstractCellRenderer extends Disposable { overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: false }, {}); + this._register(this._metadataEditor); const mode = this.modeService.create('jsonc'); const originalMetadataSource = this._getFormatedMetadataJSON( @@ -632,6 +642,7 @@ abstract class AbstractCellRenderer extends Disposable { readOnly: true, ignoreTrimWhitespace: false }); + this._register(this._outputEditor); this._outputEditorContainer?.classList.add('diff'); @@ -671,10 +682,11 @@ abstract class AbstractCellRenderer extends Disposable { }, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); + this._register(this._outputEditor); const mode = this.modeService.create('json'); const originaloutputSource = this._getFormatedOutputJSON( - this.notebookEditor.textModel!.transientOptions + this.notebookEditor.textModel!.transientOptions.transientOutputs ? [] : this.cell.type === 'insert' ? this.cell.modified!.outputs || [] @@ -706,6 +718,11 @@ abstract class AbstractCellRenderer extends Disposable { ); } + dispose() { + this._isDisposed = true; + super.dispose(); + } + abstract initData(): void; abstract styleContainer(container: HTMLElement): void; abstract buildSourceEditor(sourceContainer: HTMLElement): void; @@ -744,7 +761,7 @@ export class DeletedCell extends AbstractCellRenderer { const originalCell = this.cell.original!; const lineCount = originalCell.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); @@ -756,6 +773,8 @@ export class DeletedCell extends AbstractCellRenderer { }, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode() }, {}); + this._register(this._editor); + this._layoutInfo.editorHeight = editorHeight; this._register(this._editor.onDidContentSizeChange((e) => { @@ -766,6 +785,10 @@ export class DeletedCell extends AbstractCellRenderer { })); originalCell.resolveTextModelRef().then(ref => { + if (this._isDisposed) { + return; + } + this._register(ref); const textModel = ref.object.textEditorModel; @@ -838,7 +861,7 @@ export class InsertCell extends AbstractCellRenderer { const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(CodeEditorWidget, editorContainer, { @@ -850,6 +873,7 @@ export class InsertCell extends AbstractCellRenderer { overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: false }, {}); + this._register(this._editor); this._layoutInfo.editorHeight = editorHeight; @@ -861,6 +885,10 @@ export class InsertCell extends AbstractCellRenderer { })); modifiedCell.resolveTextModelRef().then(ref => { + if (this._isDisposed) { + return; + } + this._register(ref); const textModel = ref.object.textEditorModel; @@ -937,7 +965,7 @@ export class ModifiedCell extends AbstractCellRenderer { const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; this._editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); this._editor = this.instantiationService.createInstance(DiffEditorWidget, this._editorContainer, { @@ -946,6 +974,7 @@ export class ModifiedCell extends AbstractCellRenderer { originalEditable: false, ignoreTrimWhitespace: false }); + this._register(this._editor); this._editorContainer.classList.add('diff'); this._editor.layout({ @@ -982,6 +1011,7 @@ export class ModifiedCell extends AbstractCellRenderer { }; this._menu = this.menuService.createMenu(MenuId.NotebookDiffCellInputTitle, this.contextKeyService); + this._register(this._menu); const actions: IAction[] = []; createAndFillInActionBarActions(this._menu, { shouldForwardArgs: true }, actions); this._toolbar.setActions(actions); @@ -1007,6 +1037,11 @@ export class ModifiedCell extends AbstractCellRenderer { const originalRef = await originalCell.resolveTextModelRef(); const modifiedRef = await modifiedCell.resolveTextModelRef(); + + if (this._isDisposed) { + return; + } + const textModel = originalRef.object.textEditorModel; const modifiedTextModel = modifiedRef.object.textEditorModel; this._register(originalRef); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index f91e70ba..cb8709d1 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -7,11 +7,11 @@ import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/b import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; -import { ActiveEditorContext } from 'vs/workbench/common/editor'; +import { ActiveEditorContext, viewColumnToEditorGroup } from 'vs/workbench/common/editor'; import { CellDiffViewModel } from 'vs/workbench/contrib/notebook/browser/diff/celllDiffViewModel'; import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor'; import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; +import { openAsTextIcon, revertIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -21,7 +21,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'notebook.diff.switchToText', - icon: { id: 'codicon/file-code' }, + icon: openAsTextIcon, title: { value: localize('notebook.diff.switchToText', "Open Text Diff Editor"), original: 'Open Text Diff Editor' }, precondition: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID), menu: [{ @@ -57,7 +57,7 @@ registerAction2(class extends Action2 { { id: 'notebook.diff.cell.revertMetadata', title: localize('notebook.diff.cell.revertMetadata', "Revert Metadata"), - icon: { id: 'codicon/discard' }, + icon: revertIcon, f1: false, menu: { id: MenuId.NotebookDiffCellMetadataTitle @@ -87,7 +87,7 @@ registerAction2(class extends Action2 { { id: 'notebook.diff.cell.revertOutputs', title: localize('notebook.diff.cell.revertOutputs', "Revert Outputs"), - icon: { id: 'codicon/discard' }, + icon: revertIcon, f1: false, menu: { id: MenuId.NotebookDiffCellOutputsTitle @@ -117,7 +117,7 @@ registerAction2(class extends Action2 { { id: 'notebook.diff.cell.revertInput', title: localize('notebook.diff.cell.revertInput', "Revert Input"), - icon: { id: 'codicon/discard' }, + icon: revertIcon, f1: false, menu: { id: MenuId.NotebookDiffCellInputTitle diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 6c6c52dc..13f6973c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -60,6 +60,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return this._model?.modified.notebook; } + private _revealFirst: boolean; + constructor( @IInstantiationService readonly instantiationService: IInstantiationService, @IThemeService readonly themeService: IThemeService, @@ -74,6 +76,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD super(NotebookTextDiffEditor.ID, telemetryService, themeService, storageService); const editorOptions = this.configurationService.getValue('editor'); this._fontInfo = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()); + this._revealFirst = true; this._register(this._modifiedResourceDisposableStore); } @@ -152,13 +155,15 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return; } + this._revealFirst = true; + this._modifiedResourceDisposableStore.add(this._fileService.watch(this._model.modified.resource)); this._modifiedResourceDisposableStore.add(this._fileService.onDidFilesChange(async e => { if (this._model === null) { return; } - if (e.contains(this._model!.modified.resource)) { + if (e.contains(this._model.modified.resource)) { if (this._model.modified.isDirty()) { return; } @@ -174,7 +179,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } } - if (e.contains(this._model!.original.resource)) { + if (e.contains(this._model.original.resource)) { if (this._model.original.isDirty()) { return; } @@ -223,6 +228,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD let originalCellIndex = 0; let modifiedCellIndex = 0; + let firstChangeIndex = -1; + for (let i = 0; i < cellChanges.length; i++) { const change = cellChanges[i]; // common cells @@ -238,6 +245,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._eventDispatcher! )); } else { + if (firstChangeIndex === -1) { + firstChangeIndex = cellDiffViewModels.length; + } + cellDiffViewModels.push(new CellDiffViewModel( originalCell, modifiedCell, @@ -247,7 +258,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } } - cellDiffViewModels.push(...this._computeModifiedLCS(change, originalModel, modifiedModel)); + const modifiedLCS = this._computeModifiedLCS(change, originalModel, modifiedModel); + if (modifiedLCS.length && firstChangeIndex === -1) { + firstChangeIndex = cellDiffViewModels.length; + } + + cellDiffViewModels.push(...modifiedLCS); originalCellIndex = change.originalStart + change.originalLength; modifiedCellIndex = change.modifiedStart + change.modifiedLength; } @@ -262,6 +278,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } this._list.splice(0, this._list.length, cellDiffViewModels); + + if (this._revealFirst && firstChangeIndex !== -1) { + this._revealFirst = false; + this._list.setFocus([firstChangeIndex]); + this._list.reveal(firstChangeIndex, 0.3); + } } private _computeModifiedLCS(change: IDiffChange, originalModel: NotebookTextModel, modifiedModel: NotebookTextModel) { @@ -306,7 +328,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD layoutNotebookCell(cell: CellDiffViewModel, height: number) { const relayout = (cell: CellDiffViewModel, height: number) => { - const viewIndex = this._list!.indexOf(cell); + const viewIndex = this._list.indexOf(cell); this._list?.updateElementHeight(viewIndex, height); }; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 526e80d5..c2b2830c 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -211,6 +211,15 @@ max-width: 100%; } +.monaco-workbench .notebookOverlay .output-show-more-container { + position: absolute; +} + +.monaco-workbench .notebookOverlay .output-show-more-container p { + padding: 8px 8px 0 8px; + margin: 0px; +} + .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu { position: absolute; left: 0; @@ -258,8 +267,8 @@ /* top and bottom borders on cells */ .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before, +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { content: ""; position: absolute; width: 100%; @@ -268,35 +277,29 @@ /* top border */ .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before { border-top: 1px solid transparent; } -.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-top:before, -.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:before { - border-top-style: dashed; -} - /* bottom border */ .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { border-bottom: 1px solid transparent; } -.monaco-workbench.hc-black .notebookOverlay .monaco-list .monaco-list-row.focused.cell-editor-focus .cell-focus-indicator-bottom:before, -.monaco-workbench.hc-black .notebookOverlay .monaco-list .markdown-cell-row.focused.cell-editor-focus:after { - border-bottom-style: dashed; -} - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:before { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:before { top: 0; } .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before, -.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused:after { +.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row.focused .cell-inner-container:after { bottom: 0px; } +.monaco-workbench.hc-black .notebookOverlay .monaco-list-row .cell-editor-focus .cell-editor-part:before { + outline-style: dashed; +} + .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu.mouseover, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu:hover { cursor: pointer; @@ -433,9 +436,7 @@ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container { position: relative; - height: 16px; flex-shrink: 0; - top: 9px; z-index: 27; /* Above the drag handle */ } @@ -445,8 +446,8 @@ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .codicon { - margin: 0; - padding-right: 4px; + margin: 0 4px 0 0; + padding: 6px; } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .actions-container { @@ -479,7 +480,7 @@ } .monaco-workbench .notebookOverlay .cell .monaco-progress-container { - top: -5px; + top: -3px; position: absolute; left: 0; @@ -491,7 +492,8 @@ height: 2px; } -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell-has-toolbar-actions .cell-title-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list:focus-within > .monaco-scrollable-element > .monaco-list-rows:not(:hover) > .monaco-list-row.focused .cell-has-toolbar-actions .cell-title-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell-has-toolbar-actions .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions.cell-output-hover .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions:hover .cell-title-toolbar { visibility: visible; @@ -533,6 +535,11 @@ cursor: grab; } +.monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar.visible { + z-index: 25; + cursor: default; +} + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator .codicon:hover { cursor: pointer; } @@ -795,6 +802,7 @@ .monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon { visibility: visible; height: 16px; + padding: 4px; } /** Theming */ @@ -847,3 +855,11 @@ position: absolute; display: flex; } + +.output-show-more { + padding: 8px 0; +} + +.cell-contributed-items.cell-contributed-items-left { + margin-left: 4px; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 7cd90883..b3e9c30f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -241,7 +241,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri }); }, open: (editor, options, group) => { - return this.onEditorOpening2(editor, options, group); + return this.onEditorOpening(editor, options, group); } })); @@ -285,7 +285,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri return this.notebookService.getContributedNotebookProviders(resource); } - private onEditorOpening2(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { + private onEditorOpening(originalInput: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { let id = typeof options?.override === 'string' ? options.override : undefined; if (id === undefined && originalInput.resource?.scheme === Schemas.untitled) { @@ -304,6 +304,10 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri return undefined; } + if (originalInput instanceof NotebookDiffEditorInput) { + return undefined; + } + let notebookUri: URI = originalInput.resource; let cellOptions: IResourceEditorInput | undefined; @@ -319,7 +323,12 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri } if (id === undefined) { - const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, notebookUri) && !(editor instanceof NotebookEditorInput)); + const existingEditors = group.editors.filter(editor => + editor.resource + && isEqual(editor.resource, notebookUri) + && !(editor instanceof NotebookEditorInput) + && !(editor instanceof NotebookDiffEditorInput) + ); if (existingEditors.length) { return undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 0306ba95..1633a9f7 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -8,7 +8,7 @@ import { IListContextMenuEvent, IListEvent, IListMouseEvent } from 'vs/base/brow import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; @@ -60,6 +60,7 @@ export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey('notebo // Shared commands export const EXPAND_CELL_CONTENT_COMMAND_ID = 'notebook.cell.expandCellContent'; +export const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute'; // Kernels @@ -91,6 +92,8 @@ export interface CodeCellLayoutInfo { readonly totalHeight: number; readonly outputContainerOffset: number; readonly outputTotalHeight: number; + readonly outputShowMoreContainerHeight: number; + readonly outputShowMoreContainerOffset: number; readonly indicatorHeight: number; readonly bottomToolbarOffset: number; readonly layoutState: CodeCellLayoutState; @@ -99,6 +102,7 @@ export interface CodeCellLayoutInfo { export interface CodeCellLayoutChangeEvent { editorHeight?: boolean; outputHeight?: boolean; + outputShowMoreContainerHeight?: number; totalHeight?: boolean; outerWidth?: number; font?: BareFontInfo; @@ -203,6 +207,11 @@ export interface INotebookEditorCreationOptions { readonly contributions?: INotebookEditorContributionDescription[]; } +export interface IActiveNotebookEditor extends INotebookEditor { + viewModel: NotebookViewModel; + uri: URI; +} + export interface INotebookEditor extends IEditor { isEmbedded: boolean; @@ -212,6 +221,7 @@ export interface INotebookEditor extends IEditor { * Notebook view model attached to the current editor */ viewModel: NotebookViewModel | undefined; + hasModel(): this is IActiveNotebookEditor; /** * An event emitted when the model of this editor has changed. @@ -256,11 +266,17 @@ export interface INotebookEditor extends IEditor { * Layout info for the notebook editor */ getLayoutInfo(): NotebookLayoutInfo; + /** * Fetch the output renderers for notebook outputs. */ getOutputRenderer(): OutputRenderer; + /** + * Fetch the contributed kernels for this notebook + */ + beginComputeContributedKernels(): Promise; + /** * Insert a new cell around `cell` */ @@ -493,6 +509,7 @@ export interface INotebookCellList { revealElementInView(element: ICellViewModel): void; revealElementInCenterIfOutsideViewport(element: ICellViewModel): void; revealElementInCenter(element: ICellViewModel): void; + revealElementInCenterIfOutsideViewportAsync(element: ICellViewModel): Promise; revealElementLineInViewAsync(element: ICellViewModel, line: number): Promise; revealElementLineInCenterAsync(element: ICellViewModel, line: number): Promise; revealElementLineInCenterIfOutsideViewportAsync(element: ICellViewModel, line: number): Promise; @@ -552,6 +569,7 @@ export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { runButtonContainer: HTMLElement; executionOrderLabel: HTMLElement; outputContainer: HTMLElement; + outputShowMoreContainer: HTMLElement; focusSinkElement: HTMLElement; editor: ICodeEditor; progressBar: ProgressBar; @@ -708,3 +726,17 @@ export function getActiveNotebookEditor(editorService: IEditorService): INoteboo const activeEditorPane = editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined; return activeEditorPane?.isNotebookEditor ? (editorService.activeEditorPane?.getControl() as INotebookEditor) : undefined; } + +let EDITOR_TOP_PADDING = 12; +const editorTopPaddingChangeEmitter = new Emitter(); + +export const EditorTopPaddingChangeEvent = editorTopPaddingChangeEmitter.event; + +export function updateEditorTopPadding(top: number) { + EDITOR_TOP_PADDING = top; + editorTopPaddingChangeEmitter.fire(); +} + +export function getEditorTopPadding() { + return EDITOR_TOP_PADDING; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 55391ea4..3e8cd030 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -14,7 +14,6 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IReference } from 'vs/base/common/lifecycle'; import { INotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { IPathService } from 'vs/workbench/services/path/common/pathService'; interface NotebookEditorInputOptions { startDirty?: boolean; @@ -39,7 +38,6 @@ export class NotebookEditorInput extends EditorInput { @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, @IFilesConfigurationService private readonly _filesConfigurationService: IFilesConfigurationService, @IFileDialogService private readonly _fileDialogService: IFileDialogService, - @IPathService private readonly _pathService: IPathService, @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); @@ -145,7 +143,7 @@ ${patterns} } async suggestName(suggestedFilename: string) { - return joinPath(this._fileDialogService.defaultFilePath() || (await this._pathService.userHome()), suggestedFilename); + return joinPath(await this._fileDialogService.defaultFilePath(), suggestedFilename); } // called when users rename a notebook document diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 0aa5ef97..b58b545f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -9,7 +9,7 @@ import * as strings from 'vs/base/common/strings'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { IAction, Separator } from 'vs/base/common/actions'; -import { SequencerByKey } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, SequencerByKey } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -33,16 +33,16 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeground, focusBorder, foreground, listFocusBackground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; -import { IColorTheme, IThemeService, registerThemingParticipant, ThemeColor } from 'vs/platform/theme/common/themeService'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, IThemeService, registerThemingParticipant, ThemeColor, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento } from 'vs/workbench/common/editor'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { PANEL_BORDER } from 'vs/workbench/common/theme'; import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, ICellViewModel, INotebookCellList, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellViewModel, INotebookCellList, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; @@ -50,7 +50,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CodeCellRenderer, ListTopCellToolbar, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; -import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; +import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -61,6 +61,7 @@ import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookS import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { configureKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; const $ = DOM.$; @@ -76,7 +77,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _webviewResolved: boolean = false; private _webviewResolvePromise: Promise | null = null; private _webviewTransparentCover: HTMLElement | null = null; - private _list: INotebookCellList | undefined; + private _list!: INotebookCellList; private _dndController: CellDragAndDropController | null = null; private _listTopCellToolbar: ListTopCellToolbar | null = null; private _renderedEditors: Map = new Map(); @@ -108,9 +109,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _insetModifyQueueByOutputId = new SequencerByKey(); set scrollTop(top: number) { - if (this._list) { - this._list.scrollTop = top; - } + this._list.scrollTop = top; } private _cellContextKeyManager: CellContextKeyManager | null = null; @@ -153,6 +152,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private readonly _onDidChangeAvailableKernels = this._register(new Emitter()); readonly onDidChangeAvailableKernels: Event = this._onDidChangeAvailableKernels.event; + private _contributedKernelsComputePromise: CancelablePromise | null = null; + get activeKernel() { return this._activeKernel; } @@ -162,6 +163,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } + if (!this.viewModel) { + return; + } + if (this._activeKernel === kernel) { return; } @@ -169,8 +174,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._activeKernel = kernel; this._activeKernelResolvePromise = undefined; - const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL); - memento[this.viewModel!.viewType] = this._activeKernel?.id; + const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + memento[this.viewModel.viewType] = this._activeKernel?.id; this._activeKernelMemento.saveMemento(); this._onDidChangeKernel.fire(); } @@ -197,7 +202,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - const [focused] = this._list!.getFocusedElements(); + const [focused] = this._list.getFocusedElements(); return this._renderedEditors.get(focused); } @@ -224,7 +229,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor onDidChangeVisibleRanges: Event = this._onDidChangeVisibleRanges.event; get visibleRanges() { - return this._list?.visibleRanges || []; + return this._list.visibleRanges || []; } readonly isEmbedded: boolean; @@ -273,6 +278,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor }); this.notebookService.addNotebookEditor(this); + this._createEditor(); } /** @@ -286,7 +292,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this.viewModel?.selectionHandles || []; } - hasModel() { + hasModel(): this is IActiveNotebookEditor { return !!this._notebookViewModel; } @@ -305,7 +311,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } protected getMemento(scope: StorageScope): MementoObject { - return this._memento.getMemento(scope); + return this._memento.getMemento(scope, StorageTarget.MACHINE); } public get isNotebookEditor() { @@ -335,7 +341,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // a descendent of the notebook editor root. const focused = DOM.isAncestor(document.activeElement, this._overlayContainer); this._editorFocus?.set(focused); - this._notebookViewModel?.setFocus(focused); + this.viewModel?.setFocus(focused); } hasFocus() { @@ -380,7 +386,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return false; } - createEditor(): void { + private _createEditor(): void { const id = generateUuid(); this._overlayContainer.id = `notebook-${id}`; this._overlayContainer.className = 'notebookOverlay'; @@ -440,7 +446,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._body.classList.add('cell-list-container'); this._dndController = this._register(new CellDragAndDropController(this, this._body)); - const getScopedContextKeyService = (container?: HTMLElement) => this._list!.contextKeyService.createScoped(container); + const getScopedContextKeyService = (container?: HTMLElement) => this._list.contextKeyService.createScoped(container); const renderers = [ this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService), this.instantiationService.createInstance(MarkdownCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService), @@ -465,7 +471,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor enableKeyboardNavigation: true, additionalScrollHeight: 0, transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', - styleController: (_suffix: string) => { return this._list!; }, + styleController: (_suffix: string) => { return this._list; }, overrideStyles: { listBackground: editorBackground, listActiveSelectionBackground: editorBackground, @@ -583,7 +589,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // Will fire onDidChangeFocus, resetting the state to Container applyFocusChange(); - const newFocusedCell = this._list!.getFocusedElements()[0]; + const newFocusedCell = this._list.getFocusedElements()[0]; if (newFocusedCell.cellKind === CellKind.Code || newFocusedCell.editState === CellEditState.Editing) { this.focusNotebookCell(newFocusedCell, 'editor'); } else { @@ -621,9 +627,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (this._webiewFocused) { this._webview?.focusWebview(); } else { - const focus = this._list?.getFocus()[0]; - if (typeof focus === 'number') { - const element = this._notebookViewModel!.viewCells[focus]; + const focus = this._list.getFocus()[0]; + if (typeof focus === 'number' && this.viewModel) { + const element = this.viewModel.viewCells[focus]; if (element.focusMode === CellFocusMode.Editor) { element.editState = CellEditState.Editing; @@ -633,7 +639,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - this._list?.domFocus(); + this._list.domFocus(); } this._onDidFocusEditorWidget.fire(); @@ -644,7 +650,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async setModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined): Promise { - if (this._notebookViewModel === undefined || !this._notebookViewModel.equal(textModel)) { + if (this.viewModel === undefined || !this.viewModel.equal(textModel)) { this._detachModel(); await this._attachModel(textModel, viewState); } else { @@ -657,7 +663,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._currentKernelTokenSource = new CancellationTokenSource(); this._localStore.add(this._currentKernelTokenSource); // we don't await for it, otherwise it will slow down the file opening - this._setKernels(textModel, this._currentKernelTokenSource); + this._setKernels(this._currentKernelTokenSource); this._localStore.add(this.notebookService.onDidChangeKernels(async (e) => { if (e && e.toString() !== this.textModel?.uri.toString()) { @@ -666,11 +672,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } this._currentKernelTokenSource?.cancel(); this._currentKernelTokenSource = new CancellationTokenSource(); - await this._setKernels(textModel, this._currentKernelTokenSource); + await this._setKernels(this._currentKernelTokenSource); })); - this._localStore.add(this._list!.onDidChangeFocus(() => { - const focused = this._list!.getFocusedElements()[0]; + this._localStore.add(this._list.onDidChangeFocus(() => { + const focused = this._list.getFocusedElements()[0]; if (focused) { if (!this._cellContextKeyManager) { this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.scopedContextKeyService, this, textModel, focused as CellViewModel)); @@ -683,12 +689,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor async setOptions(options: NotebookEditorOptions | undefined) { // reveal cell if editor options tell to do so - if (options?.cellOptions) { + if (options?.cellOptions && this.viewModel) { const cellOptions = options.cellOptions; - const cell = this._notebookViewModel!.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString()); + const cell = this.viewModel.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString()); if (cell) { this.selectElement(cell); - this.revealInCenterIfOutsideViewport(cell); + await this.revealInCenterIfOutsideViewportAsync(cell); const editor = this._renderedEditors.get(cell)!; if (editor) { if (cellOptions.options?.selection) { @@ -714,21 +720,40 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _detachModel() { this._localStore.clear(); - this._list?.detachViewModel(); + this._list.detachViewModel(); this.viewModel?.dispose(); // avoid event - this._notebookViewModel = undefined; + this.viewModel = undefined; // this.webview?.clearInsets(); // this.webview?.clearPreloadsCache(); this._webview?.dispose(); this._webview?.element.remove(); this._webview = null; - this._list?.clear(); + this._list.clear(); } - private async _setKernels(textModel: NotebookTextModel, tokenSource: CancellationTokenSource) { - const provider = this.notebookService.getContributedNotebookProvider(textModel.viewType) || this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0]; - const availableKernels2 = await this.notebookService.getContributedNotebookKernels2(textModel.viewType, textModel.uri, tokenSource.token); + async beginComputeContributedKernels() { + if (this._contributedKernelsComputePromise) { + return this._contributedKernelsComputePromise; + } + + this._contributedKernelsComputePromise = createCancelablePromise(token => { + return this.notebookService.getContributedNotebookKernels(this.viewModel!.viewType, this.viewModel!.uri, token); + }); + + const result = await this._contributedKernelsComputePromise; + this._contributedKernelsComputePromise = null; + + return result; + } + + private async _setKernels(tokenSource: CancellationTokenSource) { + if (!this.viewModel) { + return; + } + + const provider = this.notebookService.getContributedNotebookProvider(this.viewModel.viewType) || this.notebookService.getContributedNotebookProviders(this.viewModel.uri)[0]; + const availableKernels = await this.beginComputeContributedKernels(); if (tokenSource.token.isCancellationRequested) { return; @@ -738,7 +763,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - if ((availableKernels2.length) > 1) { + if ((availableKernels.length) > 1) { this._notebookHasMultipleKernels!.set(true); this.multipleKernelsAvailable = true; } else { @@ -746,30 +771,24 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.multipleKernelsAvailable = false; } - const activeKernelStillExist = [...availableKernels2].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); + const activeKernelStillExist = [...availableKernels].find(kernel => kernel.id === this.activeKernel?.id && this.activeKernel?.id !== undefined); if (activeKernelStillExist) { // the kernel still exist, we don't want to modify the selection otherwise user's temporary preference is lost return; } - if (availableKernels2.length) { - return this._setKernelsFromProviders(provider, availableKernels2, tokenSource); + if (availableKernels.length) { + return this._setKernelsFromProviders(provider, availableKernels, tokenSource); } - // the provider doesn't have a builtin kernel, choose a kernel - // this.activeKernel = availableKernels[0]; - // if (this.activeKernel) { - // await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); - // } - tokenSource.dispose(); } private async _setKernelsFromProviders(provider: NotebookProviderInfo, kernels: INotebookKernelInfo2[], tokenSource: CancellationTokenSource) { const rawAssociations = this.configurationService.getValue(notebookKernelProviderAssociationsSettingId) || []; const userSetKernelProvider = rawAssociations.filter(e => e.viewType === this.viewModel?.viewType)[0]?.kernelProvider; - const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL); + const memento = this._activeKernelMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); if (userSetKernelProvider) { const filteredKernels = kernels.filter(kernel => kernel.extension.value === userSetKernelProvider); @@ -860,9 +879,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } private _updateForMetadata(): void { - const notebookMetadata = this.viewModel!.metadata; + if (!this.viewModel) { + return; + } + + const notebookMetadata = this.viewModel.metadata; this._editorEditable?.set(!!notebookMetadata?.editable); - this._editorRunnable?.set(!!notebookMetadata?.runnable); + this._editorRunnable?.set(this.viewModel.runnable); this._overflowContainer.classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); this.getDomNode().classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); @@ -881,12 +904,20 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (!this._webview) { this._webview = this.instantiationService.createInstance(BackLayerWebView, this, this.getId(), this.textModel!.uri); // attach the webview container to the DOM tree first - this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); + this._list.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); } this._webviewResolvePromise = new Promise(async resolve => { - await this._webview!.createWebview(); - this._webview!.webview!.onDidBlur(() => { + if (!this._webview) { + throw new Error('Notebook output webview object is not created successfully.'); + } + + await this._webview.createWebview(); + if (!this._webview.webview) { + throw new Error('Notebook output webview elemented is not created successfully.'); + } + + this._webview.webview.onDidBlur(() => { this._outputFocus?.set(false); this.updateEditorFocus(); @@ -894,7 +925,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webiewFocused = false; } }); - this._webview!.webview!.onDidFocus(() => { + this._webview.webview.onDidFocus(() => { this._outputFocus?.set(true); this.updateEditorFocus(); this._onDidFocusEmitter.fire(); @@ -904,7 +935,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } }); - this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => { + this._localStore.add(this._webview.onMessage(({ message, forRenderer }) => { if (this.viewModel) { this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message); } @@ -912,7 +943,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webviewResolved = true; - resolve(this._webview!); + resolve(this._webview); }); return this._webviewResolvePromise; @@ -921,7 +952,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private async _createWebview(id: string, resource: URI): Promise { this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource); // attach the webview container to the DOM tree first - this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); + this._list.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); } private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) { @@ -959,7 +990,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._onDidChangeSelection.fire(); })); - this._localStore.add(this._list!.onWillScroll(e => { + this._localStore.add(this._list.onWillScroll(e => { this._onWillScroll.fire(e); if (!this._webviewResolved) { return; @@ -969,14 +1000,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webviewTransparentCover!.style.top = `${e.scrollTop}px`; })); - this._localStore.add(this._list!.onDidChangeContentHeight(() => { + this._localStore.add(this._list.onDidChangeContentHeight(() => { DOM.scheduleAtNextAnimationFrame(() => { if (this._isDisposed) { return; } - const scrollTop = this._list?.scrollTop || 0; - const scrollHeight = this._list?.scrollHeight || 0; + const scrollTop = this._list.scrollTop; + const scrollHeight = this._list.scrollHeight; if (!this._webviewResolved) { return; @@ -989,7 +1020,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const removedItems: IProcessedOutput[] = []; this._webview?.insetMapping.forEach((value, key) => { const cell = value.cell; - const viewIndex = this._list?.getViewIndex(cell); + const viewIndex = this._list.getViewIndex(cell); if (viewIndex === undefined) { return; @@ -1000,7 +1031,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor removedItems.push(key); } - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + const cellTop = this._list.getAbsoluteTopOfElement(cell); if (this._webview!.shouldUpdateInset(cell, key, cellTop)) { updateItems.push({ cell: cell, @@ -1019,18 +1050,18 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor }); })); - this._list!.attachViewModel(this.viewModel); - this._localStore.add(this._list!.onDidRemoveOutput(output => { + this._list.attachViewModel(this.viewModel); + this._localStore.add(this._list.onDidRemoveOutput(output => { this.removeInset(output); })); - this._localStore.add(this._list!.onDidHideOutput(output => { + this._localStore.add(this._list.onDidHideOutput(output => { this.hideInset(output); })); if (this._dimension) { - this._list?.layout(this._dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, this._dimension.width); + this._list.layout(this._dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, this._dimension.width); } else { - this._list!.layout(); + this._list.layout(); } this._dndController?.clearGlobalDragState(); @@ -1041,23 +1072,23 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor restoreListViewState(viewState: INotebookEditorViewState | undefined): void { if (viewState?.scrollPosition !== undefined) { - this._list!.scrollTop = viewState!.scrollPosition.top; - this._list!.scrollLeft = viewState!.scrollPosition.left; + this._list.scrollTop = viewState!.scrollPosition.top; + this._list.scrollLeft = viewState!.scrollPosition.left; } else { - this._list!.scrollTop = 0; - this._list!.scrollLeft = 0; + this._list.scrollTop = 0; + this._list.scrollLeft = 0; } const focusIdx = typeof viewState?.focus === 'number' ? viewState.focus : 0; - if (focusIdx < this._list!.length) { - this._list!.setFocus([focusIdx]); - this._list!.setSelection([focusIdx]); - } else if (this._list!.length > 0) { - this._list!.setFocus([0]); + if (focusIdx < this._list.length) { + this._list.setFocus([focusIdx]); + this._list.setSelection([focusIdx]); + } else if (this._list.length > 0) { + this._list.setFocus([0]); } if (viewState?.editorFocused) { - const cell = this._notebookViewModel?.viewCells[focusIdx]; + const cell = this.viewModel?.viewCells[focusIdx]; if (cell) { cell.focusMode = CellFocusMode.Editor; } @@ -1065,7 +1096,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } getEditorViewState(): INotebookEditorViewState { - const state = this._notebookViewModel?.getEditorViewState(); + const state = this.viewModel?.getEditorViewState(); if (!state) { return { editingCells: {}, @@ -1088,10 +1119,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor state.cellTotalHeights = cellHeights; const focus = this._list.getFocus()[0]; - if (typeof focus === 'number') { - const element = this._notebookViewModel!.viewCells[focus]; + if (typeof focus === 'number' && this.viewModel) { + const element = this.viewModel.viewCells[focus]; if (element) { - const itemDOM = this._list?.domElementOfElement(element); + const itemDOM = this._list.domElementOfElement(element); const editorFocused = !!(document.activeElement && itemDOM && itemDOM.contains(document.activeElement)); state.editorFocused = editorFocused; @@ -1143,8 +1174,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._dimension = new DOM.Dimension(dimension.width, dimension.height); DOM.size(this._body, dimension.width, dimension.height); - this._list?.updateOptions({ additionalScrollHeight: this._scrollBeyondLastLine ? dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP : 0 }); - this._list?.layout(dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, dimension.width); + this._list.updateOptions({ additionalScrollHeight: this._scrollBeyondLastLine ? dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP : 0 }); + this._list.layout(dimension.height - SCROLLABLE_ELEMENT_PADDING_TOP, dimension.width); this._overlayContainer.style.visibility = 'visible'; this._overlayContainer.style.display = 'block'; @@ -1177,56 +1208,60 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region Editor Features selectElement(cell: ICellViewModel) { - this._list?.selectElement(cell); + this._list.selectElement(cell); // this.viewModel!.selectionHandles = [cell.handle]; } revealInView(cell: ICellViewModel) { - this._list?.revealElementInView(cell); + this._list.revealElementInView(cell); } revealInCenterIfOutsideViewport(cell: ICellViewModel) { - this._list?.revealElementInCenterIfOutsideViewport(cell); + this._list.revealElementInCenterIfOutsideViewport(cell); + } + + async revealInCenterIfOutsideViewportAsync(cell: ICellViewModel) { + return this._list.revealElementInCenterIfOutsideViewportAsync(cell); } revealInCenter(cell: ICellViewModel) { - this._list?.revealElementInCenter(cell); + this._list.revealElementInCenter(cell); } async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { - return this._list?.revealElementLineInViewAsync(cell, line); + return this._list.revealElementLineInViewAsync(cell, line); } async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise { - return this._list?.revealElementLineInCenterAsync(cell, line); + return this._list.revealElementLineInCenterAsync(cell, line); } async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise { - return this._list?.revealElementLineInCenterIfOutsideViewportAsync(cell, line); + return this._list.revealElementLineInCenterIfOutsideViewportAsync(cell, line); } async revealRangeInViewAsync(cell: ICellViewModel, range: Range): Promise { - return this._list?.revealElementRangeInViewAsync(cell, range); + return this._list.revealElementRangeInViewAsync(cell, range); } async revealRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise { - return this._list?.revealElementRangeInCenterAsync(cell, range); + return this._list.revealElementRangeInCenterAsync(cell, range); } async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise { - return this._list?.revealElementRangeInCenterIfOutsideViewportAsync(cell, range); + return this._list.revealElementRangeInCenterIfOutsideViewportAsync(cell, range); } setCellSelection(cell: ICellViewModel, range: Range): void { - this._list?.setCellSelection(cell, range); + this._list.setCellSelection(cell, range); } changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { - return this._notebookViewModel?.changeModelDecorations(callback) || null; + return this.viewModel?.changeModelDecorations(callback) || null; } setHiddenAreas(_ranges: ICellRange[]): boolean { - return this._list!.setHiddenAreas(_ranges, true); + return this._list.setHiddenAreas(_ranges, true); } private _editorStyleSheets = new Map(); @@ -1301,7 +1336,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region Cell operations async layoutNotebookCell(cell: ICellViewModel, height: number): Promise { - const viewIndex = this._list!.getViewIndex(cell); + const viewIndex = this._list.getViewIndex(cell); if (viewIndex === undefined) { // the cell is hidden return; @@ -1312,7 +1347,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - this._list?.updateElementHeight2(cell, height); + this._list.updateElementHeight2(cell, height); }; if (this.pendingLayouts.has(cell)) { @@ -1339,42 +1374,90 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return new Promise(resolve => { r = resolve; }); } + private _nearestCodeCellIndex(index: number /* exclusive */, direction: 'above' | 'below') { + if (!this.viewModel) { + return -1; + } + + const nearest = this.viewModel.viewCells.slice(0, index).reverse().findIndex(cell => cell.cellKind === CellKind.Code); + if (nearest > -1) { + return index - nearest - 1; + } else { + const nearestCellTheOtherDirection = this.viewModel.viewCells.slice(index + 1).findIndex(cell => cell.cellKind === CellKind.Code); + if (nearestCellTheOtherDirection > -1) { + return index + nearestCellTheOtherDirection; + } + return -1; + } + } + insertNotebookCell(cell: ICellViewModel | undefined, type: CellKind, direction: 'above' | 'below' = 'above', initialText: string = '', ui: boolean = false): CellViewModel | null { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = cell ? this._notebookViewModel!.getCellIndex(cell) : 0; - const nextIndex = ui ? this._notebookViewModel!.getNextVisibleCellIndex(index) : index + 1; - const newLanguages = this._notebookViewModel!.resolvedLanguages; - const language = (cell?.cellKind === CellKind.Code && type === CellKind.Code) - ? cell.language - : ((type === CellKind.Code && newLanguages && newLanguages.length) ? newLanguages[0] : 'markdown'); + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = cell ? this.viewModel.getCellIndex(cell) : 0; + const nextIndex = ui ? this.viewModel.getNextVisibleCellIndex(index) : index + 1; + let language; + if (type === CellKind.Code) { + if (cell?.cellKind === CellKind.Code) { + language = cell.language; + } else if (cell?.cellKind === CellKind.Markdown) { + const nearestCodeCellIndex = this._nearestCodeCellIndex(index, direction); + if (nearestCodeCellIndex > -1) { + language = this.viewModel.viewCells[nearestCodeCellIndex].language; + } else { + language = this.viewModel.resolvedLanguages[0] || 'plaintext'; + } + } else { + if (cell === undefined && direction === 'above') { + // insert cell at the very top + language = this.viewModel.viewCells.find(cell => cell.cellKind === CellKind.Code)?.language || this.viewModel.resolvedLanguages[0] || 'plaintext'; + } else { + language = this.viewModel.resolvedLanguages[0] || 'plaintext'; + } + } + } else { + language = 'markdown'; + } + const insertIndex = cell ? (direction === 'above' ? index : nextIndex) : index; - const focused = this._list?.getFocusedElements(); - const newCell = this._notebookViewModel!.createCell(insertIndex, initialText, language, type, undefined, [], true, undefined, focused); + const focused = this._list.getFocusedElements(); + const newCell = this.viewModel.createCell(insertIndex, initialText, language, type, undefined, [], true, undefined, focused); return newCell as CellViewModel; } async splitNotebookCell(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); + if (!this.viewModel.metadata.editable) { + return null; + } - return this._notebookViewModel!.splitNotebookCell(index); + const index = this.viewModel.getCellIndex(cell); + + return this.viewModel.splitNotebookCell(index); } async joinNotebookCells(cell: ICellViewModel, direction: 'above' | 'below', constraint?: CellKind): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); - const ret = await this._notebookViewModel!.joinNotebookCells(index, direction, constraint); + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = this.viewModel.getCellIndex(cell); + const ret = await this.viewModel.joinNotebookCells(index, direction, constraint); if (ret) { ret.deletedCells.forEach(cell => { @@ -1390,7 +1473,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async deleteNotebookCell(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { + return false; + } + + if (!this.viewModel.metadata.editable) { return false; } @@ -1398,18 +1485,22 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.pendingLayouts.get(cell)!.dispose(); } - const index = this._notebookViewModel!.getCellIndex(cell); - this._notebookViewModel!.deleteCell(index, true); + const index = this.viewModel.getCellIndex(cell); + this.viewModel.deleteCell(index, true); return true; } async moveCellDown(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); - if (index === this._notebookViewModel!.length - 1) { + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = this.viewModel.getCellIndex(cell); + if (index === this.viewModel.length - 1) { return null; } @@ -1418,11 +1509,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async moveCellUp(cell: ICellViewModel): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { return null; } - const index = this._notebookViewModel!.getCellIndex(cell); + if (!this.viewModel.metadata.editable) { + return null; + } + + const index = this.viewModel.getCellIndex(cell); if (index === 0) { return null; } @@ -1432,7 +1527,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { + return null; + } + + if (!this.viewModel.metadata.editable) { return null; } @@ -1440,15 +1539,19 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return null; } - const originalIdx = this._notebookViewModel!.getCellIndex(cell); - const relativeToIndex = this._notebookViewModel!.getCellIndex(relativeToCell); + const originalIdx = this.viewModel.getCellIndex(cell); + const relativeToIndex = this.viewModel.getCellIndex(relativeToCell); const newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1; return this._moveCellToIndex(originalIdx, 1, newIdx); } async moveCellsToIdx(index: number, length: number, toIdx: number): Promise { - if (!this._notebookViewModel!.metadata.editable) { + if (!this.viewModel) { + return null; + } + + if (!this.viewModel.metadata.editable) { return null; } @@ -1461,6 +1564,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor * @example to move the cell from index 0 down one spot, call with (0, 2) */ private async _moveCellToIndex(index: number, length: number, desiredIndex: number): Promise { + if (!this.viewModel) { + return null; + } + if (index < desiredIndex) { // The cell is moving "down", it will free up one index spot and consume a new one desiredIndex -= length; @@ -1470,7 +1577,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return null; } - if (!this._notebookViewModel!.moveCellToIdx(index, length, desiredIndex, true)) { + if (!this.viewModel.moveCellToIdx(index, length, desiredIndex, true)) { throw new Error('Notebook Editor move cell, index out of range'); } @@ -1478,10 +1585,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor DOM.scheduleAtNextAnimationFrame(() => { if (this._isDisposed) { r(null); + return; } - const viewCell = this._notebookViewModel!.viewCells[desiredIndex]; - this._list?.revealElementInView(viewCell); + if (!this.viewModel) { + r(null); + return; + } + + const viewCell = this.viewModel.viewCells[desiredIndex]; + this._list.revealElementInView(viewCell); r(viewCell); }); @@ -1489,7 +1602,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } editNotebookCell(cell: CellViewModel): void { - if (!cell.getEvaluatedMetadata(this._notebookViewModel!.metadata).editable) { + if (!this.viewModel) { + return; + } + + if (!cell.getEvaluatedMetadata(this.viewModel.metadata).editable) { return; } @@ -1499,7 +1616,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } getActiveCell() { - const elements = this._list?.getFocusedElements(); + const elements = this._list.getFocusedElements(); if (elements && elements.length) { return elements[0]; @@ -1519,9 +1636,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // pick active kernel + const picker = this.quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); + picker.placeholder = nls.localize('notebook.runCell.selectKernel', "Select a notebook kernel to run this notebook"); + picker.matchOnDetail = true; + picker.busy = true; + picker.show(); + const tokenSource = new CancellationTokenSource(); - const availableKernels2 = await this.notebookService.getContributedNotebookKernels2(this.viewModel!.viewType, this.viewModel!.uri, tokenSource.token); - const picks: QuickPickInput[] = availableKernels2.map((a) => { + const availableKernels = await this.beginComputeContributedKernels(); + const picks: QuickPickInput[] = availableKernels.map((a) => { return { id: a.id, label: a.label, @@ -1537,16 +1660,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._activeKernelResolvePromise = this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); }, buttons: [{ - iconClass: 'codicon-settings-gear', + iconClass: ThemeIcon.asClassName(configureKernelIcon), tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default kernel provider for '{0}'", this.viewModel!.viewType) }] }; }); - const picker = this.quickInputService.createQuickPick<(IQuickPickItem & { run(): void; kernelProviderId?: string })>(); picker.items = picks; - picker.placeholder = nls.localize('notebook.runCell.selectKernel', "Select a notebook kernel to run this notebook"); - picker.matchOnDetail = true; + picker.busy = false; const pickedItem = await new Promise<(IQuickPickItem & { run(): void; kernelProviderId?: string; }) | undefined>(resolve => { picker.onDidAccept(() => { @@ -1581,7 +1702,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } }); - picker.show(); }); tokenSource.dispose(); @@ -1594,29 +1714,41 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async cancelNotebookExecution(): Promise { - if (this._notebookViewModel?.metadata.runState !== NotebookRunState.Running) { + if (!this.viewModel) { + return; + } + + if (this.viewModel.metadata.runState !== NotebookRunState.Running) { return; } await this._ensureActiveKernel(); - await this._activeKernel?.cancelNotebookCell!(this._notebookViewModel!.uri, undefined); + await this._activeKernel?.cancelNotebookCell!(this.viewModel.uri, undefined); } async executeNotebook(): Promise { - if (!this._notebookViewModel!.metadata.runnable) { + if (!this.viewModel) { + return; + } + + if (!this.viewModel.runnable) { return; } await this._ensureActiveKernel(); - await this._activeKernel?.executeNotebookCell!(this._notebookViewModel!.uri, undefined); + await this._activeKernel?.executeNotebookCell!(this.viewModel.uri, undefined); } async cancelNotebookCellExecution(cell: ICellViewModel): Promise { + if (!this.viewModel) { + return; + } + if (cell.cellKind !== CellKind.Code) { return; } - const metadata = cell.getEvaluatedMetadata(this._notebookViewModel!.metadata); + const metadata = cell.getEvaluatedMetadata(this.viewModel.metadata); if (!metadata.runnable) { return; } @@ -1626,21 +1758,25 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } await this._ensureActiveKernel(); - await this._activeKernel?.cancelNotebookCell!(this._notebookViewModel!.uri, cell.handle); + await this._activeKernel?.cancelNotebookCell!(this.viewModel.uri, cell.handle); } async executeNotebookCell(cell: ICellViewModel): Promise { + if (!this.viewModel) { + return; + } + if (cell.cellKind === CellKind.Markdown) { this.focusNotebookCell(cell, 'container'); return; } - if (!cell.getEvaluatedMetadata(this._notebookViewModel!.metadata).runnable) { + if (!cell.getEvaluatedMetadata(this.viewModel.metadata).runnable) { return; } await this._ensureActiveKernel(); - await this._activeKernel?.executeNotebookCell!(this._notebookViewModel!.uri, cell.handle); + await this._activeKernel?.executeNotebookCell!(this.viewModel.uri, cell.handle); } focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') { @@ -1650,14 +1786,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (focusItem === 'editor') { this.selectElement(cell); - this._list?.focusView(); + this._list.focusView(); cell.editState = CellEditState.Editing; cell.focusMode = CellFocusMode.Editor; this.revealInCenterIfOutsideViewport(cell); } else if (focusItem === 'output') { this.selectElement(cell); - this._list?.focusView(); + this._list.focusView(); if (!this._webview) { return; @@ -1668,7 +1804,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor cell.focusMode = CellFocusMode.Container; this.revealInCenterIfOutsideViewport(cell); } else { - const itemDOM = this._list?.domElementOfElement(cell); + const itemDOM = this._list.domElementOfElement(cell); if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) { (document.activeElement as HTMLElement).blur(); } @@ -1678,7 +1814,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.selectElement(cell); this.revealInCenterIfOutsideViewport(cell); - this._list?.focusView(); + this._list.focusView(); } } @@ -1687,7 +1823,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#region MISC deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { - return this._notebookViewModel?.deltaCellDecorations(oldDecorations, newDecorations) || []; + return this.viewModel?.deltaCellDecorations(oldDecorations, newDecorations) || []; } deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]) { @@ -1707,7 +1843,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } triggerScroll(event: IMouseWheelEvent) { - this._list?.triggerScrollFromMouseWheelEvent(event); + this._list.triggerScrollFromMouseWheelEvent(event); } async createInset(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise { @@ -1719,11 +1855,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor await this._resolveWebview(); if (!this._webview!.insetMapping.has(output.source)) { - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; + const cellTop = this._list.getAbsoluteTopOfElement(cell); await this._webview!.createInset(cell, output, cellTop, offset); } else { - const cellTop = this._list?.getAbsoluteTopOfElement(cell) || 0; - const scrollTop = this._list?.scrollTop || 0; + const cellTop = this._list.getAbsoluteTopOfElement(cell); + const scrollTop = this._list.scrollTop; this._webview!.updateViewScrollTop(-scrollTop, true, [{ cell, output: output.source, cellTop }]); } @@ -1809,7 +1945,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } this._localStore.clear(); - this._list?.dispose(); + this._list.dispose(); this._listTopCellToolbar?.dispose(); this._overlayContainer.remove(); @@ -1828,7 +1964,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } export const notebookCellBorder = registerColor('notebook.cellBorderColor', { - dark: transparent(PANEL_BORDER, .4), + dark: transparent(listInactiveSelectionBackground, 1), light: transparent(listInactiveSelectionBackground, 1), hc: PANEL_BORDER }, nls.localize('notebook.cellBorderColor', "The border color for notebook cells.")); @@ -1859,7 +1995,7 @@ export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.fo export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', { dark: notebookCellBorder, - light: transparent(listFocusBackground, .4), + light: notebookCellBorder, hc: null }, nls.localize('notebook.outputContainerBackgroundColor', "The Color of the notebook output container background.")); @@ -1871,8 +2007,8 @@ export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparat }, nls.localize('notebook.cellToolbarSeparator', "The color of the separator in the cell bottom toolbar")); export const focusedCellBackground = registerColor('notebook.focusedCellBackground', { - dark: transparent(PANEL_BORDER, .4), - light: transparent(listFocusBackground, .4), + dark: null, + light: null, hc: null }, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused.")); @@ -1882,9 +2018,15 @@ export const cellHoverBackground = registerColor('notebook.cellHoverBackground', hc: null }, nls.localize('notebook.cellHoverBackground', "The background color of a cell when the cell is hovered.")); +export const selectedCellBorder = registerColor('notebook.selectedCellBorder', { + dark: notebookCellBorder, + light: notebookCellBorder, + hc: contrastBorder +}, nls.localize('notebook.selectedCellBorder', "The color of the cell's top and bottom border when the cell is selected but not focusd.")); + export const focusedCellBorder = registerColor('notebook.focusedCellBorder', { - dark: Color.white.transparent(0.12), - light: Color.black.transparent(0.12), + dark: focusBorder, + light: focusBorder, hc: focusBorder }, nls.localize('notebook.focusedCellBorder', "The color of the cell's top and bottom border when the cell is focused.")); @@ -1935,12 +2077,17 @@ registerThemingParticipant((theme, collector) => { const link = theme.getColor(textLinkForeground); if (link) { collector.addRule(`.notebookOverlay .output a, - .notebookOverlay .cell.markdown a { color: ${link};} `); + .notebookOverlay .cell.markdown a, + .notebookOverlay .output-show-more-container a + { color: ${link};} `); + } const activeLink = theme.getColor(textLinkActiveForeground); if (activeLink) { collector.addRule(`.notebookOverlay .output a:hover, - .notebookOverlay .cell .output a:active { color: ${activeLink}; }`); + .notebookOverlay .cell .output a:active, + .notebookOverlay .output-show-more-container a:active + { color: ${activeLink}; }`); } const shortcut = theme.getColor(textPreformatForeground); if (shortcut) { @@ -1964,6 +2111,7 @@ registerThemingParticipant((theme, collector) => { if (containerBackground) { collector.addRule(`.notebookOverlay .output { background-color: ${containerBackground}; }`); collector.addRule(`.notebookOverlay .output-element { background-color: ${containerBackground}; }`); + collector.addRule(`.notebookOverlay .output-show-more-container { background-color: ${containerBackground}; }`); } const editorBackgroundColor = theme.getColor(editorBackground); @@ -2005,13 +2153,23 @@ registerThemingParticipant((theme, collector) => { } const focusedCellBorderColor = theme.getColor(focusedCellBorder); - collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before, + collector.addRule(` + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before, .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-bottom:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused:before, - .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused:after { + .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):after { border-color: ${focusedCellBorderColor} !important; }`); + const selectedCellBorderColor = theme.getColor(selectedCellBorder); + collector.addRule(` + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-top:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-bottom:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:before, + .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:after { + border-color: ${selectedCellBorderColor} !important; + }`); + const cellSymbolHighlightColor = theme.getColor(cellSymbolHighlight); if (cellSymbolHighlightColor) { collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-symbolHighlight .cell-focus-indicator, @@ -2033,12 +2191,12 @@ registerThemingParticipant((theme, collector) => { const cellStatusSuccessIcon = theme.getColor(cellStatusIconSuccess); if (cellStatusSuccessIcon) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-check { color: ${cellStatusSuccessIcon} }`); + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-notebook-state-success { color: ${cellStatusSuccessIcon} }`); } const cellStatusErrorIcon = theme.getColor(cellStatusIconError); if (cellStatusErrorIcon) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-error { color: ${cellStatusErrorIcon} }`); + collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-notebook-state-error { color: ${cellStatusErrorIcon} }`); } const cellStatusRunningIcon = theme.getColor(cellStatusIconRunning); @@ -2123,6 +2281,9 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); + collector.addRule(`.notebookOverlay .output-show-more-container { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay .output-show-more-container { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; margin: 0px ${Math.floor(CELL_RUN_GUTTER - 20) / 2}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts index 783b8207..f9650ad0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidgetService.ts @@ -127,7 +127,6 @@ class NotebookEditorWidgetService implements INotebookEditorWidgetService { // NEW widget const instantiationService = accessor.get(IInstantiationService); const widget = instantiationService.createInstance(NotebookEditorWidget, { isEmbedded: false }); - widget.createEditor(); const token = this._tokenPool++; value = { widget, token }; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts new file mode 100644 index 00000000..6780f1fa --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const configureKernelIcon = registerIcon('notebook-kernel-configure', Codicon.settingsGear, localize('configureKernel', 'Configure icon in kernel configuation widget in notebook editors.')); +export const selectKernelIcon = registerIcon('notebook-kernel-select', Codicon.serverEnvironment, localize('selectKernelIcon', 'Configure icon to select a kernel in notebook editors.')); + +export const executeIcon = registerIcon('notebook-execute', Codicon.play, localize('executeIcon', 'Icon to execute in notebook editors.')); +export const stopIcon = registerIcon('notebook-stop', Codicon.primitiveSquare, localize('stopIcon', 'Icon to stop an execution in notebook editors.')); +export const deleteCellIcon = registerIcon('notebook-delete-cell', Codicon.trash, localize('deleteCellIcon', 'Icon to delete a cell in notebook editors.')); +export const executeAllIcon = registerIcon('notebook-execute-all', Codicon.runAll, localize('executeAllIcon', 'Icon to execute all cells in notebook editors.')); +export const editIcon = registerIcon('notebook-edit', Codicon.pencil, localize('editIcon', 'Icon to edit a cell in notebook editors.')); +export const stopEditIcon = registerIcon('notebook-stop-edit', Codicon.check, localize('stopEditIcon', 'Icon to stop editing a cell in notebook editors.')); +export const moveUpIcon = registerIcon('notebook-move-up', Codicon.arrowUp, localize('moveUpIcon', 'Icon to move a cell up in notebook editors.')); +export const moveDownIcon = registerIcon('notebook-move-down', Codicon.arrowDown, localize('moveDownIcon', 'Icon to move a cell down in notebook editors.')); +export const clearIcon = registerIcon('notebook-clear', Codicon.clearAll, localize('clearIcon', 'Icon to clear cell outputs in notebook editors.')); +export const splitCellIcon = registerIcon('notebook-split-cell', Codicon.splitVertical, localize('splitCellIcon', 'Icon to split a cell in notebook editors.')); +export const unfoldIcon = registerIcon('notebook-unfold', Codicon.unfold, localize('unfoldIcon', 'Icon to unfold a cell in notebook editors.')); + +export const successStateIcon = registerIcon('notebook-state-success', Codicon.check, localize('successStateIcon', 'Icon to indicate a success state in notebook editors.')); +export const errorStateIcon = registerIcon('notebook-state-error', Codicon.error, localize('errorStateIcon', 'Icon to indicate a error state in notebook editors.')); + +export const collapsedIcon = registerIcon('notebook-collapsed', Codicon.chevronRight, localize('collapsedIcon', 'Icon to annotated a collapsed section in notebook editors.')); +export const expandedIcon = registerIcon('notebook-expanded', Codicon.chevronDown, localize('expandedIcon', 'Icon to annotated a expanded section in notebook editors.')); +export const openAsTextIcon = registerIcon('notebook-open-as-text', Codicon.fileCode, localize('openAsTextIcon', 'Icon to open the notebook in a text editor.')); +export const revertIcon = registerIcon('notebook-revert', Codicon.discard, localize('revertIcon', 'Icon to revert in notebook editors.')); +export const mimetypeIcon = registerIcon('notebook-mimetype', Codicon.code, localize('mimetypeIcon', 'Icon for a mime type in notebook editors.')); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 36869d4c..ec00de56 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getZoomLevel } from 'vs/base/browser/browser'; import { flatten } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -13,22 +14,25 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; import * as nls from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol'; import { Memento } from 'vs/workbench/common/memento'; import { INotebookEditorContribution, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; -import { getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, CellOutputKind, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellKind, CellOutputKind, DisplayOrderKey, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -88,7 +92,7 @@ export class NotebookProviderInfoStore extends Disposable { super(); this._memento = new Memento(NotebookProviderInfoStore.CUSTOM_EDITORS_STORAGE_ID, storageService); - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); for (const info of (mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] || []) as NotebookEditorDescriptor[]) { this.add(new NotebookProviderInfo(info)); } @@ -129,7 +133,7 @@ export class NotebookProviderInfoStore extends Disposable { } } - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); this._memento.saveMemento(); @@ -179,7 +183,7 @@ export class NotebookProviderInfoStore extends Disposable { } this._contributedEditors.set(info.id, info); - const mementoObject = this._memento.getMemento(StorageScope.GLOBAL); + const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); this._memento.saveMemento(); } @@ -276,7 +280,9 @@ export class NotebookService extends Disposable implements INotebookService, ICu @IConfigurationService private readonly _configurationService: IConfigurationService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IStorageService private readonly _storageService: IStorageService, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); @@ -336,6 +342,41 @@ export class NotebookService extends Disposable implements INotebookService, ICu updateOrder(); })); + let decorationTriggeredAdjustment = false; + let decorationCheckSet = new Set(); + this._register(this._codeEditorService.onDecorationTypeRegistered(e => { + if (decorationTriggeredAdjustment) { + return; + } + + if (decorationCheckSet.has(e)) { + return; + } + + const options = this._codeEditorService.resolveDecorationOptions(e, true); + if (options.afterContentClassName || options.beforeContentClassName) { + const cssRules = this._codeEditorService.resolveDecorationCSSRules(e); + if (cssRules !== null) { + for (let i = 0; i < cssRules.length; i++) { + // The following ways to index into the list are equivalent + if ( + ((cssRules[i] as CSSStyleRule).selectorText.endsWith('::after') || (cssRules[i] as CSSStyleRule).selectorText.endsWith('::after')) + && (cssRules[i] as CSSStyleRule).cssText.indexOf('top:') > -1 + ) { + // there is a `::before` or `::after` text decoration whose position is above or below current line + // we at least make sure that the editor top padding is at least one line + const editorOptions = this.configurationService.getValue('editor'); + updateEditorTopPadding(BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight + 2); + decorationTriggeredAdjustment = true; + break; + } + } + } + } + + decorationCheckSet.add(e); + })); + const getContext = () => { const editor = getActiveNotebookEditor(this._editorService); const activeCell = editor?.getActiveCell(); @@ -352,7 +393,13 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (editor?.viewModel) { return editor.viewModel.undo().then(cellResources => { if (cellResources?.length) { - editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } })); + editor?.viewModel?.viewCells.forEach(cell => { + if (cell.cellKind === CellKind.Markdown && cellResources.find(resource => resource.fragment === cell.model.uri.fragment)) { + cell.editState = CellEditState.Editing; + } + }); + + editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] }, preserveFocus: true })); } }); } @@ -365,7 +412,13 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (editor?.viewModel) { return editor.viewModel.redo().then(cellResources => { if (cellResources?.length) { - editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } })); + editor?.viewModel?.viewCells.forEach(cell => { + if (cell.cellKind === CellKind.Markdown && cellResources.find(resource => resource.fragment === cell.model.uri.fragment)) { + cell.editState = CellEditState.Editing; + } + }); + + editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] }, preserveFocus: true })); } }); } @@ -630,7 +683,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu }); } - async getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise { + async getContributedNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise { const filteredProvider = this.notebookKernelProviderInfoStore.get(viewType, resource); const result = new Array(filteredProvider.length); @@ -706,8 +759,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._models.set(uri, modelData); this._onDidAddNotebookDocument.fire(notebookModel); - // after the document is added to the store and sent to ext host, we transform the ouputs - await this.transformTextModelOutputs(notebookModel); return modelData.model; } @@ -720,68 +771,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu return Iterable.map(this._models.values(), data => data.model); } - private async transformTextModelOutputs(textModel: NotebookTextModel) { - for (let i = 0; i < textModel.cells.length; i++) { - const cell = textModel.cells[i]; - - cell.outputs.forEach((output) => { - if (output.outputKind === CellOutputKind.Rich) { - // TODO@rebornix no string[] casting - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - } + getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[] { + // TODO@rebornix no string[] casting + return this._getOrderedMimeTypes(textModel, output, textModel.metadata.displayOrder as string[] ?? []); } - transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) { - edits.forEach((edit) => { - if (edit.editType === CellEditType.Replace) { - edit.cells.forEach((cell) => { - const outputs = cell.outputs; - outputs.map((output) => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - }); - } else if (edit.editType === CellEditType.Output) { - edit.outputs.map((output) => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - } - }); - } - - transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]) { - splices.forEach((splice) => { - const outputs = splice[2]; - outputs.map((output) => { - if (output.outputKind === CellOutputKind.Rich) { - const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []); - const orderedMimeTypes = ret.orderedMimeTypes!; - const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!; - output.pickedMimeTypeIndex = pickedMimeTypeIndex; - output.orderedMimeTypes = orderedMimeTypes; - } - }); - }); - } - - private _transformMimeTypes(output: IDisplayOutput, outputId: string, documentDisplayOrder: string[]): ITransformedDisplayOutputDto { + private _getOrderedMimeTypes(textModel: NotebookTextModel, output: IDisplayOutput, documentDisplayOrder: string[]): IOrderedMimeType[] { const mimeTypes = Object.keys(output.data); const coreDisplayOrder = this._displayOrder; const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []); @@ -797,36 +792,42 @@ export class NotebookService extends Disposable implements INotebookService, ICu orderMimeTypes.push({ mimeType: mimeType, rendererId: handler.id, + isTrusted: textModel.metadata.trusted }); for (let i = 1; i < handlers.length; i++) { orderMimeTypes.push({ mimeType: mimeType, - rendererId: handlers[i].id + rendererId: handlers[i].id, + isTrusted: textModel.metadata.trusted }); } if (mimeTypeSupportedByCore(mimeType)) { orderMimeTypes.push({ mimeType: mimeType, - rendererId: BUILTIN_RENDERER_ID + rendererId: BUILTIN_RENDERER_ID, + isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted }); } } else { - orderMimeTypes.push({ - mimeType: mimeType, - rendererId: BUILTIN_RENDERER_ID - }); + if (mimeTypeSupportedByCore(mimeType)) { + orderMimeTypes.push({ + mimeType: mimeType, + rendererId: BUILTIN_RENDERER_ID, + isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted + }); + } else { + orderMimeTypes.push({ + mimeType: mimeType, + rendererId: RENDERER_NOT_AVAILABLE, + isTrusted: textModel.metadata.trusted + }); + } } }); - return { - outputKind: output.outputKind, - outputId, - data: output.data, - orderedMimeTypes: orderMimeTypes, - pickedMimeTypeIndex: 0 - }; + return orderMimeTypes; } private _findBestMatchedRenderer(mimeType: string): readonly NotebookOutputRendererInfo[] { @@ -992,11 +993,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu if (modelData) { // delete editors and documents const willRemovedEditors: INotebookEditor[] = []; - this._notebookEditors.forEach(editor => { - if (editor.textModel === modelData!.model) { + for (const editor of this._notebookEditors.values()) { + if (editor.textModel === modelData.model) { willRemovedEditors.push(editor); } - }); + } modelData.model.dispose(); modelData.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index e51a8c00..d6d8ed7c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -21,7 +21,7 @@ import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState, CellFocusMode, BaseCellRenderTemplate, NOTEBOOK_CELL_LIST_FOCUSED, cellRangesEqual } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, ICellRange, NOTEBOOK_EDITOR_CURSOR_BEGIN_END } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { clamp } from 'vs/base/common/numbers'; import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; @@ -115,6 +115,9 @@ export class NotebookCellList extends WorkbenchList implements ID const notebookEditorCursorAtBoundaryContext = NOTEBOOK_EDITOR_CURSOR_BOUNDARY.bindTo(contextKeyService); notebookEditorCursorAtBoundaryContext.set('none'); + const notebookEditorCursorAtBeginEndContext = NOTEBOOK_EDITOR_CURSOR_BEGIN_END.bindTo(contextKeyService); + notebookEditorCursorAtBeginEndContext.set(false); + let cursorSelectionListener: IDisposable | null = null; let textEditorAttachListener: IDisposable | null = null; @@ -133,6 +136,13 @@ export class NotebookCellList extends WorkbenchList implements ID notebookEditorCursorAtBoundaryContext.set('none'); break; } + + if (element.cursorAtBeginEnd()) { + notebookEditorCursorAtBeginEndContext.set(true); + } else { + notebookEditorCursorAtBeginEndContext.set(false); + } + return; }; @@ -595,6 +605,14 @@ export class NotebookCellList extends WorkbenchList implements ID } } + async revealElementInCenterIfOutsideViewportAsync(cell: ICellViewModel): Promise { + const index = this._getViewIndexUpperBound(cell); + + if (index >= 0) { + return this._revealInCenterIfOutsideViewportAsync(index); + } + } + async revealElementLineInViewAsync(cell: ICellViewModel, line: number): Promise { const index = this._getViewIndexUpperBound(cell); @@ -855,6 +873,17 @@ export class NotebookCellList extends WorkbenchList implements ID } } + private async _revealInCenterIfOutsideViewportAsync(viewIndex: number): Promise { + this._revealInternal(viewIndex, true, CellRevealPosition.Center); + const element = this.view.element(viewIndex); + + if (!element.editorAttached) { + return getEditorAttachedPromise(element); + } + + return; + } + private async _revealLineInCenterIfOutsideViewportAsync(viewIndex: number, line: number): Promise { return this._revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 7840f023..b2cec399 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -16,8 +16,10 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { URI } from 'vs/base/common/uri'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; import { dirname } from 'vs/base/common/resources'; +import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; class RichRenderer implements IOutputTransformContribution { private _richMimeTypeRenderers = new Map IRenderOutput>(); @@ -27,7 +29,9 @@ class RichRenderer implements IOutputTransformContribution { @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @IModeService private readonly modeService: IModeService, - @IThemeService private readonly themeService: IThemeService + @IThemeService private readonly themeService: IThemeService, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, ) { this._richMimeTypeRenderers.set('application/json', this.renderJSON.bind(this)); this._richMimeTypeRenderers.set('application/javascript', this.renderJavaScript.bind(this)); @@ -204,9 +208,8 @@ class RichRenderer implements IOutputTransformContribution { renderPlainText(output: ITransformedDisplayOutputDto, notebookUri: URI, container: HTMLElement): IRenderOutput { const data = output.data['text/plain']; - const str = (isArray(data) ? data.join('') : data) as string; const contentNode = DOM.$('.output-plaintext'); - contentNode.appendChild(handleANSIOutput(str, this.themeService)); + truncatedArrayOfString(contentNode, isArray(data) ? data : [data], this.openerService, this.textFileService, this.themeService, true); container.appendChild(contentNode); return { type: RenderOutputType.None, hasDynamicHeight: false }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts index 65f1826f..b05a25d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/streamTransform.ts @@ -7,16 +7,23 @@ import * as DOM from 'vs/base/browser/dom'; import { IRenderOutput, CellOutputKind, IStreamOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; class StreamRenderer implements IOutputTransformContribution { constructor( - editor: INotebookEditor + editor: INotebookEditor, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, + @IThemeService readonly themeService: IThemeService ) { } render(output: IStreamOutput, container: HTMLElement): IRenderOutput { const contentNode = DOM.$('.output-stream'); - contentNode.innerText = output.text; + truncatedArrayOfString(contentNode, [output.text], this.openerService, this.textFileService, this.themeService, false); container.appendChild(contentNode); return { type: RenderOutputType.None, hasDynamicHeight: false }; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts new file mode 100644 index 00000000..8ef36f85 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DefaultEndOfLine, EndOfLinePreference, ITextBuffer } from 'vs/editor/common/model'; +import { Range } from 'vs/editor/common/core/range'; +import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; + +const SIZE_LIMIT = 65535; +const LINES_LIMIT = 500; + +function generateViewMoreElement(outputs: string[], openerService: IOpenerService, textFileService: ITextFileService) { + const md: IMarkdownString = { + value: '[show more (open the raw output data in a text editor) ...](command:workbench.action.openLargeOutput)', + isTrusted: true, + supportThemeIcons: true + }; + + const element = renderMarkdown(md, { + actionHandler: { + callback: (content) => { + if (content === 'command:workbench.action.openLargeOutput') { + return textFileService.untitled.resolve({ + associatedResource: undefined, + mode: 'plaintext', + initialValue: outputs.join('') + }).then(model => { + const resource = model.resource; + openerService.open(resource); + }); + } + + return; + }, + disposeables: new DisposableStore() + } + }); + + element.classList.add('output-show-more'); + return element; +} + +export function truncatedArrayOfString(container: HTMLElement, outputs: string[], openerService: IOpenerService, textFileService: ITextFileService, themeService: IThemeService, renderANSI: boolean) { + const fullLen = outputs.reduce((p, c) => { + return p + c.length; + }, 0); + + let buffer: ITextBuffer | undefined = undefined; + + if (fullLen > SIZE_LIMIT) { + // it's too large and we should find min(maxSizeLimit, maxLineLimit) + const bufferBuilder = new PieceTreeTextBufferBuilder(); + outputs.forEach(output => bufferBuilder.acceptChunk(output)); + const factory = bufferBuilder.finish(); + buffer = factory.create(DefaultEndOfLine.LF); + const sizeBufferLimitPosition = buffer.getPositionAt(SIZE_LIMIT); + if (sizeBufferLimitPosition.lineNumber < LINES_LIMIT) { + const truncatedText = buffer.getValueInRange(new Range(1, 1, sizeBufferLimitPosition.lineNumber, sizeBufferLimitPosition.column), EndOfLinePreference.TextDefined); + if (renderANSI) { + container.appendChild(handleANSIOutput(truncatedText, themeService)); + } else { + const pre = DOM.$('div'); + pre.innerText = truncatedText; + container.appendChild(pre); + } + + // view more ... + container.appendChild(generateViewMoreElement(outputs, openerService, textFileService)); + return; + } + } + + if (!buffer) { + const bufferBuilder = new PieceTreeTextBufferBuilder(); + outputs.forEach(output => bufferBuilder.acceptChunk(output)); + const factory = bufferBuilder.finish(); + buffer = factory.create(DefaultEndOfLine.LF); + } + + if (buffer.getLineCount() < LINES_LIMIT) { + const lineCount = buffer.getLineCount(); + const fullRange = new Range(1, 1, lineCount, buffer.getLineLastNonWhitespaceColumn(lineCount)); + + container.innerText = buffer.getValueInRange(fullRange, EndOfLinePreference.TextDefined); + return; + } + + if (renderANSI) { + container.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(1, 1, LINES_LIMIT - 5, buffer.getLineLastNonWhitespaceColumn(LINES_LIMIT - 5)), EndOfLinePreference.TextDefined), themeService)); + } else { + const pre = DOM.$('div'); + pre.innerText = buffer.getValueInRange(new Range(1, 1, LINES_LIMIT - 5, buffer.getLineLastNonWhitespaceColumn(LINES_LIMIT - 5)), EndOfLinePreference.TextDefined); + container.appendChild(pre); + } + + + // view more ... + container.appendChild(generateViewMoreElement(outputs, openerService, textFileService)); + + const lineCount = buffer.getLineCount(); + if (renderANSI) { + container.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(lineCount - 5, 1, lineCount, buffer.getLineLastNonWhitespaceColumn(lineCount)), EndOfLinePreference.TextDefined), themeService)); + } else { + const post = DOM.$('div'); + post.innerText = buffer.getValueInRange(new Range(lineCount - 5, 1, lineCount, buffer.getLineLastNonWhitespaceColumn(lineCount)), EndOfLinePreference.TextDefined); + container.appendChild(post); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 7e99796e..31fc6590 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -6,7 +6,6 @@ import * as DOM from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import * as path from 'vs/base/common/path'; import { isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; @@ -373,7 +372,7 @@ export class BackLayerWebView extends Disposable { }()); `; const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); - this.initialize(htmlContent); + this._initialize(htmlContent); resolveFunc!(); } else { const loaderUri = FileAccess.asBrowserUri('vs/loader.js', require); @@ -397,7 +396,7 @@ var requirejs = (function() { `; const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); - this.initialize(htmlContent); + this._initialize(htmlContent); resolveFunc!(); }); } @@ -405,7 +404,7 @@ var requirejs = (function() { await this._initalized; } - async initialize(content: string) { + private async _initialize(content: string) { if (!document.body.contains(this.element)) { throw new Error('Element is already detached from the DOM tree'); } @@ -555,9 +554,8 @@ var requirejs = (function() { } private _createInset(webviewService: IWebviewService, content: string) { - const rootPathStr = path.dirname(FileAccess.asFileUri('', require).fsPath); + const rootPath = isWeb ? FileAccess.asBrowserUri('', require) : FileAccess.asFileUri('', require); - const rootPath = isWeb ? FileAccess.asBrowserUri(rootPathStr, require) : FileAccess.asFileUri(rootPathStr, require); const workspaceFolders = this.contextService.getWorkspace().folders.map(x => x.uri); this.localResourceRootsCache = [...this.notebookService.getNotebookProviderResourceRoots(), ...workspaceFolders, rootPath]; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts index 2e698f1e..3ded6bb4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts @@ -3,10 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action, IAction, Separator } from 'vs/base/common/actions'; -import { IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { renderCodicons } from 'vs/base/browser/codicons'; +import * as DOM from 'vs/base/browser/dom'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export class VerticalSeparator extends Action { static readonly ID = 'vs.actions.verticalSeparator'; @@ -74,3 +79,19 @@ function asDisposable(groups: ReadonlyArray<[string, ReadonlyArray(); + + readonly resizeListener = new DisposableStore(); + domNode!: HTMLElement; + renderResult?: IRenderOutput; + + constructor( + private notebookEditor: INotebookEditor, + private notebookService: INotebookService, + private quickInputService: IQuickInputService, + private viewCell: CodeCellViewModel, + private outputContainer: HTMLElement, + readonly output: IProcessedOutput + ) { + super(); + } + + render(index: number, beforeElement?: HTMLElement) { + if (this.viewCell.metadata.outputCollapsed) { + return; + } + + const outputItemDiv = document.createElement('div'); + let result: IRenderOutput | undefined = undefined; + + if (this.output.outputKind === CellOutputKind.Rich) { + const transformedDisplayOutput = this.output as ITransformedDisplayOutputDto; + + const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, this.output); + + // there is at least one mimetype which is safe and can be rendered by the core + const pick = this.pickedMimeTypes.get(this.output) ?? Math.max(mimeTypes.findIndex(mimeType => mimeType.rendererId !== RENDERER_NOT_AVAILABLE && mimeType.isTrusted), 0); + + if (mimeTypes.length > 1) { + outputItemDiv.style.position = 'relative'; + const mimeTypePicker = DOM.$('.multi-mimetype-output'); + mimeTypePicker.classList.add(...ThemeIcon.asClassNameArray(mimetypeIcon)); + mimeTypePicker.tabIndex = 0; + mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", mimeTypes.map(mimeType => mimeType.mimeType).join(', ')); + outputItemDiv.appendChild(mimeTypePicker); + this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => { + if (e.leftButton) { + e.preventDefault(); + e.stopPropagation(); + await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); + } + })); + + this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => { + const event = new StandardKeyboardEvent(e); + if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { + e.preventDefault(); + e.stopPropagation(); + await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); + } + }))); + + } + const pickedMimeTypeRenderer = mimeTypes[pick]; + + const innerContainer = DOM.$('.output-inner-container'); + DOM.append(outputItemDiv, innerContainer); + + + if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) { + const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); + result = renderer + ? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType } + : this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); + } else { + result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); + } + + this.pickedMimeTypes.set(this.output, pick); + } else { + // for text and error, there is no mimetype + const innerContainer = DOM.$('.output-inner-container'); + DOM.append(outputItemDiv, innerContainer); + + result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),); + } + + this.domNode = outputItemDiv; + this.renderResult = result; + + if (!result) { + this.viewCell.updateOutputHeight(index, 0); + return; + } + + if (beforeElement) { + this.outputContainer.insertBefore(outputItemDiv, beforeElement); + } else { + this.outputContainer.appendChild(outputItemDiv); + } + + if (result.type !== RenderOutputType.None) { + this.viewCell.selfSizeMonitoring = true; + this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index)); + } else { + outputItemDiv.classList.add('foreground', 'output-element'); + outputItemDiv.style.position = 'absolute'; + } + + if (outputHasDynamicHeight(result)) { + this.viewCell.selfSizeMonitoring = true; + + const clientHeight = outputItemDiv.clientHeight; + const dimension = { + width: this.viewCell.layoutInfo.editorWidth, + height: clientHeight + }; + const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { + if (this.outputContainer && document.body.contains(this.outputContainer!)) { + const height = Math.ceil(elementSizeObserver.getHeight()); + + if (clientHeight === height) { + return; + } + + const currIndex = this.viewCell.outputs.indexOf(this.output); + if (currIndex < 0) { + return; + } + + this.viewCell.updateOutputHeight(currIndex, height); + this.relayoutCell(); + } + }); + elementSizeObserver.startObserving(); + this.resizeListener.add(elementSizeObserver); + this.viewCell.updateOutputHeight(index, clientHeight); + } else if (result.type === RenderOutputType.None) { // no-op if it's a webview + const clientHeight = Math.ceil(outputItemDiv.clientHeight); + this.viewCell.updateOutputHeight(index, clientHeight); + + const top = this.viewCell.getOutputOffsetInContainer(index); + outputItemDiv.style.top = `${top}px`; + } + } + + async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) { + + const mimeTypes = this.notebookService.getMimeTypeInfo(this.notebookEditor.textModel!, output); + const currIndex = this.pickedMimeTypes.get(output); + + // const currIndex = output.pickedMimeTypeIndex; + const items = mimeTypes.filter(mimeType => mimeType.isTrusted).map((mimeType, index): IMimeTypeRenderer => ({ + label: mimeType.mimeType, + id: mimeType.mimeType, + index: index, + picked: index === currIndex, + detail: this.generateRendererInfo(mimeType.rendererId), + description: index === currIndex ? nls.localize('curruentActiveMimeType', "Currently Active") : undefined + })); + + const picker = this.quickInputService.createQuickPick(); + picker.items = items; + picker.activeItems = items.filter(item => !!item.picked); + picker.placeholder = items.length !== mimeTypes.length + ? nls.localize('promptChooseMimeTypeInSecure.placeHolder', "Select mimetype to render for current output. Rich mimetypes are available only when the notebook is trusted") + : nls.localize('promptChooseMimeType.placeHolder', "Select mimetype to render for current output"); + + const pick = await new Promise(resolve => { + picker.onDidAccept(() => { + resolve(picker.selectedItems.length === 1 ? (picker.selectedItems[0] as IMimeTypeRenderer).index : undefined); + picker.dispose(); + }); + picker.show(); + }); + + if (pick === undefined) { + return; + } + + if (pick !== currIndex) { + // user chooses another mimetype + const index = this.viewCell.outputs.indexOf(output); + const nextElement = this.domNode.nextElementSibling; + this.resizeListener.clear(); + const element = this.domNode; + if (element) { + element.parentElement?.removeChild(element); + this.notebookEditor.removeInset(output); + } + + this.pickedMimeTypes.set(output, pick); + this.render(index, nextElement as HTMLElement); + this.relayoutCell(); + } + } + + private getNotebookUri(): URI | undefined { + return CellUri.parse(this.viewCell.uri)?.notebook; + } + + generateRendererInfo(renderId: string | undefined): string { + if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) { + return nls.localize('builtinRenderInfo', "built-in"); + } + + const renderInfo = this.notebookService.getRendererInfo(renderId); + + if (renderInfo) { + const displayName = renderInfo.displayName !== '' ? renderInfo.displayName : renderInfo.id; + return `${displayName} (${renderInfo.extensionId.value})`; + } + + return nls.localize('builtinRenderInfo', "built-in"); + } + + relayoutCell() { + this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); + } +} + +export class OutputContainer extends Disposable { + private outputEntries = new Map(); + + constructor( + private notebookEditor: INotebookEditor, + private viewCell: CodeCellViewModel, + private templateData: CodeCellRenderTemplate, + @INotebookService private notebookService: INotebookService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, + ) { + super(); + + this._register(viewCell.onDidChangeOutputs(splices => { + this._updateOutputs(splices); + })); + + this._register(viewCell.onDidChangeLayout(() => { + this.outputEntries.forEach((value, key) => { + const index = viewCell.outputs.indexOf(key); + if (index >= 0) { + const top = this.viewCell.getOutputOffsetInContainer(index); + value.domNode.style.top = `${top}px`; + } + }); + })); + } + + render(editorHeight: number) { + if (this.viewCell.outputs.length > 0) { + let layoutCache = false; + if (this.viewCell.layoutInfo.totalHeight !== 0 && this.viewCell.layoutInfo.editorHeight > editorHeight) { + layoutCache = true; + this._relayoutCell(); + } + + this.templateData.outputContainer.style.display = 'block'; + // there are outputs, we need to calcualte their sizes and trigger relayout + // @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance + const outputsToRender = this._calcuateOutputsToRender(); + for (let index = 0; index < outputsToRender.length; index++) { + const currOutput = this.viewCell.outputs[index]; + + // always add to the end + this._renderOutput(currOutput, index, undefined); + } + + this.viewCell.editorHeight = editorHeight; + if (this.viewCell.outputs.length > OUTPUT_COUNT_LIMIT) { + this.templateData.outputShowMoreContainer.style.display = 'block'; + this.viewCell.updateOutputShowMoreContainerHeight(46); + } + + if (layoutCache) { + this._relayoutCellDebounced(); + } else { + this._relayoutCell(); + } + } else { + // noop + this.viewCell.editorHeight = editorHeight; + this._relayoutCell(); + this.templateData.outputContainer.style.display = 'none'; + } + + this.templateData.outputShowMoreContainer.innerText = ''; + this.templateData.outputShowMoreContainer.appendChild(this._generateShowMoreElement()); + // this.templateData.outputShowMoreContainer.style.top = `${this.viewCell.layoutInfo.outputShowMoreContainerOffset}px`; + + if (this.viewCell.outputs.length < OUTPUT_COUNT_LIMIT) { + this.templateData.outputShowMoreContainer.style.display = 'none'; + this.viewCell.updateOutputShowMoreContainerHeight(0); + } + } + + viewUpdateShowOutputs(): void { + for (let index = 0; index < this.viewCell.outputs.length; index++) { + const currOutput = this.viewCell.outputs[index]; + + const renderedOutput = this.outputEntries.get(currOutput); + if (renderedOutput && renderedOutput.renderResult) { + if (renderedOutput.renderResult.type !== RenderOutputType.None) { + this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index)); + } else { + // Anything else, just update the height + this.viewCell.updateOutputHeight(index, renderedOutput.domNode.clientHeight); + } + } else { + // Wasn't previously rendered, render it now + this._renderOutput(currOutput, index); + } + } + + this._relayoutCell(); + } + + viewUpdateHideOuputs(): void { + for (const e of this.outputEntries.keys()) { + this.notebookEditor.hideInset(e); + } + } + + onCellWidthChange(): void { + this.viewCell.outputs.forEach((o, i) => { + const renderedOutput = this.outputEntries.get(o); + if (renderedOutput && renderedOutput.renderResult && renderedOutput.renderResult.type === RenderOutputType.None && !renderedOutput.renderResult.hasDynamicHeight) { + this.viewCell.updateOutputHeight(i, renderedOutput.domNode.clientHeight); + } + }); + } + + private _calcuateOutputsToRender() { + const outputs = this.viewCell.outputs.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputs.length)); + if (!this.notebookEditor.viewModel!.metadata.trusted) { + // not trusted + const secureOutput = outputs.filter(output => { + switch (output.outputKind) { + case CellOutputKind.Text: + return true; + case CellOutputKind.Error: + return true; + case CellOutputKind.Rich: + { + const mimeTypes = []; + for (const property in output.data) { + mimeTypes.push(property); + } + + if (mimeTypes.indexOf('text/plain') >= 0 + || mimeTypes.indexOf('text/markdown') >= 0 + || mimeTypes.indexOf('application/json') >= 0 + || mimeTypes.includes('image/png')) { + return true; + } + + return false; + } + default: + return false; + } + }); + + return secureOutput; + } + + return outputs; + } + + private _updateOutputs(splices: NotebookCellOutputsSplice[]) { + if (!splices.length) { + return; + } + + const previousOutputHeight = this.viewCell.layoutInfo.outputTotalHeight; + + if (this.viewCell.outputs.length) { + this.templateData.outputContainer.style.display = 'block'; + } else { + this.templateData.outputContainer.style.display = 'none'; + } + + const reversedSplices = splices.reverse(); + + reversedSplices.forEach(splice => { + this.viewCell.spliceOutputHeights(splice[0], splice[1], splice[2].map(_ => 0)); + }); + + const removedKeys: IProcessedOutput[] = []; + + this.outputEntries.forEach((value, key) => { + if (this.viewCell.outputs.indexOf(key) < 0) { + // already removed + removedKeys.push(key); + // remove element from DOM + this.templateData?.outputContainer?.removeChild(value.domNode); + this.notebookEditor.removeInset(key); + } + }); + + removedKeys.forEach(key => { + this.outputEntries.get(key)?.dispose(); + this.outputEntries.delete(key); + }); + + let prevElement: HTMLElement | undefined = undefined; + const outputsToRender = this._calcuateOutputsToRender(); + + outputsToRender.reverse().forEach(output => { + if (this.outputEntries.has(output)) { + // already exist + prevElement = this.outputEntries.get(output)!.domNode; + return; + } + + // newly added element + const currIndex = this.viewCell.outputs.indexOf(output); + this._renderOutput(output, currIndex, prevElement); + prevElement = this.outputEntries.get(output)?.domNode; + }); + + if (this.viewCell.outputs.length > OUTPUT_COUNT_LIMIT) { + this.templateData.outputShowMoreContainer.style.display = 'block'; + this.viewCell.updateOutputShowMoreContainerHeight(46); + } else { + this.templateData.outputShowMoreContainer.style.display = 'none'; + } + + const editorHeight = this.templateData.editor.getContentHeight(); + this.viewCell.editorHeight = editorHeight; + + if (previousOutputHeight === 0 || this.viewCell.outputs.length === 0) { + // first execution or removing all outputs + this._relayoutCell(); + } else { + this._relayoutCellDebounced(); + } + } + + private _renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { + if (!this.outputEntries.has(currOutput)) { + this.outputEntries.set(currOutput, new OutputElement(this.notebookEditor, this.notebookService, this.quickInputService, this.viewCell, this.templateData.outputContainer, currOutput)); + } + + this.outputEntries.get(currOutput)!.render(index, beforeElement); + } + + private _generateShowMoreElement(): any { + const md: IMarkdownString = { + value: `There are more than ${OUTPUT_COUNT_LIMIT} outputs, [show more (open the raw output data in a text editor) ...](command:workbench.action.openLargeOutput)`, + isTrusted: true, + supportThemeIcons: true + }; + + const element = renderMarkdown(md, { + actionHandler: { + callback: (content) => { + if (content === 'command:workbench.action.openLargeOutput') { + const content = JSON.stringify(this.viewCell.outputs.map(output => { + switch (output.outputKind) { + case CellOutputKind.Text: + return { + outputKind: 'text', + text: output.text + }; + case CellOutputKind.Error: + return { + outputKind: 'error', + ename: output.ename, + evalue: output.evalue, + traceback: output.traceback + }; + case CellOutputKind.Rich: + return { + data: output.data, + metadata: output.metadata + }; + } + })); + const edits = format(content, undefined, {}); + const metadataSource = applyEdits(content, edits); + + return this.textFileService.untitled.resolve({ + associatedResource: undefined, + mode: 'json', + initialValue: metadataSource + }).then(model => { + const resource = model.resource; + this.openerService.open(resource); + }); + } + + return; + }, + disposeables: new DisposableStore() + } + }); + + element.classList.add('output-show-more'); + return element; + } + + private _relayoutCell() { + if (this._timer !== null) { + clearTimeout(this._timer); + } + + this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); + } + + private _timer: number | null = null; + + private _relayoutCellDebounced() { + if (this._timer !== null) { + clearTimeout(this._timer); + } + + this._timer = setTimeout(() => { + this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); + this._timer = null; + }, 200) as unknown as number | null; + } + + dispose() { + this.outputEntries.forEach((value) => { + value.dispose(); + }); + + super.dispose(); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 25156780..2fccd56c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -35,21 +35,22 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CancelCellAction, DeleteCellAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EXPAND_CELL_CONTENT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, EditorTopPaddingChangeEvent, EXPAND_CELL_CONTENT_COMMAND_ID, getEditorTopPadding, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents'; -import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/dnd'; +import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellRunState, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; +import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellRunState, NotebookCellsChangeType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CodiconActionViewItem, createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; const $ = DOM.$; @@ -118,10 +119,16 @@ export class CellEditorOptions { } }); + EditorTopPaddingChangeEvent(() => { + this._value = computeEditorOptions(); + this._onDidChange.fire(this.value); + + }); + const computeEditorOptions = () => { const showCellStatusBar = configurationService.getValue(ShowCellStatusBarKey); const editorPadding = { - top: EDITOR_TOP_PADDING, + top: getEditorTopPadding(), bottom: showCellStatusBar ? EDITOR_BOTTOM_PADDING : EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR }; @@ -352,8 +359,8 @@ abstract class AbstractCellRenderer { } protected setupCollapsedPart(container: HTMLElement): { collapsedPart: HTMLElement, expandButton: HTMLElement } { - const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part', undefined, ...renderCodicons('$(unfold)'))); - const expandButton = collapsedPart.querySelector('.codicon') as HTMLElement; + const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part', undefined, $('span.expandButton' + ThemeIcon.asCSSSelector(unfoldIcon)))); + const expandButton = collapsedPart.querySelector('.expandButton') as HTMLElement; const keybinding = this.keybindingService.lookupKeybinding(EXPAND_CELL_CONTENT_COMMAND_ID); let title = localize('cellExpandButtonLabel', "Expand"); if (keybinding) { @@ -415,6 +422,9 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService)); const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); + DOM.hide(statusBar.durationContainer); + DOM.hide(statusBar.cellRunStatusContainer); + const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); const templateData: MarkdownCellRenderTemplate = { @@ -446,7 +456,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } private getDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { - if (templateData.currentRenderedCell!.editState === CellEditState.Editing) { + if (templateData.currentRenderedCell?.editState === CellEditState.Editing) { return this.getEditDragImage(templateData); } else { return this.getMarkdownDragImage(templateData); @@ -472,6 +482,10 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } renderElement(element: MarkdownCellViewModel, index: number, templateData: MarkdownCellRenderTemplate, height: number | undefined): void { + if (!this.notebookEditor.hasModel()) { + throw new Error('The notebook editor is not attached with view model yet.'); + } + const removedClassNames: string[] = []; templateData.rootContainer.classList.forEach(className => { if (/^nb\-.*$/.test(className)) { @@ -489,7 +503,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR templateData.currentRenderedCell = element; templateData.currentEditor = undefined; - templateData.editorPart!.style.display = 'none'; + templateData.editorPart.style.display = 'none'; templateData.cellContainer.innerText = ''; if (height === undefined) { @@ -514,7 +528,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } })); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel?.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element)); // render toolbar first this.setupCellToolbarActions(templateData, elementDisposables); @@ -715,7 +729,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende }, {}); disposables.add(this.editorOptions.onDidChange(newValue => editor.updateOptions(newValue))); - const { collapsedPart, expandButton } = this.setupCollapsedPart(container); const progressBar = new ProgressBar(editorPart); @@ -727,6 +740,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const cellRunState = new RunStateRenderer(statusBar.cellRunStatusContainer, runToolbar, this.instantiationService); const outputContainer = DOM.append(container, $('.output')); + const outputShowMoreContainer = DOM.append(container, $('.output-show-more-container')); const focusIndicatorRight = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-right')); @@ -761,6 +775,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende runButtonContainer, executionOrderLabel, outputContainer, + outputShowMoreContainer, editor, disposables, elementDisposables: new DisposableStore(), @@ -793,7 +808,11 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } private updateForMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { - const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); + if (!this.notebookEditor.hasModel()) { + return; + } + + const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel.notebookDocument.metadata); templateData.container.classList.toggle('runnable', !!metadata.runnable); this.updateExecutionOrder(metadata, templateData); templateData.statusBar.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; @@ -843,10 +862,15 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.focusIndicatorRight.style.height = `${element.layoutInfo.indicatorHeight}px`; templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`; templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`; + templateData.outputShowMoreContainer.style.top = `${element.layoutInfo.outputShowMoreContainerOffset}px`; templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP}px`; } renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { + if (!this.notebookEditor.hasModel()) { + throw new Error('The notebook editor is not attached with view model yet.'); + } + const removedClassNames: string[] = []; templateData.rootContainer.classList.forEach(className => { if (/^nb\-.*$/.test(className)) { @@ -893,7 +917,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(this.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData)); this.renderedEditors.set(element, templateData.editor); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel?.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element)); this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { @@ -912,6 +936,11 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateForHover(element, templateData); } })); + elementDisposables.add(this.notebookEditor.viewModel.notebookDocument.onDidChangeContent(e => { + if (e.rawEvents.find(event => event.kind === NotebookCellsChangeType.ChangeDocumentMetadata)) { + this.updateForMetadata(element, templateData); + } + })); this.updateForOutputs(element, templateData); elementDisposables.add(element.onDidChangeOutputs(_e => this.updateForOutputs(element, templateData))); @@ -999,11 +1028,13 @@ export class RunStateRenderer { private pendingNewState: NotebookCellRunState | undefined; constructor(private readonly element: HTMLElement, private readonly runToolbar: ToolBar, private readonly instantiationService: IInstantiationService) { + DOM.hide(element); } clear() { if (this.spinnerTimer) { clearTimeout(this.spinnerTimer); + this.spinnerTimer = undefined; } } @@ -1020,9 +1051,9 @@ export class RunStateRenderer { } if (runState === NotebookCellRunState.Success) { - DOM.reset(this.element, ...renderCodicons('$(check)')); + DOM.reset(this.element, ...renderCodicons(ThemeIcon.asCodiconLabel(successStateIcon))); } else if (runState === NotebookCellRunState.Error) { - DOM.reset(this.element, ...renderCodicons('$(error)')); + DOM.reset(this.element, ...renderCodicons(ThemeIcon.asCodiconLabel(errorStateIcon))); } else if (runState === NotebookCellRunState.Running) { DOM.reset(this.element, ...renderCodicons('$(sync~spin)')); @@ -1036,6 +1067,12 @@ export class RunStateRenderer { } else { this.element.innerText = ''; } + + if (runState === NotebookCellRunState.Idle) { + DOM.hide(this.element); + } else { + this.element.style.display = 'flex'; + } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts index 41bb82bb..6be1604e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts @@ -9,9 +9,13 @@ import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { stripCodicons } from 'vs/base/common/codicons'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { isWindows } from 'vs/base/common/platform'; import { extUri } from 'vs/base/common/resources'; +import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; +import { IDimension } from 'vs/editor/common/editorCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -20,11 +24,25 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ChangeCellLanguageAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarEntry } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + const $ = DOM.$; +export interface IClickTarget { + type: ClickTargetType; + event: MouseEvent; +} + +export const enum ClickTargetType { + Container = 0, + CellStatus = 1, + ContributedTextItem = 2, + ContributedCommandItem = 3 +} + export class CellEditorStatusBar extends Disposable { readonly cellStatusMessageContainer: HTMLElement; readonly cellRunStatusContainer: HTMLElement; @@ -37,6 +55,8 @@ export class CellEditorStatusBar extends Disposable { private readonly itemsDisposable: DisposableStore; private currentContext: INotebookCellActionContext | undefined; + protected readonly _onDidClick: Emitter = this._register(new Emitter()); + readonly onDidClick: Event = this._onDidClick.event; constructor( container: HTMLElement, @@ -60,6 +80,38 @@ export class CellEditorStatusBar extends Disposable { this.updateStatusBarItems(); } })); + + this._register(DOM.addDisposableListener(this.statusBarContainer, DOM.EventType.CLICK, e => { + if (e.target === leftItemsContainer || e.target === rightItemsContainer || e.target === this.statusBarContainer) { + // hit on empty space + this._onDidClick.fire({ + type: ClickTargetType.Container, + event: e + }); + } else if (e.target && ( + this.cellStatusMessageContainer.contains(e.target as Node) + || this.cellRunStatusContainer.contains(e.target as Node) + || this.durationContainer.contains(e.target as Node) + )) { + this._onDidClick.fire({ + type: ClickTargetType.CellStatus, + event: e + }); + } else { + if ((e.target as HTMLElement).classList.contains('cell-status-item-has-command')) { + this._onDidClick.fire({ + type: ClickTargetType.ContributedCommandItem, + event: e + }); + } else { + // text + this._onDidClick.fire({ + type: ClickTargetType.ContributedTextItem, + event: e + }); + } + } + })); } update(context: INotebookCellActionContext) { @@ -110,6 +162,10 @@ class CellStatusBarItem extends Disposable { super(); new CodiconLabel(this.container).text = this._itemModel.text; + if (this._itemModel.opacity) { + this.container.style.opacity = this._itemModel.opacity; + } + let ariaLabel: string; let role: string | undefined; if (this._itemModel.accessibilityInformation) { @@ -181,11 +237,12 @@ export class CellLanguageStatusBarItem extends Disposable { super(); this.labelElement = DOM.append(container, $('.cell-language-picker.cell-status-item')); this.labelElement.tabIndex = 0; + this.labelElement.classList.add('cell-status-item-has-command'); this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { this.run(); })); - this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.KEY_UP, e => { + this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { this.run(); @@ -196,7 +253,10 @@ export class CellLanguageStatusBarItem extends Disposable { private run() { this.instantiationService.invokeFunction(accessor => { - new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor!, cell: this.cell! }); + if (!this.editor || !this.editor.hasModel() || !this.cell) { + return; + } + new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor, cell: this.cell }); }); } @@ -215,3 +275,87 @@ export class CellLanguageStatusBarItem extends Disposable { this.labelElement.title = localize('notebook.cell.status.language', "Select Cell Language Mode"); } } + +declare const ResizeObserver: any; + +export interface IResizeObserver { + startObserving: () => void; + stopObserving: () => void; + getWidth(): number; + getHeight(): number; + dispose(): void; +} + +export class BrowserResizeObserver extends Disposable implements IResizeObserver { + private readonly referenceDomElement: HTMLElement | null; + + private readonly observer: any; + private width: number; + private height: number; + + constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) { + super(); + + this.referenceDomElement = referenceDomElement; + this.width = -1; + this.height = -1; + + this.observer = new ResizeObserver((entries: any) => { + for (const entry of entries) { + if (entry.target === referenceDomElement && entry.contentRect) { + if (this.width !== entry.contentRect.width || this.height !== entry.contentRect.height) { + this.width = entry.contentRect.width; + this.height = entry.contentRect.height; + DOM.scheduleAtNextAnimationFrame(() => { + changeCallback(); + }); + } + } + } + }); + } + + getWidth(): number { + return this.width; + } + + getHeight(): number { + return this.height; + } + + startObserving(): void { + this.observer.observe(this.referenceDomElement!); + } + + stopObserving(): void { + this.observer.unobserve(this.referenceDomElement!); + } + + dispose(): void { + this.observer.disconnect(); + super.dispose(); + } +} + +export function getResizesObserver(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void): IResizeObserver { + if (ResizeObserver) { + return new BrowserResizeObserver(referenceDomElement, dimension, changeCallback); + } else { + return new ElementSizeObserver(referenceDomElement, dimension, changeCallback); + } +} + +export function getExecuteCellPlaceholder(viewCell: BaseCellViewModel) { + return { + alignment: CellStatusbarAlignment.LEFT, + priority: -1, + cellResource: viewCell.uri, + command: undefined, + // text: `${keybinding?.getLabel() || 'Ctrl + Enter'} to run`, + // tooltip: `${keybinding?.getLabel() || 'Ctrl + Enter'} to run`, + text: isWindows ? 'Ctrl + Alt + Enter' : 'Ctrl + Enter to run', + tooltip: isWindows ? 'Ctrl + Alt + Enter' : 'Ctrl + Enter to run', + visible: true, + opacity: '0.7' + }; +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 98638963..56869eb8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -4,239 +4,41 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IDimension } from 'vs/editor/common/editorCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; -import * as nls from 'vs/nls'; -import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellFocusMode, CodeCellRenderTemplate, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { BUILTIN_RENDERER_ID, CellOutputKind, CellUri, IInsetRenderOutput, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ClickTargetType, getExecuteCellPlaceholder } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -interface IMimeTypeRenderer extends IQuickPickItem { - index: number; -} - -class OutputElement extends Disposable { - readonly resizeListener = new DisposableStore(); - domNode!: HTMLElement; - renderResult?: IRenderOutput; - - constructor( - private notebookEditor: INotebookEditor, - private notebookService: INotebookService, - private quickInputService: IQuickInputService, - private viewCell: CodeCellViewModel, - private outputContainer: HTMLElement, - readonly output: IProcessedOutput - ) { - super(); - } - - render(index: number, beforeElement?: HTMLElement) { - if (this.viewCell.metadata.outputCollapsed) { - return; - } - - const outputItemDiv = document.createElement('div'); - let result: IRenderOutput | undefined = undefined; - - if (this.output.outputKind === CellOutputKind.Rich) { - const transformedDisplayOutput = this.output as ITransformedDisplayOutputDto; - - if (transformedDisplayOutput.orderedMimeTypes!.length > 1) { - outputItemDiv.style.position = 'relative'; - const mimeTypePicker = DOM.$('.multi-mimetype-output'); - mimeTypePicker.classList.add('codicon', 'codicon-code'); - mimeTypePicker.tabIndex = 0; - mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", transformedDisplayOutput.orderedMimeTypes!.map(mimeType => mimeType.mimeType).join(', ')); - outputItemDiv.appendChild(mimeTypePicker); - this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => { - if (e.leftButton) { - e.preventDefault(); - e.stopPropagation(); - await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); - } - })); - - this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => { - const event = new StandardKeyboardEvent(e); - if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { - e.preventDefault(); - e.stopPropagation(); - await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); - } - }))); - - } - const pickedMimeTypeRenderer = this.output.orderedMimeTypes![this.output.pickedMimeTypeIndex!]; - - const innerContainer = DOM.$('.output-inner-container'); - DOM.append(outputItemDiv, innerContainer); - - - if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) { - const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); - result = renderer - ? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType } - : this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); - } else { - result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),); - } - } else { - // for text and error, there is no mimetype - const innerContainer = DOM.$('.output-inner-container'); - DOM.append(outputItemDiv, innerContainer); - - result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),); - } - - this.domNode = outputItemDiv; - this.renderResult = result; - - if (!result) { - this.viewCell.updateOutputHeight(index, 0); - return; - } - - if (beforeElement) { - this.outputContainer.insertBefore(outputItemDiv, beforeElement); - } else { - this.outputContainer.appendChild(outputItemDiv); - } - - if (result.type !== RenderOutputType.None) { - this.viewCell.selfSizeMonitoring = true; - this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index)); - } else { - outputItemDiv.classList.add('foreground', 'output-element'); - outputItemDiv.style.position = 'absolute'; - } - - if (outputHasDynamicHeight(result)) { - this.viewCell.selfSizeMonitoring = true; - - const clientHeight = outputItemDiv.clientHeight; - const dimension = { - width: this.viewCell.layoutInfo.editorWidth, - height: clientHeight - }; - const elementSizeObserver = getResizesObserver(outputItemDiv, dimension, () => { - if (this.outputContainer && document.body.contains(this.outputContainer!)) { - const height = Math.ceil(elementSizeObserver.getHeight()); - - if (clientHeight === height) { - return; - } - - const currIndex = this.viewCell.outputs.indexOf(this.output); - if (currIndex < 0) { - return; - } - - this.viewCell.updateOutputHeight(currIndex, height); - this.relayoutCell(); - } - }); - elementSizeObserver.startObserving(); - this.resizeListener.add(elementSizeObserver); - this.viewCell.updateOutputHeight(index, clientHeight); - } else if (result.type === RenderOutputType.None) { // no-op if it's a webview - const clientHeight = Math.ceil(outputItemDiv.clientHeight); - this.viewCell.updateOutputHeight(index, clientHeight); - - const top = this.viewCell.getOutputOffsetInContainer(index); - outputItemDiv.style.top = `${top}px`; - } - } - - async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) { - const currIndex = output.pickedMimeTypeIndex; - const items = output.orderedMimeTypes!.map((mimeType, index): IMimeTypeRenderer => ({ - label: mimeType.mimeType, - id: mimeType.mimeType, - index: index, - picked: index === currIndex, - detail: this.generateRendererInfo(mimeType.rendererId), - description: index === currIndex ? nls.localize('curruentActiveMimeType', "Currently Active") : undefined - })); - - const picker = this.quickInputService.createQuickPick(); - picker.items = items; - picker.activeItems = items.filter(item => !!item.picked); - picker.placeholder = nls.localize('promptChooseMimeType.placeHolder', "Select output mimetype to render for current output"); - - const pick = await new Promise(resolve => { - picker.onDidAccept(() => { - resolve(picker.selectedItems.length === 1 ? (picker.selectedItems[0] as IMimeTypeRenderer).index : undefined); - picker.dispose(); - }); - picker.show(); - }); - - if (pick === undefined) { - return; - } - - if (pick !== currIndex) { - // user chooses another mimetype - const index = this.viewCell.outputs.indexOf(output); - const nextElement = this.domNode.nextElementSibling; - this.resizeListener.clear(); - const element = this.domNode; - if (element) { - element.parentElement?.removeChild(element); - this.notebookEditor.removeInset(output); - } - - output.pickedMimeTypeIndex = pick; - this.render(index, nextElement as HTMLElement); - this.relayoutCell(); - } - } - - private getNotebookUri(): URI | undefined { - return CellUri.parse(this.viewCell.uri)?.notebook; - } - - generateRendererInfo(renderId: string | undefined): string { - if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) { - return nls.localize('builtinRenderInfo', "built-in"); - } - - const renderInfo = this.notebookService.getRendererInfo(renderId); - - if (renderInfo) { - const displayName = renderInfo.displayName !== '' ? renderInfo.displayName : renderInfo.id; - return `${displayName} (${renderInfo.extensionId.value})`; - } - - return nls.localize('builtinRenderInfo', "built-in"); - } - - relayoutCell() { - this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight); - } -} export class CodeCell extends Disposable { - private outputEntries = new Map(); + private _outputContainerRenderer: OutputContainer; + private _activeCellRunPlaceholder: IDisposable | null = null; + private _untrustedStatusItem: IDisposable | null = null; constructor( - private notebookEditor: INotebookEditor, + private notebookEditor: IActiveNotebookEditor, private viewCell: CodeCellViewModel, private templateData: CodeCellRenderTemplate, - @INotebookService private notebookService: INotebookService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IModeService private readonly _modeService: IModeService + @INotebookService notebookService: INotebookService, + @IQuickInputService quickInputService: IQuickInputService, + @INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService, + @IOpenerService readonly openerService: IOpenerService, + @ITextFileService readonly textFileService: ITextFileService, + @IModeService private readonly _modeService: IModeService, + // @IKeybindingService private readonly _keybindingService: IKeybindingService, + ) { super(); @@ -244,7 +46,7 @@ export class CodeCell extends Disposable { const lineNum = this.viewCell.lineCount; const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; const editorHeight = this.viewCell.layoutInfo.editorHeight === 0 - ? lineNum * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING + ? lineNum * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING : this.viewCell.layoutInfo.editorHeight; this.layoutEditor( @@ -292,10 +94,10 @@ export class CodeCell extends Disposable { })); updateForFocusMode(); - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel!.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel.metadata).editable) }); this._register(viewCell.onDidChangeState((e) => { if (e.metadataChanged) { - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel!.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel.metadata).editable) }); // TODO@rob this isn't nice this.viewCell.layoutChange({}); @@ -313,14 +115,14 @@ export class CodeCell extends Disposable { this._register(viewCell.onDidChangeLayout((e) => { if (e.outerWidth !== undefined) { - const layoutInfo = templateData.editor!.getLayoutInfo(); + const layoutInfo = templateData.editor.getLayoutInfo(); if (layoutInfo.width !== viewCell.layoutInfo.editorWidth) { this.onCellWidthChange(); } } })); - this._register(templateData.editor!.onDidContentSizeChange((e) => { + this._register(templateData.editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { if (this.viewCell.layoutInfo.editorHeight !== e.contentHeight) { this.onCellHeightChange(e.contentHeight); @@ -328,92 +130,20 @@ export class CodeCell extends Disposable { } })); - this._register(templateData.editor!.onDidChangeCursorSelection((e) => { + this._register(templateData.editor.onDidChangeCursorSelection((e) => { if (e.source === 'restoreState') { // do not reveal the cell into view if this selection change was caused by restoring editors... return; } - const primarySelection = templateData.editor!.getSelection(); + const primarySelection = templateData.editor.getSelection(); if (primarySelection) { - this.notebookEditor.revealLineInViewAsync(viewCell, primarySelection!.positionLineNumber); + this.notebookEditor.revealLineInViewAsync(viewCell, primarySelection.positionLineNumber); } })); - this._register(viewCell.onDidChangeOutputs((splices) => { - if (!splices.length) { - return; - } - - const previousOutputHeight = this.viewCell.layoutInfo.outputTotalHeight; - - if (this.viewCell.outputs.length) { - this.templateData.outputContainer!.style.display = 'block'; - } else { - this.templateData.outputContainer!.style.display = 'none'; - } - - const reversedSplices = splices.reverse(); - - reversedSplices.forEach(splice => { - viewCell.spliceOutputHeights(splice[0], splice[1], splice[2].map(_ => 0)); - }); - - const removedKeys: IProcessedOutput[] = []; - - this.outputEntries.forEach((value, key) => { - if (viewCell.outputs.indexOf(key) < 0) { - // already removed - removedKeys.push(key); - // remove element from DOM - this.templateData?.outputContainer?.removeChild(value.domNode); - this.notebookEditor.removeInset(key); - } - }); - - removedKeys.forEach(key => { - this.outputEntries.get(key)?.dispose(); - this.outputEntries.delete(key); - }); - - let prevElement: HTMLElement | undefined = undefined; - - [...this.viewCell.outputs].reverse().forEach(output => { - if (this.outputEntries.has(output)) { - // already exist - prevElement = this.outputEntries.get(output)!.domNode; - return; - } - - // newly added element - const currIndex = this.viewCell.outputs.indexOf(output); - this.renderOutput(output, currIndex, prevElement); - prevElement = this.outputEntries.get(output)?.domNode; - }); - - const editorHeight = templateData.editor!.getContentHeight(); - viewCell.editorHeight = editorHeight; - - if (previousOutputHeight === 0 || this.viewCell.outputs.length === 0) { - // first execution or removing all outputs - this.relayoutCell(); - } else { - this.relayoutCellDebounced(); - } - })); - - this._register(viewCell.onDidChangeLayout(() => { - this.outputEntries.forEach((value, key) => { - const index = viewCell.outputs.indexOf(key); - if (index >= 0) { - const top = this.viewCell.getOutputOffsetInContainer(index); - value.domNode.style.top = `${top}px`; - } - }); - - })); - + // Apply decorations this._register(viewCell.onCellDecorationsChanged((e) => { e.added.forEach(options => { if (options.className) { @@ -435,7 +165,6 @@ export class CodeCell extends Disposable { } }); })); - // apply decorations viewCell.getCellDecorations().forEach(options => { if (options.className) { @@ -447,7 +176,18 @@ export class CodeCell extends Disposable { } }); - this._register(templateData.editor!.onMouseDown(e => { + // Mouse click handlers + this._register(templateData.statusBar.onDidClick(e => { + if (e.type !== ClickTargetType.ContributedCommandItem) { + const target = templateData.editor.getTargetAtClientPoint(e.event.clientX, e.event.clientY - viewCell.getEditorStatusbarHeight()); + if (target?.position) { + templateData.editor.setPosition(target.position); + templateData.editor.focus(); + } + } + })); + + this._register(templateData.editor.onMouseDown(e => { // prevent default on right mouse click, otherwise it will trigger unexpected focus changes // the catch is, it means we don't allow customization of right button mouse down handlers other than the built in ones. if (e.event.rightButton) { @@ -455,49 +195,88 @@ export class CodeCell extends Disposable { } })); - const updateFocusMode = () => viewCell.focusMode = templateData.editor!.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; - this._register(templateData.editor!.onDidFocusEditorWidget(() => { + // Focus Mode + const updateFocusMode = () => viewCell.focusMode = templateData.editor.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; + this._register(templateData.editor.onDidFocusEditorWidget(() => { updateFocusMode(); })); - - this._register(templateData.editor!.onDidBlurEditorWidget(() => { - updateFocusMode(); + this._register(templateData.editor.onDidBlurEditorWidget(() => { + // this is for a special case: + // users click the status bar empty space, which we will then focus the editor + // so we don't want to update the focus state too eagerly + if (document.activeElement?.contains(this.templateData.container)) { + setTimeout(() => { + updateFocusMode(); + }, 300); + } else { + updateFocusMode(); + } })); - updateFocusMode(); - if (viewCell.outputs.length > 0) { - let layoutCache = false; - if (this.viewCell.layoutInfo.totalHeight !== 0 && this.viewCell.layoutInfo.editorHeight > editorHeight) { - layoutCache = true; - this.relayoutCell(); - } - - this.templateData.outputContainer!.style.display = 'block'; - // there are outputs, we need to calcualte their sizes and trigger relayout - // @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance - for (let index = 0; index < this.viewCell.outputs.length; index++) { - const currOutput = this.viewCell.outputs[index]; - - // always add to the end - this.renderOutput(currOutput, index, undefined); - } - - viewCell.editorHeight = editorHeight; - if (layoutCache) { - this.relayoutCellDebounced(); - } else { - this.relayoutCell(); - } - } else { - // noop - viewCell.editorHeight = editorHeight; - this.relayoutCell(); - this.templateData.outputContainer!.style.display = 'none'; - } - + // Render Outputs + this._outputContainerRenderer = new OutputContainer(notebookEditor, viewCell, templateData, notebookService, quickInputService, openerService, textFileService); + this._outputContainerRenderer.render(editorHeight); // Need to do this after the intial renderOutput updateForCollapseState(); + + const updatePlaceholder = () => { + if (this.notebookEditor.viewModel + && this.notebookEditor.getActiveCell() === this.viewCell + && viewCell.getEvaluatedMetadata(this.notebookEditor.viewModel.metadata).runnable + && viewCell.metadata.runState === undefined + && viewCell.metadata.lastRunDuration === undefined + ) { + // active cell and no run status + if (this._activeCellRunPlaceholder === null) { + // const keybinding = this._keybindingService.lookupKeybinding(EXECUTE_CELL_COMMAND_ID); + this._activeCellRunPlaceholder = this.notebookCellStatusBarService.addEntry(getExecuteCellPlaceholder(this.viewCell)); + } + + return; + } + + this._activeCellRunPlaceholder?.dispose(); + this._activeCellRunPlaceholder = null; + }; + + this._register(this.notebookEditor.onDidChangeActiveCell(() => { + updatePlaceholder(); + })); + + this._register(this.viewCell.model.onDidChangeMetadata(() => { + updatePlaceholder(); + })); + + // const updateUntrustedStatus = () => { + // if (this.notebookEditor.viewModel + // && this.notebookEditor.viewModel.metadata.trusted) { + // this._untrustedStatusItem?.dispose(); + // this._untrustedStatusItem = null; + // } else { + // if (this._untrustedStatusItem === null) { + // this._untrustedStatusItem = this.notebookCellStatusBarService.addEntry({ + // alignment: CellStatusbarAlignment.LEFT, + // priority: -1, + // cellResource: viewCell.uri, + // command: TRUST_NOTEBOOK_COMMAND_ID, + // text: 'Untrusted', + // tooltip: 'Untrusted notebook', + // visible: true, + // }); + // } + // } + // }; + + this._register(this.notebookEditor.viewModel.notebookDocument.onDidChangeContent(e => { + if (e.rawEvents.find(event => event.kind === NotebookCellsChangeType.ChangeDocumentMetadata)) { + updatePlaceholder(); + // updateUntrustedStatus(); + } + })); + + updatePlaceholder(); + // updateUntrustedStatus(); } private viewUpdate(): void { @@ -512,52 +291,24 @@ export class CodeCell extends Disposable { } } - private viewUpdateShowOutputs(): void { - for (let index = 0; index < this.viewCell.outputs.length; index++) { - const currOutput = this.viewCell.outputs[index]; - - const renderedOutput = this.outputEntries.get(currOutput); - if (renderedOutput && renderedOutput.renderResult) { - if (renderedOutput.renderResult.type !== RenderOutputType.None) { - this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index)); - } else { - // Anything else, just update the height - this.viewCell.updateOutputHeight(index, renderedOutput.domNode.clientHeight); - } - } else { - // Wasn't previously rendered, render it now - this.renderOutput(currOutput, index); - } - } - - this.relayoutCell(); - } - private viewUpdateInputCollapsed(): void { DOM.hide(this.templateData.cellContainer); DOM.hide(this.templateData.runButtonContainer); DOM.show(this.templateData.collapsedPart); DOM.show(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', true); - - this.viewUpdateShowOutputs(); + this._outputContainerRenderer.viewUpdateShowOutputs(); this.relayoutCell(); } - private viewUpdateHideOuputs(): void { - for (const e of this.outputEntries.keys()) { - this.notebookEditor.hideInset(e); - } - } - private viewUpdateOutputCollapsed(): void { DOM.show(this.templateData.cellContainer); DOM.show(this.templateData.runButtonContainer); DOM.show(this.templateData.collapsedPart); DOM.hide(this.templateData.outputContainer); - this.viewUpdateHideOuputs(); + this._outputContainerRenderer.viewUpdateHideOuputs(); this.templateData.container.classList.toggle('collapsed', false); this.templateData.container.classList.toggle('output-collapsed', true); @@ -572,11 +323,7 @@ export class CodeCell extends Disposable { DOM.hide(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', true); this.templateData.container.classList.toggle('output-collapsed', true); - - for (const e of this.outputEntries.keys()) { - this.notebookEditor.hideInset(e); - } - + this._outputContainerRenderer.viewUpdateHideOuputs(); this.relayoutCell(); } @@ -587,9 +334,7 @@ export class CodeCell extends Disposable { DOM.show(this.templateData.outputContainer); this.templateData.container.classList.toggle('collapsed', false); this.templateData.container.classList.toggle('output-collapsed', false); - - this.viewUpdateShowOutputs(); - + this._outputContainerRenderer.viewUpdateShowOutputs(); this.relayoutCell(); } @@ -599,7 +344,7 @@ export class CodeCell extends Disposable { } private onCellWidthChange(): void { - const realContentHeight = this.templateData.editor!.getContentHeight(); + const realContentHeight = this.templateData.editor.getContentHeight(); this.viewCell.editorHeight = realContentHeight; this.relayoutCell(); @@ -611,16 +356,11 @@ export class CodeCell extends Disposable { ); // for contents for which we don't observe for dynamic height, update them manually - this.viewCell.outputs.forEach((o, i) => { - const renderedOutput = this.outputEntries.get(o); - if (renderedOutput && renderedOutput.renderResult && renderedOutput.renderResult.type === RenderOutputType.None && !renderedOutput.renderResult.hasDynamicHeight) { - this.viewCell.updateOutputHeight(i, renderedOutput.domNode.clientHeight); - } - }); + this._outputContainerRenderer.onCellWidthChange(); } private onCellHeightChange(newHeight: number): void { - const viewLayout = this.templateData.editor!.getLayoutInfo(); + const viewLayout = this.templateData.editor.getLayoutInfo(); this.viewCell.editorHeight = newHeight; this.relayoutCell(); this.layoutEditor( @@ -631,14 +371,6 @@ export class CodeCell extends Disposable { ); } - private renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) { - if (!this.outputEntries.has(currOutput)) { - this.outputEntries.set(currOutput, new OutputElement(this.notebookEditor, this.notebookService, this.quickInputService, this.viewCell, this.templateData.outputContainer, currOutput)); - } - - this.outputEntries.get(currOutput)!.render(index, beforeElement); - } - relayoutCell() { if (this._timer !== null) { clearTimeout(this._timer); @@ -662,13 +394,11 @@ export class CodeCell extends Disposable { dispose() { this.viewCell.detachTextEditor(); - this.outputEntries.forEach((value) => { - value.dispose(); - }); - - this.templateData.focusIndicatorLeft!.style.height = 'initial'; + this._outputContainerRenderer.dispose(); + this._activeCellRunPlaceholder?.dispose(); + this._untrustedStatusItem?.dispose(); + this.templateData.focusIndicatorLeft.style.height = 'initial'; super.dispose(); } } - diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts deleted file mode 100644 index 4b5002eb..00000000 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as DOM from 'vs/base/browser/dom'; -import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { MenuItemAction } from 'vs/platform/actions/common/actions'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { renderCodicons } from 'vs/base/browser/codicons'; - -export class CodiconActionViewItem extends MenuEntryActionViewItem { - constructor( - readonly _action: MenuItemAction, - keybindingService: IKeybindingService, - notificationService: INotificationService, - ) { - super(_action, keybindingService, notificationService); - } - updateLabel(): void { - if (this.options.label && this.label) { - DOM.reset(this.label, ...renderCodicons(this._commandAction.label ?? '')); - } - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index f75ca70e..da2e04bc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -6,20 +6,24 @@ import * as DOM from 'vs/base/browser/dom'; import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { renderCodicons } from 'vs/base/browser/codicons'; -import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, INotebookEditor, MarkdownCellRenderTemplate, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFocusMode, MarkdownCellRenderTemplate, ICellViewModel, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellFoldingState } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; +import { getExecuteCellPlaceholder, getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { renderCodicons } from 'vs/base/browser/codicons'; export class StatefulMarkdownCell extends Disposable { @@ -29,14 +33,16 @@ export class StatefulMarkdownCell extends Disposable { private localDisposables = new DisposableStore(); private foldingState: CellFoldingState; + private _activeCellRunPlaceholder: IDisposable | null = null; constructor( - private readonly notebookEditor: INotebookEditor, + private readonly notebookEditor: IActiveNotebookEditor, private readonly viewCell: MarkdownCellViewModel, private readonly templateData: MarkdownCellRenderTemplate, private editorOptions: IEditorOptions, private readonly renderedEditors: Map, @IContextKeyService private readonly contextKeyService: IContextKeyService, + @INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); @@ -129,6 +135,40 @@ export class StatefulMarkdownCell extends Disposable { }); this.viewUpdate(); + + const updatePlaceholder = () => { + if ( + this.notebookEditor.getActiveCell() === this.viewCell + && !!this.notebookEditor.viewModel.metadata.trusted + ) { + // active cell and no run status + if (this._activeCellRunPlaceholder === null) { + // const keybinding = this._keybindingService.lookupKeybinding(EXECUTE_CELL_COMMAND_ID); + this._activeCellRunPlaceholder = this.notebookCellStatusBarService.addEntry(getExecuteCellPlaceholder(this.viewCell)); + } + + return; + } + + this._activeCellRunPlaceholder?.dispose(); + this._activeCellRunPlaceholder = null; + }; + + this._register(this.notebookEditor.onDidChangeActiveCell(() => { + updatePlaceholder(); + })); + + this._register(this.viewCell.model.onDidChangeMetadata(() => { + updatePlaceholder(); + })); + + this._register(this.notebookEditor.viewModel.notebookDocument.onDidChangeContent(e => { + if (e.rawEvents.find(event => event.kind === NotebookCellsChangeType.ChangeDocumentMetadata)) { + updatePlaceholder(); + } + })); + + updatePlaceholder(); } private viewUpdate(): void { @@ -159,13 +199,13 @@ export class StatefulMarkdownCell extends Disposable { this.templateData.container.classList.toggle('collapsed', false); if (this.editor) { - editorHeight = this.editor!.getContentHeight(); + editorHeight = this.editor.getContentHeight(); // not first time, we don't need to create editor or bind listeners this.viewCell.attachTextEditor(this.editor); this.focusEditorIfNeeded(); - this.bindEditorListeners(); + this.bindEditorListeners(this.editor); this.editor.layout({ width: this.viewCell.layoutInfo.editorWidth, @@ -175,7 +215,7 @@ export class StatefulMarkdownCell extends Disposable { const width = this.viewCell.layoutInfo.editorWidth; const lineNum = this.viewCell.lineCount; const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; - editorHeight = Math.max(lineNum, 1) * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + editorHeight = Math.max(lineNum, 1) * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; this.templateData.editorContainer.innerText = ''; @@ -221,7 +261,7 @@ export class StatefulMarkdownCell extends Disposable { this.focusEditorIfNeeded(); } - this.bindEditorListeners(); + this.bindEditorListeners(this.editor!); this.viewCell.editorHeight = editorHeight; }); @@ -255,7 +295,7 @@ export class StatefulMarkdownCell extends Disposable { this.relayoutCell(); } else { // first time, readonly mode - this.localDisposables.add(markdownRenderer.onDidRenderCodeBlock(() => { + this.localDisposables.add(markdownRenderer.onDidRenderAsync(() => { this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight; this.relayoutCell(); })); @@ -315,10 +355,10 @@ export class StatefulMarkdownCell extends Disposable { this.templateData.foldingIndicator.innerText = ''; break; case CellFoldingState.Collapsed: - DOM.reset(this.templateData.foldingIndicator, ...renderCodicons('$(chevron-right)')); + DOM.reset(this.templateData.foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(collapsedIcon))); break; case CellFoldingState.Expanded: - DOM.reset(this.templateData.foldingIndicator, ...renderCodicons('$(chevron-down)')); + DOM.reset(this.templateData.foldingIndicator, ...renderCodicons(ThemeIcon.asCodiconLabel(expandedIcon))); break; default: @@ -326,13 +366,13 @@ export class StatefulMarkdownCell extends Disposable { } } - private bindEditorListeners() { - this.localDisposables.add(this.editor!.onDidContentSizeChange(e => { - const viewLayout = this.editor!.getLayoutInfo(); + private bindEditorListeners(editor: CodeEditorWidget) { + this.localDisposables.add(editor.onDidContentSizeChange(e => { + const viewLayout = editor.getLayoutInfo(); if (e.contentHeightChanged) { this.viewCell.editorHeight = e.contentHeight; - this.editor!.layout( + editor.layout( { width: viewLayout.width, height: e.contentHeight @@ -341,26 +381,35 @@ export class StatefulMarkdownCell extends Disposable { } })); - this.localDisposables.add(this.editor!.onDidChangeCursorSelection((e) => { + this.localDisposables.add(editor.onDidChangeCursorSelection((e) => { if (e.source === 'restoreState') { // do not reveal the cell into view if this selection change was caused by restoring editors... return; } - const primarySelection = this.editor!.getSelection(); + const primarySelection = editor.getSelection(); if (primarySelection) { - this.notebookEditor.revealLineInViewAsync(this.viewCell, primarySelection!.positionLineNumber); + this.notebookEditor.revealLineInViewAsync(this.viewCell, primarySelection.positionLineNumber); } })); - const updateFocusMode = () => this.viewCell.focusMode = this.editor!.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; - this.localDisposables.add(this.editor!.onDidFocusEditorWidget(() => { + const updateFocusMode = () => this.viewCell.focusMode = editor.hasWidgetFocus() ? CellFocusMode.Editor : CellFocusMode.Container; + this.localDisposables.add(editor.onDidFocusEditorWidget(() => { updateFocusMode(); })); - this.localDisposables.add(this.editor!.onDidBlurEditorWidget(() => { - updateFocusMode(); + this.localDisposables.add(editor.onDidBlurEditorWidget(() => { + // this is for a special case: + // users click the status bar empty space, which we will then focus the editor + // so we don't want to update the focus state too eagerly + if (document.activeElement?.contains(this.templateData.container)) { + setTimeout(() => { + updateFocusMode(); + }, 300); + } else { + updateFocusMode(); + } })); updateFocusMode(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts deleted file mode 100644 index d9be462c..00000000 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver.ts +++ /dev/null @@ -1,78 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as DOM from 'vs/base/browser/dom'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IDimension } from 'vs/editor/common/editorCommon'; -import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; - -declare const ResizeObserver: any; - -export interface IResizeObserver { - startObserving: () => void; - stopObserving: () => void; - getWidth(): number; - getHeight(): number; - dispose(): void; -} - -export class BrowserResizeObserver extends Disposable implements IResizeObserver { - private readonly referenceDomElement: HTMLElement | null; - - private readonly observer: any; - private width: number; - private height: number; - - constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) { - super(); - - this.referenceDomElement = referenceDomElement; - this.width = -1; - this.height = -1; - - this.observer = new ResizeObserver((entries: any) => { - for (const entry of entries) { - if (entry.target === referenceDomElement && entry.contentRect) { - if (this.width !== entry.contentRect.width || this.height !== entry.contentRect.height) { - this.width = entry.contentRect.width; - this.height = entry.contentRect.height; - DOM.scheduleAtNextAnimationFrame(() => { - changeCallback(); - }); - } - } - } - }); - } - - getWidth(): number { - return this.width; - } - - getHeight(): number { - return this.height; - } - - startObserving(): void { - this.observer.observe(this.referenceDomElement!); - } - - stopObserving(): void { - this.observer.unobserve(this.referenceDomElement!); - } - - dispose(): void { - this.observer.disconnect(); - super.dispose(); - } -} - -export function getResizesObserver(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void): IResizeObserver { - if (ResizeObserver) { - return new BrowserResizeObserver(referenceDomElement, dimension, changeCallback); - } else { - return new ElementSizeObserver(referenceDomElement, dimension, changeCallback); - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 4036c512..936792e8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -101,16 +101,30 @@ function webviewPreloads() { } }; - const runScript = async (url: string, originalUri: string, globals: { [name: string]: unknown } = {}): Promise => { - const res = await fetch(url); - const text = await res.text(); - if (!res.ok) { - throw new Error(`Unexpected ${res.status} requesting ${originalUri}: ${text || res.statusText}`); + const runScript = async (url: string, originalUri: string, globals: { [name: string]: unknown } = {}): Promise<() => (PreloadResult)> => { + let text: string; + try { + const res = await fetch(url); + text = await res.text(); + if (!res.ok) { + throw new Error(`Unexpected ${res.status} requesting ${originalUri}: ${text || res.statusText}`); + } + + globals.scriptUrl = url; + } catch (e) { + return () => ({ state: PreloadState.Error, error: e.message }); } const args = Object.entries(globals); - new Function(...args.map(([k]) => k), text)(...args.map(([, v]) => v)); - return undefined; + return () => { + try { + new Function(...args.map(([k]) => k), text)(...args.map(([, v]) => v)); + return { state: PreloadState.Ok }; + } catch (e) { + console.error(e); + return { state: PreloadState.Error, error: e.message }; + } + }; }; const outputObservers = new Map(); @@ -123,14 +137,27 @@ function webviewPreloads() { } if (entry.target.id === id && entry.contentRect) { - vscode.postMessage({ - __vscode_notebook_message: true, - type: 'dimension', - id: id, - data: { - height: entry.contentRect.height + __outputNodePadding__ * 2 - } - }); + if (entry.contentRect.height !== 0) { + entry.target.style.padding = `${__outputNodePadding__}px`; + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'dimension', + id: id, + data: { + height: entry.contentRect.height + __outputNodePadding__ * 2 + } + }); + } else { + entry.target.style.padding = `0px`; + vscode.postMessage({ + __vscode_notebook_message: true, + type: 'dimension', + id: id, + data: { + height: entry.contentRect.height + } + }); + } } } }); @@ -324,11 +351,18 @@ function webviewPreloads() { }; }; + const enum PreloadState { + Ok, + Error + } + + type PreloadResult = { state: PreloadState.Ok } | { state: PreloadState.Error, error: string }; + /** * Map of preload resource URIs to promises that resolve one the resource * loads or errors. */ - const preloadPromises = new Map>(); + const preloadPromises = new Map>(); const queuedOuputActions = new Map>(); /** @@ -361,7 +395,7 @@ function webviewPreloads() { switch (event.data.type) { case 'html': enqueueOutputAction(event.data, async data => { - const preloadErrs = await Promise.all(data.requiredPreloads.map(p => preloadPromises.get(p.uri))); + const preloadResults = await Promise.all(data.requiredPreloads.map(p => preloadPromises.get(p.uri))); if (!queuedOuputActions.has(data.outputId)) { // output was cleared while loading return; } @@ -389,7 +423,8 @@ function webviewPreloads() { outputNode.style.top = data.top + 'px'; outputNode.style.left = data.left + 'px'; outputNode.style.width = 'calc(100% - ' + data.left + 'px)'; - outputNode.style.minHeight = '32px'; + // outputNode.style.minHeight = '32px'; + outputNode.style.padding = '0px'; outputNode.id = outputId; addMouseoverListeners(outputNode, outputId); @@ -398,13 +433,13 @@ function webviewPreloads() { outputNode.innerHTML = content.htmlContent; cellOutputContainer.appendChild(outputNode); domEval(outputNode); - } else if (preloadErrs.some(e => !!e)) { + } else if (preloadResults.some(e => e?.state === PreloadState.Error)) { outputNode.innerText = `Error loading preloads:`; const errList = document.createElement('ul'); - for (const err of preloadErrs) { - if (err) { + for (const result of preloadResults) { + if (result?.state === PreloadState.Error) { const item = document.createElement('li'); - item.innerText = err; + item.innerText = result.error; errList.appendChild(item); } } @@ -498,11 +533,20 @@ function webviewPreloads() { case 'preload': const resources = event.data.resources; const globals = event.data.type === 'preload' ? { acquireVsCodeApi } : {}; + let queue: Promise = Promise.resolve({ state: PreloadState.Ok }); for (const { uri, originalUri } of resources) { - preloadPromises.set(uri, runScript(uri, originalUri, globals).catch(err => { - console.error(err); - return err.message || String(err); + // create the promise so that the scripts download in parallel, but + // only invoke them in series within the queue + const promise = runScript(uri, originalUri, globals); + queue = queue.then(() => promise.then(fn => { + const result = fn(); + if (result.state === PreloadState.Error) { + console.error(result.error); + } + + return result; })); + preloadPromises.set(uri, queue); } break; case 'focus-output': diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 6d42d670..3d4b3b4e 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -12,8 +12,8 @@ import { IPosition } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; -import { CELL_STATUSBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CELL_STATUSBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions, getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -131,7 +131,7 @@ export abstract class BaseCellViewModel extends Disposable { })); } - protected getEditorStatusbarHeight() { + getEditorStatusbarHeight() { const showCellStatusBar = this._configurationService.getValue(ShowCellStatusBarKey); return showCellStatusBar ? CELL_STATUSBAR_HEIGHT : 0; } @@ -341,7 +341,7 @@ export abstract class BaseCellViewModel extends Disposable { return 0; } - return this._textEditor.getTopForLineNumber(line) + EDITOR_TOP_PADDING; + return this._textEditor.getTopForLineNumber(line) + getEditorTopPadding(); } getPositionScrollTopOffset(line: number, column: number): number { @@ -349,7 +349,35 @@ export abstract class BaseCellViewModel extends Disposable { return 0; } - return this._textEditor.getTopForPosition(line, column) + EDITOR_TOP_PADDING; + return this._textEditor.getTopForPosition(line, column) + getEditorTopPadding(); + } + + cursorAtBeginEnd(): boolean { + if (!this._textEditor) { + return false; + } + + if (!this.textModel) { + return false; + } + + // only validate primary cursor + const selection = this._textEditor.getSelection(); + + // only validate empty cursor + if (!selection || !selection.isEmpty()) { + return false; + } + + if (selection.startLineNumber === 1 && selection.startColumn === 1) { + return true; + } + + if (selection.startLineNumber === this._textModel?.getLineCount() && selection.startColumn === this._textModel?.getLineMaxColumn(selection.startLineNumber)) { + return true; + } + + return false; } cursorAtBoundary(): CursorAtBoundary { @@ -425,8 +453,8 @@ export abstract class BaseCellViewModel extends Disposable { const editable = this.metadata?.editable ?? documentMetadata.cellEditable; - const runnable = this.metadata?.runnable ?? - documentMetadata.cellRunnable; + const runnable = (this.metadata?.runnable ?? + documentMetadata.cellRunnable) && !!documentMetadata.trusted; const hasExecutionOrder = this.metadata?.hasExecutionOrder ?? documentMetadata.cellHasExecutionOrder; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 9e57b51a..bbc8ee23 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -9,8 +9,8 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, getEditorTopPadding, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -86,9 +86,11 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this._layoutInfo = { fontInfo: initialNotebookLayoutInfo?.fontInfo || null, editorHeight: 0, - editorWidth: initialNotebookLayoutInfo ? this.computeEditorWidth(initialNotebookLayoutInfo!.width) : 0, + editorWidth: initialNotebookLayoutInfo ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0, outputContainerOffset: 0, outputTotalHeight: 0, + outputShowMoreContainerHeight: 0, + outputShowMoreContainerOffset: 0, totalHeight: 0, indicatorHeight: 0, bottomToolbarOffset: 0, @@ -103,6 +105,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod layoutChange(state: CodeCellLayoutChangeEvent) { // recompute this._ensureOutputsTop(); + const outputShowMoreContainerHeight = state.outputShowMoreContainerHeight ? state.outputShowMoreContainerHeight : this._layoutInfo.outputShowMoreContainerHeight; let outputTotalHeight = this.metadata?.outputCollapsed ? COLLAPSED_INDICATOR_HEIGHT : this._outputsTop!.getTotalValue(); if (!this.metadata?.inputCollapsed) { @@ -117,17 +120,18 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } else if (state.editorHeight || this._layoutInfo.layoutState === CodeCellLayoutState.Measured) { // Editor has been measured editorHeight = this._editorHeight; - totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight); + totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight, outputShowMoreContainerHeight); newState = CodeCellLayoutState.Measured; } else { editorHeight = this.estimateEditorHeight(state.font?.lineHeight); - totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight); + totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight, outputShowMoreContainerHeight); newState = CodeCellLayoutState.Estimated; } const statusbarHeight = this.getEditorStatusbarHeight(); - const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight; + const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight; + const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; @@ -137,6 +141,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorWidth, outputContainerOffset, outputTotalHeight, + outputShowMoreContainerHeight, + outputShowMoreContainerOffset, totalHeight, indicatorHeight, bottomToolbarOffset, @@ -144,9 +150,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod }; } else { outputTotalHeight = this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight; - const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight; + const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT; - const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight; + const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight + outputShowMoreContainerHeight; + const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; @@ -156,6 +163,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorWidth, outputContainerOffset, outputTotalHeight, + outputShowMoreContainerHeight, + outputShowMoreContainerOffset, totalHeight, indicatorHeight, bottomToolbarOffset, @@ -183,6 +192,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorWidth: this._layoutInfo.editorWidth, outputContainerOffset: this._layoutInfo.outputContainerOffset, outputTotalHeight: this._layoutInfo.outputTotalHeight, + outputShowMoreContainerHeight: this._layoutInfo.outputShowMoreContainerHeight, + outputShowMoreContainerOffset: this._layoutInfo.outputShowMoreContainerOffset, totalHeight: totalHeight, indicatorHeight: this._layoutInfo.indicatorHeight, bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset, @@ -203,18 +214,18 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod getHeight(lineHeight: number) { if (this._layoutInfo.layoutState === CodeCellLayoutState.Uninitialized) { const editorHeight = this.estimateEditorHeight(lineHeight); - return this.computeTotalHeight(editorHeight, 0); + return this.computeTotalHeight(editorHeight, 0, 0); } else { return this._layoutInfo.totalHeight; } } private estimateEditorHeight(lineHeight: number | undefined = 20): number { - return this.lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING; + return this.lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; } - private computeTotalHeight(editorHeight: number, outputsTotalHeight: number): number { - return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; + private computeTotalHeight(editorHeight: number, outputsTotalHeight: number, outputShowMoreContainerHeight: number): number { + return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + outputShowMoreContainerHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; } /** @@ -240,6 +251,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this.editState = CellEditState.Preview; } + updateOutputShowMoreContainerHeight(height: number) { + this.layoutChange({ outputShowMoreContainerHeight: height }); + } + updateOutputHeight(index: number, height: number) { if (index >= this._outputCollection.length) { throw new Error('Output index out of range!'); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 4ea7ba52..d1843f21 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -180,6 +180,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._notebook.metadata; } + get runnable() { + return !!this._notebook.metadata?.runnable && !!this._notebook.metadata?.trusted; + } + private readonly _onDidChangeViewCells = this._register(new Emitter()); get onDidChangeViewCells(): Event { return this._onDidChangeViewCells.event; } @@ -348,7 +352,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD }); })); - this._viewCells = this._notebook!.cells.map(cell => { + this._viewCells = this._notebook.cells.map(cell => { return createCellViewModel(this._instantiationService, this, cell); }); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index d46143ec..709f0a4f 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -12,6 +12,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { hash } from 'vs/base/common/hash'; +import { createTextBuffer } from 'vs/editor/common/model/textModel'; export class NotebookCellTextModel extends Disposable implements ICell { private _onDidChangeOutputs = new Emitter(); @@ -171,6 +172,9 @@ export class NotebookCellTextModel extends Disposable implements ICell { } dispose() { + // Manually release reference to previous text buffer to avoid large leaks + // in case someone leaks a CellTextModel reference + this._textBuffer = createTextBuffer('', model.DefaultEndOfLine.LF); super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 36c08e95..f932f185 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -337,14 +337,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._replaceCells(edit.index, edit.count, edit.cells, synchronous, computeUndoRedo); break; case CellEditType.Output: - //TODO@joh,@rebornix no event, no undo stop (?) + //TODO@jrieken,@rebornix no event, no undo stop (?) this._assertIndex(edit.index); const cell = this._cells[edit.index]; this._spliceNotebookCellOutputs2(cell.handle, edit.outputs, computeUndoRedo); break; case CellEditType.OutputsSplice: { - //TODO@joh,@rebornix no event, no undo stop (?) + //TODO@jrieken,@rebornix no event, no undo stop (?) this._assertIndex(edit.index); const cell = this._cells[edit.index]; this._spliceNotebookCellOutputs(cell.handle, edit.splices, computeUndoRedo); @@ -483,6 +483,17 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } + private _isDocumentMetadataChangeTransient(a: NotebookDocumentMetadata, b: NotebookDocumentMetadata) { + const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); + for (let key of keys) { + if (key !== 'trusted') { + return true; + } + } + + return false; + } + private _updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean) { const oldMetadata = this.metadata; this.metadata = metadata; @@ -504,7 +515,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel }(), undefined, undefined); } - this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata, transient: false }, true); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata, transient: this._isDocumentMetadataChangeTransient(oldMetadata, metadata) }, true); } private _insertNewCell(index: number, cells: NotebookCellTextModel[], synchronous: boolean, endSelections?: number[]): void { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 4b3bbab2..941f4a47 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -58,6 +58,7 @@ export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER = [ ]; export const BUILTIN_RENDERER_ID = '_builtin'; +export const RENDERER_NOT_AVAILABLE = '_notAvailable'; export enum NotebookRunState { Running = 1, @@ -72,7 +73,8 @@ export const notebookDocumentMetadataDefaults: Required output.outputKind === CellOutputKind.Rich ? ({ ...output, outputId: id }) : output; @@ -548,6 +539,19 @@ export namespace CellUri { } } +export function mimeTypeIsAlwaysSecure(mimeType: string) { + if ([ + 'application/json', + 'text/markdown', + 'image/png', + 'text/plain' + ].indexOf(mimeType) > -1) { + return true; + } + + return false; +} + export function mimeTypeSupportedByCore(mimeType: string) { if ([ 'application/json', @@ -681,6 +685,7 @@ export interface ICellEditorViewState { } export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' | 'bottom' | 'both'>('notebookEditorCursorAtBoundary', 'none'); +export const NOTEBOOK_EDITOR_CURSOR_BEGIN_END = new RawContextKey('notebookEditorCursorAtEditorBeginEnd', false); export interface INotebookEditorModel extends IEditorModel { @@ -743,7 +748,6 @@ export interface IEditor extends editorCommon.ICompositeCodeEditor { textModel?: NotebookTextModel; getId(): string; hasFocus(): boolean; - hasModel(): boolean; } export enum NotebookEditorPriority { @@ -865,6 +869,7 @@ export interface INotebookCellStatusBarEntry { readonly command: string | Command | undefined; readonly accessibilityInformation?: IAccessibilityInformation; readonly visible: boolean; + readonly opacity?: string; } export const DisplayOrderKey = 'notebook.displayOrder'; @@ -882,3 +887,4 @@ export interface INotebookDecorationRenderOptions { borderColor?: string | ThemeColor; top?: editorCommon.IContentDecorationRenderOptions; } + diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index fd79380d..1ad9b60a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -17,6 +17,8 @@ import { Schemas } from 'vs/base/common/network'; import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ILogService } from 'vs/platform/log/common/log'; +import { TaskSequentializer } from 'vs/base/common/async'; export interface INotebookLoadOptions { @@ -40,6 +42,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM private readonly _name: string; private readonly _workingCopyResource: URI; + private readonly saveSequentializer = new TaskSequentializer(); private _dirty = false; @@ -51,6 +54,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM @IBackupFileService private readonly _backupFileService: IBackupFileService, @IFileService private readonly _fileService: IFileService, @INotificationService private readonly _notificationService: INotificationService, + @ILogService private readonly _logService: ILogService, @ILabelService labelService: ILabelService, ) { super(); @@ -197,8 +201,14 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } private async _assertStat() { + this._logService.debug('[notebook editor model] start assert stat'); const stats = await this._resolveStats(this.resource); if (this._lastResolvedFileStat && stats && stats.mtime > this._lastResolvedFileStat.mtime) { + this._logService.debug(`[notebook editor model] noteboook file on disk is newer: +LastResolvedStat: ${this._lastResolvedFileStat ? JSON.stringify(this._lastResolvedFileStat) : undefined}. +Current stat: ${JSON.stringify(stats)} +`); + this._lastResolvedFileStat = stats; return new Promise<'overwrite' | 'revert' | 'none'>(resolve => { const handle = this._notificationService.prompt( Severity.Info, @@ -221,31 +231,57 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM resolve('none'); }); }); + } else if (!this._lastResolvedFileStat && stats) { + // finally get a stats + this._lastResolvedFileStat = stats; } return 'overwrite'; } async save(): Promise { - const result = await this._assertStat(); - if (result === 'none') { - return false; + let versionId = this._notebook.versionId; + this._logService.debug(`[notebook editor model] save(${versionId}) - enter with versionId ${versionId}`, this.resource.toString(true)); + + if (this.saveSequentializer.hasPending(versionId)) { + this._logService.debug(`[notebook editor model] save(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource.toString(true)); + return this.saveSequentializer.pending.then(() => { + return true; + }); } - if (result === 'revert') { - await this.revert(); + if (this.saveSequentializer.hasPending()) { + return this.saveSequentializer.setNext(async () => { + await this.save(); + }).then(() => { + return true; + }); + } + + return this.saveSequentializer.setPending(versionId, (async () => { + const result = await this._assertStat(); + if (result === 'none') { + return; + } + + if (result === 'revert') { + await this.revert(); + return; + } + + const tokenSource = new CancellationTokenSource(); + await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); + this._logService.debug(`[notebook editor model] save(${versionId}) - document saved saved, start updating file stats`, this.resource.toString(true)); + const newStats = await this._resolveStats(this.resource); + this._lastResolvedFileStat = newStats; + this.setDirty(false); + })()).then(() => { return true; - } - - const tokenSource = new CancellationTokenSource(); - await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); - const newStats = await this._resolveStats(this.resource); - this._lastResolvedFileStat = newStats; - this.setDirty(false); - return true; + }); } async saveAs(targetResource: URI): Promise { + this._logService.debug(`[notebook editor model] saveAs - enter`, this.resource.toString(true)); const result = await this._assertStat(); if (result === 'none') { @@ -259,6 +295,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM const tokenSource = new CancellationTokenSource(); await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token); + this._logService.debug(`[notebook editor model] saveAs - document saved, start updating file stats`, this.resource.toString(true)); const newStats = await this._resolveStats(this.resource); this._lastResolvedFileStat = newStats; this.setDirty(false); @@ -271,7 +308,9 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } try { + this._logService.debug(`[notebook editor model] _resolveStats`, this.resource.toString(true)); const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true }); + this._logService.debug(`[notebook editor model] _resolveStats - latest file stats: ${JSON.stringify(newStats)}`, this.resource.toString(true)); return newStats; } catch (e) { return undefined; diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 51a80bc3..06f77081 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -10,7 +10,7 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr import { Event } from 'vs/base/common/event'; import { INotebookTextModel, INotebookRendererInfo, - IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter + IEditor, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -49,10 +49,10 @@ export interface INotebookService { onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>; registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable; - transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): void; - transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): void; + getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[]; + registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable; - getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise; + getContributedNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise; getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined; getRendererInfo(id: string): INotebookRendererInfo | undefined; diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index b8d970e6..1ec883fe 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -156,7 +156,7 @@ suite('NotebookViewModel', () => { ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false, runnable: false }], ], (editor, viewModel) => { - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: true, cellEditable: true, cellHasExecutionOrder: true, trusted: true }; const defaults = { hasExecutionOrder: true }; @@ -190,7 +190,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: true, cellHasExecutionOrder: true, trusted: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: true, @@ -222,7 +222,7 @@ suite('NotebookViewModel', () => { ...defaults }); - viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true }; + viewModel.notebookDocument.metadata = { editable: true, runnable: true, cellRunnable: false, cellEditable: false, cellHasExecutionOrder: true, trusted: true }; assert.deepEqual(viewModel.viewCells[0].getEvaluatedMetadata(viewModel.metadata), { editable: false, diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 1679604a..855ffe9f 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -66,6 +66,9 @@ export class TestNotebookEditor implements INotebookEditor { constructor( ) { } + async beginComputeContributedKernels(): Promise { + return []; + } setEditorDecorations(key: string, range: ICellRange): void { // throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index 3dc1cf7f..01b5f2a9 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -11,13 +11,17 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymbols/outline'; import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export const PANEL_ID = 'panel.view.outline'; +const outlineViewIcon = registerIcon('outline-view-icon', Codicon.symbolClass, localize('outlineViewIcon', 'View icon of the outline view.')); + const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), - containerIcon: 'codicon-symbol-class', + containerIcon: outlineViewIcon, ctorDescriptor: new SyncDescriptor(OutlinePane), canToggleVisibility: true, canMoveView: true, diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 3f981c8e..ab48b16b 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -29,7 +29,7 @@ import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor' import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -48,6 +48,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Codicon } from 'vs/base/common/codicons'; class RequestState { @@ -211,7 +212,7 @@ class OutlineViewState { followCursor: this.followCursor, sortBy: this.sortBy, filterOnType: this.filterOnType, - }), StorageScope.WORKSPACE); + }), StorageScope.WORKSPACE, StorageTarget.USER); } restore(storageService: IStorageService): void { @@ -399,7 +400,7 @@ export class OutlinePane extends ViewPane { getActions(): IAction[] { return [ - new CollapseAction(() => this._tree, true, 'explorer-action codicon-collapse-all') + new CollapseAction(() => this._tree, true, 'explorer-action ' + Codicon.collapseAll.classNames) ]; } @@ -425,7 +426,7 @@ export class OutlinePane extends ViewPane { private _onDidChangeUserState(e: { followCursor?: boolean, sortBy?: boolean, filterOnType?: boolean }) { this._outlineViewState.persist(this._storageService); if (e.followCursor) { - // todo@joh update immediately + // todo@jrieken update immediately } if (e.sortBy) { this._treeComparator.type = this._outlineViewState.sortBy; diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 7b028369..0b615745 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -33,6 +33,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { ContextKeyEqualsExpr, ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { CATEGORIES } from 'vs/workbench/common/actions'; // Register Service @@ -60,10 +61,13 @@ const toggleOutputActionKeybindings = { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_H) // On Ubuntu Ctrl+Shift+U is taken by some global OS command } }; + +const outputViewIcon = registerIcon('output-view-icon', Codicon.output, nls.localize('outputViewIcon', 'View icon of the output view.')); + const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: OUTPUT_VIEW_ID, name: nls.localize('output', "Output"), - icon: Codicon.output.classNames, + icon: outputViewIcon, order: 1, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [OUTPUT_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: OUTPUT_VIEW_ID, @@ -74,7 +78,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewC Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: OUTPUT_VIEW_ID, name: nls.localize('output', "Output"), - containerIcon: Codicon.output.classNames, + containerIcon: outputViewIcon, canMoveView: true, canToggleVisibility: false, ctorDescriptor: new SyncDescriptor(OutputViewPane), @@ -139,7 +143,7 @@ registerAction2(class extends Action2 { id: MenuId.EditorContext, when: CONTEXT_IN_OUTPUT }], - icon: { id: 'codicon/clear-all' } + icon: Codicon.clearAll }); } async run(accessor: ServicesAccessor): Promise { @@ -163,10 +167,10 @@ registerAction2(class extends Action2 { group: 'navigation', order: 3, }, - icon: { id: 'codicon/unlock' }, + icon: Codicon.unlock, toggled: { condition: CONTEXT_OUTPUT_SCROLL_LOCK, - icon: { id: 'codicon/lock' }, + icon: Codicon.lock, tooltip: { value: nls.localize('outputScrollOn', "Turn Auto Scrolling On"), original: 'Turn Auto Scrolling On' } } }); @@ -190,7 +194,7 @@ registerAction2(class extends Action2 { id: MenuId.CommandPalette, when: CONTEXT_ACTIVE_LOG_OUTPUT, }], - icon: { id: 'codicon/go-to-file' }, + icon: Codicon.goToFile, precondition: CONTEXT_ACTIVE_LOG_OUTPUT }); } @@ -200,7 +204,7 @@ registerAction2(class extends Action2 { const instantiationService = accessor.get(IInstantiationService); const logFileOutputChannelDescriptor = this.getLogFileOutputChannelDescriptor(outputService); if (logFileOutputChannelDescriptor) { - await editorService.openEditor(instantiationService.createInstance(LogViewerInput, logFileOutputChannelDescriptor)); + await editorService.openEditor(instantiationService.createInstance(LogViewerInput, logFileOutputChannelDescriptor), { pinned: true }); } } private getLogFileOutputChannelDescriptor(outputService: IOutputService): IFileOutputChannelDescriptor | null { @@ -298,7 +302,7 @@ registerAction2(class extends Action2 { const entry = await quickInputService.pick(entries, { placeHolder: nls.localize('selectlogFile', "Select Log file") }); if (entry) { assertIsDefined(entry.channel.file); - await editorService.openEditor(instantiationService.createInstance(LogViewerInput, (entry.channel as IFileOutputChannelDescriptor))); + await editorService.openEditor(instantiationService.createInstance(LogViewerInput, (entry.channel as IFileOutputChannelDescriptor)), { pinned: true }); } } }); diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index f1f11105..062b00c0 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -7,7 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_SCHEME, LOG_MIME, OUTPUT_MIME } from 'vs/workbench/contrib/output/common/output'; import { IOutputChannelDescriptor, Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; @@ -16,7 +16,7 @@ import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/s import { ITextModel } from 'vs/editor/common/model'; import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IOutputChannelModel, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { IOutputChannelModel, IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { IViewsService } from 'vs/workbench/common/views'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; @@ -177,7 +177,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this.activeChannel = channel; if (this.activeChannel) { - this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE); + this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE, StorageTarget.USER); } else { this.storageService.remove(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 7e86eb96..a01c54ae 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -11,7 +11,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; @@ -164,11 +163,6 @@ export class OutputViewPane extends ViewPane { export class OutputEditor extends AbstractTextResourceEditor { - // Override the instantiation service to use to be the scoped one - private scopedInstantiationService: IInstantiationService; - protected get instantiationService(): IInstantiationService { return this.scopedInstantiationService; } - protected set instantiationService(instantiationService: IInstantiationService) { } - constructor( @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, @@ -177,15 +171,10 @@ export class OutputEditor extends AbstractTextResourceEditor { @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, @IThemeService themeService: IThemeService, @IOutputService private readonly outputService: IOutputService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IEditorService editorService: IEditorService ) { super(OUTPUT_VIEW_ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService); - - // Initially, the scoped instantiation service is the global - // one until the editor is created later on - this.scopedInstantiationService = instantiationService; } getId(): string { @@ -258,13 +247,12 @@ export class OutputEditor extends AbstractTextResourceEditor { parent.setAttribute('role', 'document'); - // First create the scoped instantiation service and only then construct the editor using the scoped service - const scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent)); - this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); - super.createEditor(parent); - CONTEXT_IN_OUTPUT.bindTo(scopedContextKeyService).set(true); + const scopedContextKeyService = this.scopedContextKeyService; + if (scopedContextKeyService) { + CONTEXT_IN_OUTPUT.bindTo(scopedContextKeyService).set(true); + } } } diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/contrib/output/common/outputChannelModel.ts similarity index 99% rename from src/vs/workbench/services/output/common/outputChannelModel.ts rename to src/vs/workbench/contrib/output/common/outputChannelModel.ts index 3fa84481..364c4a8b 100644 --- a/src/vs/workbench/services/output/common/outputChannelModel.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModel.ts @@ -37,7 +37,7 @@ export interface IOutputChannelModelService { } -export abstract class AsbtractOutputChannelModelService { +export abstract class AbstractOutputChannelModelService { constructor( @IInstantiationService protected readonly instantiationService: IInstantiationService diff --git a/src/vs/workbench/services/output/common/outputChannelModelService.ts b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts similarity index 75% rename from src/vs/workbench/services/output/common/outputChannelModelService.ts rename to src/vs/workbench/contrib/output/common/outputChannelModelService.ts index 4d9c36ad..79418319 100644 --- a/src/vs/workbench/services/output/common/outputChannelModelService.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IOutputChannelModelService, AsbtractOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { IOutputChannelModelService, AbstractOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { +export class OutputChannelModelService extends AbstractOutputChannelModelService implements IOutputChannelModelService { declare readonly _serviceBrand: undefined; } diff --git a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts index e532dbc5..d9297827 100644 --- a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts @@ -56,7 +56,7 @@ export class OutputLinkComputer { } const links: ILink[] = []; - const lines = model.getValue().split(/\r\n|\r|\n/); + const lines = strings.splitLines(model.getValue()); // For each workspace root patterns for (const [folderUri, folderPatterns] of this.patterns) { diff --git a/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts b/src/vs/workbench/contrib/output/electron-browser/outputChannelModelService.ts similarity index 97% rename from src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts rename to src/vs/workbench/contrib/output/electron-browser/outputChannelModelService.ts index fdc63aae..6613887f 100644 --- a/src/vs/workbench/services/output/electron-browser/outputChannelModelService.ts +++ b/src/vs/workbench/contrib/output/electron-browser/outputChannelModelService.ts @@ -14,7 +14,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { IOutputChannelModel, AbstractFileOutputChannelModel, IOutputChannelModelService, AsbtractOutputChannelModelService, BufferredOutputChannel } from 'vs/workbench/services/output/common/outputChannelModel'; +import { IOutputChannelModel, AbstractFileOutputChannelModel, IOutputChannelModelService, AbstractOutputChannelModelService, BufferredOutputChannel } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { toLocalISOString } from 'vs/base/common/date'; @@ -197,7 +197,7 @@ class DelegatedOutputChannelModel extends Disposable implements IOutputChannelMo } -export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { +export class OutputChannelModelService extends AbstractOutputChannelModelService implements IOutputChannelModelService { declare readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts index a1f68bc0..2daa5225 100644 --- a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts @@ -51,6 +51,6 @@ registerAction2(class extends Action2 { run(accessor: ServicesAccessor) { const editorService = accessor.get(IEditorService); const instaService = accessor.get(IInstantiationService); - return editorService.openEditor(instaService.createInstance(PerfviewInput)); + return editorService.openEditor(instaService.createInstance(PerfviewInput), { pinned: true }); } }); diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 4d0b7970..60f7caa4 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -18,14 +18,15 @@ import * as perf from 'vs/base/common/performance'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; -import { mergeSort } from 'vs/base/common/arrays'; +import { LoaderStats } from 'vs/base/common/amd'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IFileService } from 'vs/platform/files/common/files'; +import { ByteSize, IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { isWeb } from 'vs/base/common/platform'; export class PerfviewContrib { @@ -107,7 +108,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { this._modelDisposables.push(langId); this._modelDisposables.push(this._extensionService.onDidChangeExtensionsStatus(this._updateModel, this)); - writeTransientState(this._model, { forceWordWrap: 'off', forceWordWrapMinified: false }, this._editorService); + writeTransientState(this._model, { wordWrapOverride: 'off' }, this._editorService); } this._updateModel(); return Promise.resolve(this._model); @@ -150,10 +151,10 @@ class PerfModelContentProvider implements ITextModelContentProvider { md.li(`CPUs: ${metrics.cpus.model}(${metrics.cpus.count} x ${metrics.cpus.speed})`); } if (typeof metrics.totalmem === 'number' && typeof metrics.freemem === 'number') { - md.li(`Memory(System): ${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)`); + md.li(`Memory(System): ${(metrics.totalmem / (ByteSize.GB)).toFixed(2)} GB(${(metrics.freemem / (ByteSize.GB)).toFixed(2)}GB free)`); } if (metrics.meminfo) { - md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)`); + md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / ByteSize.KB).toFixed(2)} MB working set(${(metrics.meminfo.privateBytes / ByteSize.KB).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / ByteSize.KB).toFixed(2)}MB shared)`); } md.li(`VM(likelyhood): ${metrics.isVMLikelyhood}%`); md.li(`Initial Startup: ${metrics.initialStartup}`); @@ -171,8 +172,13 @@ class PerfModelContentProvider implements ITextModelContentProvider { table.push(['app.isReady => window.loadUrl()', metrics.timers.ellapsedWindowLoad, '[main]', `initial startup: ${metrics.initialStartup}`]); table.push(['window.loadUrl() => begin to require(workbench.desktop.main.js)', metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)]); table.push(['require(workbench.desktop.main.js)', metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${stats ? `, node_modules took ${stats.nodeRequireTotal}ms` : ''}`]); + table.push(['wait for shell environment', metrics.timers.ellapsedWaitForShellEnv, '[renderer]', undefined]); table.push(['require & init workspace storage', metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]', undefined]); table.push(['init workspace service', metrics.timers.ellapsedWorkspaceServiceInit, '[renderer]', undefined]); + if (isWeb) { + table.push(['init settings and global state from settings sync service', metrics.timers.ellapsedRequiredUserDataInit, '[renderer]', undefined]); + table.push(['init keybindings, snippets & extensions from settings sync service', metrics.timers.ellapsedOtherUserDataInit, '[renderer]', undefined]); + } table.push(['register extensions & spawn extension host', metrics.timers.ellapsedExtensions, '[renderer]', undefined]); table.push(['restore viewlet', metrics.timers.ellapsedViewletRestore, '[renderer]', metrics.viewletId]); table.push(['restore panel', metrics.timers.ellapsedPanelRestore, '[renderer]', metrics.panelId]); @@ -279,100 +285,6 @@ class PerfModelContentProvider implements ITextModelContentProvider { } } -abstract class LoaderStats { - abstract get amdLoad(): (string | number)[][]; - abstract get amdInvoke(): (string | number)[][]; - abstract get nodeRequire(): (string | number)[][]; - abstract get nodeEval(): (string | number)[][]; - abstract get nodeRequireTotal(): number; - - - static get(): LoaderStats { - - - const amdLoadScript = new Map(); - const amdInvokeFactory = new Map(); - const nodeRequire = new Map(); - const nodeEval = new Map(); - - function mark(map: Map, stat: LoaderEvent) { - if (map.has(stat.detail)) { - // console.warn('BAD events, DOUBLE start', stat); - // map.delete(stat.detail); - return; - } - map.set(stat.detail, -stat.timestamp); - } - - function diff(map: Map, stat: LoaderEvent) { - let duration = map.get(stat.detail); - if (!duration) { - // console.warn('BAD events, end WITHOUT start', stat); - // map.delete(stat.detail); - return; - } - if (duration >= 0) { - // console.warn('BAD events, DOUBLE end', stat); - // map.delete(stat.detail); - return; - } - map.set(stat.detail, duration + stat.timestamp); - } - - const stats = mergeSort(require.getStats().slice(0), (a, b) => a.timestamp - b.timestamp); - - for (const stat of stats) { - switch (stat.type) { - case LoaderEventType.BeginLoadingScript: - mark(amdLoadScript, stat); - break; - case LoaderEventType.EndLoadingScriptOK: - case LoaderEventType.EndLoadingScriptError: - diff(amdLoadScript, stat); - break; - - case LoaderEventType.BeginInvokeFactory: - mark(amdInvokeFactory, stat); - break; - case LoaderEventType.EndInvokeFactory: - diff(amdInvokeFactory, stat); - break; - - case LoaderEventType.NodeBeginNativeRequire: - mark(nodeRequire, stat); - break; - case LoaderEventType.NodeEndNativeRequire: - diff(nodeRequire, stat); - break; - - case LoaderEventType.NodeBeginEvaluatingScript: - mark(nodeEval, stat); - break; - case LoaderEventType.NodeEndEvaluatingScript: - diff(nodeEval, stat); - break; - } - } - - let nodeRequireTotal = 0; - nodeRequire.forEach(value => nodeRequireTotal += value); - - function to2dArray(map: Map): (string | number)[][] { - let res: (string | number)[][] = []; - map.forEach((value, index) => res.push([index, value])); - return res; - } - - return { - amdLoad: to2dArray(amdLoadScript), - amdInvoke: to2dArray(amdInvokeFactory), - nodeRequire: to2dArray(nodeRequire), - nodeEval: to2dArray(nodeEval), - nodeRequireTotal - }; - } -} - class MarkdownBuilder { value: string = ''; @@ -393,34 +305,6 @@ class MarkdownBuilder { } table(header: string[], rows: Array>) { - let lengths: number[] = []; - header.forEach((cell, ci) => { - lengths[ci] = cell.length; - }); - rows.forEach(row => { - row.forEach((cell, ci) => { - if (typeof cell === 'undefined') { - cell = row[ci] = '-'; - } - const len = cell.toString().length; - lengths[ci] = Math.max(len, lengths[ci]); - }); - }); - - // header - header.forEach((cell, ci) => { this.value += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; }); - this.value += '|\n'; - header.forEach((_cell, ci) => { this.value += `| ${'-'.repeat(lengths[ci])} `; }); - this.value += '|\n'; - - // cells - rows.forEach(row => { - row.forEach((cell, ci) => { - if (typeof cell !== 'undefined') { - this.value += `| ${cell + ' '.repeat(lengths[ci] - cell.toString().length)} `; - } - }); - this.value += '|\n'; - }); + this.value += LoaderStats.toMarkdownTable(header, rows); } } diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts index cfb82245..0e640c75 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts @@ -24,6 +24,8 @@ import { editorWidgetBackground, editorWidgetForeground, widgetShadow } from 'vs import { ScrollType } from 'vs/editor/common/editorCommon'; import { SearchWidget, SearchOptions } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { timeout } from 'vs/base/common/async'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export interface KeybindingsSearchOptions extends SearchOptions { recordEnter?: boolean; @@ -50,13 +52,14 @@ export class KeybindingsSearchWidget extends SearchWidget { private _onBlur = this._register(new Emitter()); readonly onBlur: Event = this._onBlur.event; - constructor(parent: HTMLElement, options: SearchOptions, + constructor(parent: HTMLElement, options: KeybindingsSearchOptions, @IContextViewService contextViewService: IContextViewService, @IKeybindingService private readonly keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IContextKeyService contextKeyService: IContextKeyService ) { - super(parent, options, contextViewService, instantiationService, themeService); + super(parent, options, contextViewService, instantiationService, themeService, contextKeyService); this._register(attachInputBoxStyler(this.inputBox, themeService)); this._register(toDisposable(() => this.stopRecordingKeys())); this._firstPart = null; @@ -198,7 +201,7 @@ export class DefineKeybindingWidget extends Widget { } })); - this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, this._domNode.domNode, { ariaLabel: message })); + this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, this._domNode.domNode, { ariaLabel: message, history: [] })); this._keybindingInputWidget.startRecordingKeys(); this._register(this._keybindingInputWidget.onKeybinding(keybinding => this.onKeybinding(keybinding))); this._register(this._keybindingInputWidget.onEnter(() => this.hide())); @@ -219,7 +222,7 @@ export class DefineKeybindingWidget extends Widget { define(): Promise { this._keybindingInputWidget.clear(); - return new Promise((c) => { + return new Promise(async (c) => { if (!this._isVisible) { this._isVisible = true; this._domNode.setDisplay('block'); @@ -229,6 +232,11 @@ export class DefineKeybindingWidget extends Widget { this._keybindingInputWidget.setInputValue(''); dom.clearNode(this._outputNode); dom.clearNode(this._showExistingKeybindingsNode); + + // Input is not getting focus without timeout in safari + // https://github.com/microsoft/vscode/issues/108817 + await timeout(0); + this._keybindingInputWidget.focus(); } const disposable = this._onHide.event(() => { diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 08273a27..7b7c3dd5 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -21,12 +21,12 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { KeybindingsEditorModel, IKeybindingItemEntry, IListEntry, KEYBINDING_ENTRY_TEMPLATE_ID } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; -import { DefineKeybindingWidget, KeybindingsSearchWidget, KeybindingsSearchOptions } from 'vs/workbench/contrib/preferences/browser/keybindingWidgets'; -import { IKeybindingsEditorPane, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR } from 'vs/workbench/contrib/preferences/common/preferences'; +import { DefineKeybindingWidget, KeybindingsSearchWidget } from 'vs/workbench/contrib/preferences/browser/keybindingWidgets'; +import { CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_ADD } from 'vs/workbench/contrib/preferences/common/preferences'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; -import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; @@ -37,16 +37,17 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { attachStylerCallback, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { attachStylerCallback, attachInputBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { Emitter, Event } from 'vs/base/common/event'; import { MenuRegistry, MenuId, isIMenuItem } from 'vs/platform/actions/common/actions'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { Color, RGBA } from 'vs/base/common/color'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; -import { ThemableCheckboxActionViewItem } from 'vs/platform/theme/browser/checkbox'; +import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IKeybindingsEditorPane } from 'vs/workbench/services/preferences/common/preferences'; +import { keybindingsRecordKeysIcon, keybindingsSortIcon, keybindingsAddIcon, preferencesClearInputIcon, keybindingsEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const $ = DOM.$; @@ -58,6 +59,20 @@ interface ColumnItem { const oddRowBackgroundColor = new Color(new RGBA(130, 130, 130, 0.04)); +class ThemableCheckboxActionViewItem extends CheckboxActionViewItem { + + constructor(context: any, action: IAction, options: IBaseActionViewItemOptions | undefined, private readonly themeService: IThemeService) { + super(context, action, options); + } + + render(container: HTMLElement): void { + super.render(container); + if (this.checkbox) { + this.disposables.add(attachCheckboxStyler(this.checkbox, this.themeService)); + } + } +} + export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorPane { static readonly ID: string = 'workbench.editor.keybindings'; @@ -73,6 +88,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private headerContainer!: HTMLElement; private actionsContainer!: HTMLElement; private searchWidget!: KeybindingsSearchWidget; + private searchHistoryDelayer: Delayer; private overlayContainer!: HTMLElement; private defineKeybindingWidget!: DefineKeybindingWidget; @@ -86,7 +102,6 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private dimension: DOM.Dimension | null = null; private delayedFiltering: Delayer; private latestEmptyFilters: string[] = []; - private delayedFilterLogging: Delayer; private keybindingsEditorContextKey: IContextKey; private keybindingFocusContextKey: IContextKey; private searchFocusContextKey: IContextKey; @@ -116,16 +131,16 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService); this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); this.keybindingFocusContextKey = CONTEXT_KEYBINDING_FOCUS.bindTo(this.contextKeyService); - this.delayedFilterLogging = new Delayer(1000); + this.searchHistoryDelayer = new Delayer(500); const recordKeysActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS); const recordKeysActionLabel = localize('recordKeysLabel', "Record Keys"); - this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, 'codicon-record-keys'); + this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, ThemeIcon.asClassName(keybindingsRecordKeysIcon)); this.recordKeysAction.checked = false; const sortByPrecedenceActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE); const sortByPrecedenceActionLabel = localize('sortByPrecedeneLabel', "Sort by Precedence"); - this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, 'codicon-sort-precedence'); + this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, ThemeIcon.asClassName(keybindingsSortIcon)); this.sortByPrecedenceAction.checked = false; } @@ -190,13 +205,13 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? focusedElement : null; } - defineKeybinding(keybindingEntry: IKeybindingItemEntry): Promise { + defineKeybinding(keybindingEntry: IKeybindingItemEntry, add: boolean): Promise { this.selectEntry(keybindingEntry); this.showOverlayContainer(); return this.defineKeybindingWidget.define().then(key => { if (key) { this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_DEFINE, keybindingEntry.keybindingItem.command, key); - return this.updateKeybinding(keybindingEntry, key, keybindingEntry.keybindingItem.when); + return this.updateKeybinding(keybindingEntry, key, keybindingEntry.keybindingItem.when, add); } return null; }).then(() => { @@ -217,17 +232,18 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP } } - updateKeybinding(keybindingEntry: IKeybindingItemEntry, key: string, when: string | undefined): Promise { + async updateKeybinding(keybindingEntry: IKeybindingItemEntry, key: string, when: string | undefined, add?: boolean): Promise { const currentKey = keybindingEntry.keybindingItem.keybinding ? keybindingEntry.keybindingItem.keybinding.getUserSettingsLabel() : ''; if (currentKey !== key || keybindingEntry.keybindingItem.when !== when) { - return this.keybindingEditingService.editKeybinding(keybindingEntry.keybindingItem.keybindingItem, key, when || undefined) - .then(() => { - if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering - this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry; - } - }); + if (add) { + await this.keybindingEditingService.addKeybinding(keybindingEntry.keybindingItem.keybindingItem, key, when || undefined); + } else { + await this.keybindingEditingService.editKeybinding(keybindingEntry.keybindingItem.keybindingItem, key, when || undefined); + } + if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering + this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry; + } } - return Promise.resolve(); } removeKeybinding(keybindingEntry: IKeybindingItemEntry): Promise { @@ -286,6 +302,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP search(filter: string): void { this.focusSearch(); this.searchWidget.setValue(filter); + this.selectEntry(0); } clearSearchResults(): void { @@ -328,16 +345,17 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP const fullTextSearchPlaceholder = localize('SearchKeybindings.FullTextSearchPlaceholder', "Type to search in keybindings"); const keybindingsSearchPlaceholder = localize('SearchKeybindings.KeybindingsSearchPlaceholder', "Recording Keys. Press Escape to exit"); - const clearInputAction = new Action(KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Keybindings Search Input"), 'codicon-clear-all', false, () => { this.search(''); return Promise.resolve(null); }); + const clearInputAction = new Action(KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Keybindings Search Input"), ThemeIcon.asClassName(preferencesClearInputIcon), false, () => { this.clearSearchResults(); return Promise.resolve(null); }); const searchContainer = DOM.append(this.headerContainer, $('.search-container')); - this.searchWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, searchContainer, { + this.searchWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, searchContainer, { ariaLabel: fullTextSearchPlaceholder, placeholder: fullTextSearchPlaceholder, focusKey: this.searchFocusContextKey, ariaLabelledBy: 'keybindings-editor-aria-label-element', recordEnter: true, - quoteRecordedKeys: true + quoteRecordedKeys: true, + history: this.getMemento(StorageScope.GLOBAL, StorageTarget.USER)['searchHistory'] || [], })); this._register(this.searchWidget.onDidChange(searchValue => { clearInputAction.enabled = !!searchValue; @@ -483,7 +501,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP this._register(this.keybindingsList.onMouseDblClick(() => { const activeKeybindingEntry = this.activeKeybindingEntry; if (activeKeybindingEntry) { - this.defineKeybinding(activeKeybindingEntry); + this.defineKeybinding(activeKeybindingEntry, false); } })); this._register(this.keybindingsList.onKeyDown(e => { @@ -491,7 +509,7 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP if (event.keyCode === KeyCode.Enter) { const keybindingEntry = this.activeKeybindingEntry; if (keybindingEntry) { - this.defineKeybinding(keybindingEntry); + this.defineKeybinding(keybindingEntry, false); } e.stopPropagation(); } @@ -529,7 +547,12 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP private filterKeybindings(): void { this.renderKeybindingsEntries(this.searchWidget.hasFocus()); - this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(this.searchWidget.getValue())); + this.searchHistoryDelayer.trigger(() => { + this.searchWidget.inputBox.addToHistory(); + this.getMemento(StorageScope.GLOBAL, StorageTarget.USER)['searchHistory'] = this.searchWidget.inputBox.getHistory(); + this.saveState(); + this.reportFilteringUsed(this.searchWidget.getValue()); + }); } private renderKeybindingsEntries(reset: boolean, preserveFocus?: boolean): void { @@ -657,19 +680,24 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP } if (e.element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { - this.selectEntry(e.element); + const keybindingItemEntry = e.element; + this.selectEntry(keybindingItemEntry); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => [ - this.createCopyAction(e.element), - this.createCopyCommandAction(e.element), + this.createCopyAction(keybindingItemEntry), + this.createCopyCommandAction(keybindingItemEntry), new Separator(), - this.createDefineAction(e.element), - this.createRemoveAction(e.element), - this.createResetAction(e.element), - this.createDefineWhenExpressionAction(e.element), + ...(keybindingItemEntry.keybindingItem.keybinding + ? [this.createDefineKeybindingAction(keybindingItemEntry), this.createAddKeybindingAction(keybindingItemEntry)] + : [this.createDefineKeybindingAction(keybindingItemEntry)]), new Separator(), - this.createShowConflictsAction(e.element)] + this.createRemoveAction(keybindingItemEntry), + this.createResetAction(keybindingItemEntry), + new Separator(), + this.createDefineWhenExpressionAction(keybindingItemEntry), + new Separator(), + this.createShowConflictsAction(keybindingItemEntry)] }); } } @@ -685,12 +713,21 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP } } - private createDefineAction(keybindingItemEntry: IKeybindingItemEntry): IAction { + private createDefineKeybindingAction(keybindingItemEntry: IKeybindingItemEntry): IAction { return { - label: keybindingItemEntry.keybindingItem.keybinding ? localize('changeLabel', "Change Keybinding") : localize('addLabel', "Add Keybinding"), + label: keybindingItemEntry.keybindingItem.keybinding ? localize('changeLabel', "Change Keybinding...") : localize('addLabel', "Add Keybinding..."), enabled: true, id: KEYBINDINGS_EDITOR_COMMAND_DEFINE, - run: () => this.defineKeybinding(keybindingItemEntry) + run: () => this.defineKeybinding(keybindingItemEntry, false) + }; + } + + private createAddKeybindingAction(keybindingItemEntry: IKeybindingItemEntry): IAction { + return { + label: localize('addLabel', "Add Keybinding..."), + enabled: true, + id: KEYBINDINGS_EDITOR_COMMAND_ADD, + run: () => this.defineKeybinding(keybindingItemEntry, true) }; } @@ -897,22 +934,22 @@ class ActionsColumn extends Column { private createEditAction(keybindingItemEntry: IKeybindingItemEntry): IAction { const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE); return { - class: preferencesEditIcon.classNames, + class: ThemeIcon.asClassName(keybindingsEditIcon), enabled: true, id: 'editKeybinding', tooltip: keybinding ? localize('editKeybindingLabelWithKey', "Change Keybinding {0}", `(${keybinding.getLabel()})`) : localize('editKeybindingLabel', "Change Keybinding"), - run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry, false) }; } private createAddAction(keybindingItemEntry: IKeybindingItemEntry): IAction { const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE); return { - class: 'codicon-add', + class: ThemeIcon.asClassName(keybindingsAddIcon), enabled: true, id: 'addKeybinding', tooltip: keybinding ? localize('addKeybindingLabelWithKey', "Add Keybinding {0}", `(${keybinding.getLabel()})`) : localize('addKeybindingLabel', "Add Keybinding"), - run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry, false) }; } diff --git a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts index cdf16c24..a7e4bad5 100644 --- a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts +++ b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; -import { IKeymapService, areKeyboardLayoutsEqual, parseKeyboardLayoutDescription, getKeyboardLayoutId, IKeyboardLayoutInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { parseKeyboardLayoutDescription, areKeyboardLayoutsEqual, getKeyboardLayoutId, IKeyboardLayoutService, IKeyboardLayoutInfo } from 'vs/platform/keyboardLayout/common/keyboardLayout'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; @@ -27,12 +27,12 @@ export class KeyboardLayoutPickerContribution extends Disposable implements IWor private readonly pickerElement = this._register(new MutableDisposable()); constructor( - @IKeymapService private readonly keymapService: IKeymapService, + @IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService, @IStatusbarService private readonly statusbarService: IStatusbarService, ) { super(); - let layout = this.keymapService.getCurrentKeyboardLayout(); + let layout = this.keyboardLayoutService.getCurrentKeyboardLayout(); if (layout) { let layoutInfo = parseKeyboardLayoutDescription(layout); const text = nls.localize('keyboardLayout', "Layout: {0}", layoutInfo.label); @@ -49,8 +49,8 @@ export class KeyboardLayoutPickerContribution extends Disposable implements IWor ); } - this._register(keymapService.onDidChangeKeyboardMapper(() => { - let layout = this.keymapService.getCurrentKeyboardLayout(); + this._register(keyboardLayoutService.onDidChangeKeyboardLayout(() => { + let layout = this.keyboardLayoutService.getCurrentKeyboardLayout(); let layoutInfo = parseKeyboardLayoutDescription(layout); if (this.pickerElement.value) { @@ -101,7 +101,7 @@ export class KeyboardLayoutPickerAction extends Action { actionLabel: string, @IFileService private readonly fileService: IFileService, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IKeymapService private readonly keymapService: IKeymapService, + @IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IEditorService private readonly editorService: IEditorService @@ -110,8 +110,8 @@ export class KeyboardLayoutPickerAction extends Action { } async run(): Promise { - let layouts = this.keymapService.getAllKeyboardLayouts(); - let currentLayout = this.keymapService.getCurrentKeyboardLayout(); + let layouts = this.keyboardLayoutService.getAllKeyboardLayouts(); + let currentLayout = this.keyboardLayoutService.getCurrentKeyboardLayout(); let layoutConfig = this.configurationService.getValue('keyboard.layout'); let isAutoDetect = layoutConfig === 'autodetect'; @@ -169,7 +169,8 @@ export class KeyboardLayoutPickerAction extends Action { } return this.editorService.openEditor({ resource: stat.resource, - mode: 'jsonc' + mode: 'jsonc', + options: { pinned: true } }); }, (error) => { throw new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", file.toString(), error)); diff --git a/src/vs/workbench/contrib/preferences/browser/media/preferences.css b/src/vs/workbench/contrib/preferences/browser/media/preferences.css index 2476aeea..89e32232 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/preferences.css +++ b/src/vs/workbench/contrib/preferences/browser/media/preferences.css @@ -194,28 +194,6 @@ justify-content: center; } -.monaco-editor .settings-group-title-widget .title-container.collapsed .codicon::before { - transform: rotate(-90deg); -} - -.monaco-editor .codicon-edit { - transform: rotate(-90deg); - width:16px; - height: 16px; - cursor: pointer; -} - -.monaco-editor .codicon-edit.hidden { - display: none; - visibility: hidden; -} - .monaco-editor .dim-configuration { color: #b1b1b1; } - -.monaco-editor .floating-click-widget { - padding: 10px; - border-radius: 5px; - cursor: pointer; -} diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 58ce2825..f9f59178 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -124,6 +124,7 @@ .settings-editor > .settings-body { position: relative; + margin-top: 14px; } .settings-editor > .settings-body > .no-results-message { @@ -247,7 +248,6 @@ } .settings-editor > .settings-body .settings-tree-container { - margin-top: 14px; border-spacing: 0; border-collapse: separate; position: relative; @@ -344,13 +344,11 @@ } .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides { - opacity: 0.9; font-style: italic; margin-right: 4px; } .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored { - opacity: 0.9; font-style: italic; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 8c59d791..511b2c23 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -33,13 +33,14 @@ import { KeybindingsEditor } from 'vs/workbench/contrib/preferences/browser/keyb import { ConfigureLanguageBasedSettingsAction } from 'vs/workbench/contrib/preferences/browser/preferencesActions'; import { PreferencesEditor } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; import { SettingsEditor2, SettingsFocusContext } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; -import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; +import { preferencesOpenSettingsIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -95,7 +96,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register Preferences Editor Input Factory class PreferencesEditorInputFactory extends AbstractSideBySideEditorInputFactory { - protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { + protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput { return new PreferencesEditorInput(name, description, secondaryInput, primaryInput); } } @@ -277,7 +278,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon super({ id: '_workbench.openUserSettingsEditor', title: OPEN_SETTINGS2_ACTION_TITLE, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, menu: [{ id: MenuId.EditorTitle, when: ResourceContextKey.Resource.isEqualTo(that.environmentService.settingsResource.toString()), @@ -295,7 +296,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon super({ id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, title: { value: nls.localize('openSettingsJson', "Open Settings (JSON)"), original: 'Open Settings (JSON)' }, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, menu: [{ id: MenuId.EditorTitle, when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()), @@ -815,7 +816,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openGlobalKeybindings', title: { value: nls.localize('openGlobalKeybindings', "Open Keyboard Shortcuts"), original: 'Open Keyboard Shortcuts' }, category, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, keybinding: { when: null, weight: KeybindingWeight.WorkbenchContrib, @@ -832,8 +833,9 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ] }); } - run(accessor: ServicesAccessor) { - return accessor.get(IPreferencesService).openGlobalKeybindingSettings(false); + run(accessor: ServicesAccessor, args: string | undefined) { + const query = typeof args === 'string' ? args : undefined; + return accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query }); } }); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { @@ -871,7 +873,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openGlobalKeybindingsFile', title: { value: nls.localize('openGlobalKeybindingsFile', "Open Keyboard Shortcuts (JSON)"), original: 'Open Keyboard Shortcuts (JSON)' }, category, - icon: { id: 'codicon/go-to-file' }, + icon: preferencesOpenSettingsIcon, menu: [ { id: MenuId.CommandPalette }, { @@ -960,7 +962,20 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon handler: (accessor, args: any) => { const editorPane = accessor.get(IEditorService).activeEditorPane; if (editorPane instanceof KeybindingsEditor) { - editorPane.defineKeybinding(editorPane.activeKeybindingEntry!); + editorPane.defineKeybinding(editorPane.activeKeybindingEntry!, false); + } + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_ADD, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_A), + handler: (accessor, args: any) => { + const editorPane = accessor.get(IEditorService).activeEditorPane; + if (editorPane instanceof KeybindingsEditor) { + editorPane.defineKeybinding(editorPane.activeKeybindingEntry!, true); } } }); @@ -1091,7 +1106,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS), - primary: KeyCode.DownArrow, + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, handler: (accessor, args: any) => { const editorPane = accessor.get(IEditorService).activeEditorPane; if (editorPane instanceof KeybindingsEditor) { @@ -1109,7 +1124,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: commandId, title: OPEN_SETTINGS2_ACTION_TITLE, - icon: { id: 'codicon/go-to-file' } + icon: preferencesOpenSettingsIcon }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource!.toString()), WorkbenchStateContext.isEqualTo('workspace')), group: 'navigation', @@ -1134,7 +1149,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon command: { id: commandId, title: OPEN_SETTINGS2_ACTION_TITLE, - icon: { id: 'codicon/go-to-file' } + icon: preferencesOpenSettingsIcon }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.getFolderSettingsResource(folder.uri)!.toString())), group: 'navigation', diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index 8b095ecb..e6955647 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -55,7 +55,7 @@ export class ConfigureLanguageBasedSettingsAction extends Action { if (pick) { const modeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase()); if (typeof modeId === 'string') { - return this.preferencesService.openGlobalSettings(true, { editSetting: `[${modeId}]` }); + return this.preferencesService.openGlobalSettings(true, { revealSetting: { key: `[${modeId}]`, edit: true } }); } } return undefined; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 8c39361f..8f471950 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -111,7 +111,8 @@ export class PreferencesEditor extends EditorPane { placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"), focusKey: this.searchFocusContextKey, showResultCount: true, - ariaLive: 'assertive' + ariaLive: 'assertive', + history: [], })); this._register(this.searchWidget.onDidChange(value => this.onInputChanged())); this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget)); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts b/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts new file mode 100644 index 00000000..ec9d0a95 --- /dev/null +++ b/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const settingsGroupExpandedIcon = registerIcon('settings-group-expanded', Codicon.chevronDown, localize('settingsGroupExpandedIcon', 'Icon for an expanded section in the split JSON settings editor.')); +export const settingsGroupCollapsedIcon = registerIcon('settings-group-collapsed', Codicon.chevronRight, localize('settingsGroupCollapsedIcon', 'Icon for an collapsed section in the split JSON settings editor.')); +export const settingsScopeDropDownIcon = registerIcon('settings-folder-dropdown', Codicon.triangleDown, localize('settingsScopeDropDownIcon', 'Icon for the folder dropdown button in the split JSON settings editor.')); +export const settingsMoreActionIcon = registerIcon('settings-more-action', Codicon.gear, localize('settingsMoreActionIcon', 'Icon for the \'more actions\' action in the settings UI.')); + +export const keybindingsRecordKeysIcon = registerIcon('keybindings-record-keys', Codicon.recordKeys, localize('keybindingsRecordKeysIcon', 'Icon for the \'record keys\' action in the keybinding UI.')); +export const keybindingsSortIcon = registerIcon('keybindings-sort', Codicon.sortPrecedence, localize('keybindingsSortIcon', 'Icon for the \'sort by precedence\' toggle in the keybinding UI.')); + +export const keybindingsEditIcon = registerIcon('keybindings-edit', Codicon.edit, localize('keybindingsEditIcon', 'Icon for the edit action in the keybinding UI.')); +export const keybindingsAddIcon = registerIcon('keybindings-add', Codicon.add, localize('keybindingsAddIcon', 'Icon for the add action in the keybinding UI.')); + +export const settingsEditIcon = registerIcon('settings-edit', Codicon.edit, localize('settingsEditIcon', 'Icon for the edit action in the settings UI.')); +export const settingsAddIcon = registerIcon('settings-add', Codicon.add, localize('settingsAddIcon', 'Icon for the add action in the settings UI.')); + +export const settingsRemoveIcon = registerIcon('settings-remove', Codicon.close, localize('settingsRemoveIcon', 'Icon for the remove action in the settings UI.')); +export const settingsDiscardIcon = registerIcon('settings-discard', Codicon.discard, localize('preferencesDiscardIcon', 'Icon for the discard action in the settings UI.')); + +export const preferencesClearInputIcon = registerIcon('preferences-clear-input', Codicon.clearAll, localize('preferencesClearInput', 'Icon for clear input in the settings and keybinding UI.')); +export const preferencesOpenSettingsIcon = registerIcon('preferences-open-settings', Codicon.goToFile, localize('preferencesOpenSettings', 'Icon for open settings commands.')); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index b13338f9..49511397 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -25,12 +25,15 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; -import { DefaultSettingsHeaderWidget, EditPreferenceWidget, SettingsGroupTitleWidget, SettingsHeaderWidget, preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; +import { DefaultSettingsHeaderWidget, EditPreferenceWidget, SettingsGroupTitleWidget, SettingsHeaderWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { IFilterResult, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultSettingsEditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IMarkerService, IMarkerData, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { FindDecorations } from 'vs/editor/contrib/find/findDecorations'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { settingsEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; export interface IPreferencesRenderer extends IDisposable { readonly preferencesModel: IPreferencesEditorModel; @@ -569,15 +572,10 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr private createDecoration(range: IRange): IModelDeltaDecoration { return { range, - options: FilteredMatchesRenderer._FIND_MATCH + options: FindDecorations._FIND_MATCH_DECORATION }; } - private static readonly _FIND_MATCH = ModelDecorationOptions.register({ - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'findMatch' - }); - private computeHiddenRanges(filteredGroups: ISettingsGroup[] | undefined, allSettingsGroups: ISettingsGroup[]): IRange[] { // Hide the contents of hidden groups const notMatchesRanges: IRange[] = []; @@ -614,15 +612,10 @@ export class HighlightMatchesRenderer extends Disposable { this.decorationIds = this.editor.deltaDecorations(this.decorationIds, matches.map(match => this.createDecoration(match))); } - private static readonly _FIND_MATCH = ModelDecorationOptions.register({ - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'findMatch' - }); - private createDecoration(range: IRange): IModelDeltaDecoration { return { range, - options: HighlightMatchesRenderer._FIND_MATCH + options: FindDecorations._FIND_MATCH_DECORATION }; } @@ -746,7 +739,7 @@ class EditSettingRenderer extends Disposable { const decorations = this.editor.getLineDecorations(line); if (decorations) { for (const { options } of decorations) { - if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf(preferencesEditIcon.classNames) === -1) { + if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf(ThemeIcon.asClassName(settingsEditIcon)) === -1) { return false; } } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index a736bea3..67b174bd 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { HistoryInputBox, IHistoryInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; import { Widget } from 'vs/base/browser/ui/widget'; import { Action, IAction } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; @@ -21,22 +21,23 @@ import { Position } from 'vs/editor/common/core/position'; import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { Schemas } from 'vs/base/common/network'; import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISettingsGroup, IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { isEqual } from 'vs/base/common/resources'; -import { registerIcon, Codicon } from 'vs/base/common/codicons'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { settingsEditIcon, settingsGroupCollapsedIcon, settingsGroupExpandedIcon, settingsScopeDropDownIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; +import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; export class SettingsHeaderWidget extends Widget implements IViewZone { @@ -171,13 +172,23 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { this._register(focusTracker.onDidFocus(() => this.toggleFocus(true))); this._register(focusTracker.onDidBlur(() => this.toggleFocus(false))); - this.icon = DOM.append(this.titleContainer, DOM.$('.codicon.codicon-chevron-down')); + this.icon = DOM.append(this.titleContainer, DOM.$('')); this.title = DOM.append(this.titleContainer, DOM.$('.title')); this.title.textContent = this.settingsGroup.title + ` (${this.settingsGroup.sections.reduce((count, section) => count + section.settings.length, 0)})`; + this.updateTwisty(false); this.layout(); } + private getTwistyIcon(isCollapsed: boolean): ThemeIcon { + return isCollapsed ? settingsGroupCollapsedIcon : settingsGroupExpandedIcon; + } + + private updateTwisty(collapse: boolean) { + this.icon.classList.remove(...ThemeIcon.asClassNameArray(this.getTwistyIcon(!collapse))); + this.icon.classList.add(...ThemeIcon.asClassNameArray(this.getTwistyIcon(collapse))); + } + render() { if (!this.settingsGroup.range) { // #61352 @@ -193,6 +204,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { toggleCollapse(collapse: boolean) { this.titleContainer.classList.toggle('collapsed', collapse); + this.updateTwisty(collapse); } toggleFocus(focus: boolean): void { @@ -257,6 +269,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { private collapse(collapse: boolean) { if (collapse !== this.isCollapsed()) { this.titleContainer.classList.toggle('collapsed', collapse); + this.updateTwisty(collapse); this._onToggled.fire(collapse); } } @@ -345,7 +358,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem { this.container = container; this.labelElement = DOM.$('.action-title'); this.detailsElement = DOM.$('.action-details'); - this.dropDownElement = DOM.$('.dropdown-icon.codicon.codicon-triangle-down.hide'); + this.dropDownElement = DOM.$('.dropdown-icon.hide' + ThemeIcon.asCSSSelector(settingsScopeDropDownIcon)); this.anchorElement = DOM.$('a.action-label.folder-settings', { role: 'button', 'aria-haspopup': 'true', @@ -604,7 +617,7 @@ export class SettingsTargetsWidget extends Widget { } } -export interface SearchOptions extends IInputOptions { +export interface SearchOptions extends IHistoryInputOptions { focusKey?: IContextKey; showResultCount?: boolean; ariaLive?: string; @@ -617,7 +630,7 @@ export class SearchWidget extends Widget { private countElement!: HTMLElement; private searchContainer!: HTMLElement; - inputBox!: InputBox; + inputBox!: HistoryInputBox; private controlsDiv!: HTMLElement; private readonly _onDidChange: Emitter = this._register(new Emitter()); @@ -629,7 +642,8 @@ export class SearchWidget extends Widget { constructor(parent: HTMLElement, protected options: SearchOptions, @IContextViewService private readonly contextViewService: IContextViewService, @IInstantiationService protected instantiationService: IInstantiationService, - @IThemeService private readonly themeService: IThemeService + @IThemeService private readonly themeService: IThemeService, + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(); this.create(parent); @@ -678,8 +692,8 @@ export class SearchWidget extends Widget { this._register(this.inputBox.onDidChange(value => this._onDidChange.fire(value))); } - protected createInputBox(parent: HTMLElement): InputBox { - const box = this._register(new InputBox(parent, this.contextViewService, this.options)); + protected createInputBox(parent: HTMLElement): HistoryInputBox { + const box = this._register(new ContextScopedHistoryInputBox(parent, this.contextViewService, this.options, this.contextKeyService)); this._register(attachInputBoxStyler(box, this.themeService)); return box; @@ -746,8 +760,6 @@ export class SearchWidget extends Widget { } } -export const preferencesEditIcon = registerIcon('preferences-edit', Codicon.edit, localize('preferencesEditIcon', 'Icon for the edit action in preferences.')); - export class EditPreferenceWidget extends Disposable { private _line: number = -1; @@ -785,7 +797,7 @@ export class EditPreferenceWidget extends Disposable { this._line = line; newDecoration.push({ options: { - glyphMarginClassName: preferencesEditIcon.classNames, + glyphMarginClassName: ThemeIcon.asClassName(settingsEditIcon), glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage), stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, }, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 941cd7b5..7abb3fca 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -31,12 +31,11 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { badgeBackground, badgeForeground, contrastBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IUserDataAutoSyncEnablementService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/common/editor'; @@ -47,12 +46,13 @@ import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickE import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/contrib/preferences/common/preferences'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { preferencesClearInputIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; export const enum SettingsFocusContext { Search, @@ -91,7 +91,7 @@ export class SettingsEditor2 extends EditorPane { private static CONFIG_SCHEMA_UPDATE_DELAYER = 500; private static readonly SUGGESTIONS: string[] = [ - `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', '@tag:sync', `@${EXTENSION_SETTING_TAG}` + `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', '@tag:sync', `@${ID_SETTING_TAG}`, `@${EXTENSION_SETTING_TAG}`, `@${FEATURE_SETTING_TAG}scm`, `@${FEATURE_SETTING_TAG}explorer`, `@${FEATURE_SETTING_TAG}search`, `@${FEATURE_SETTING_TAG}debug`, `@${FEATURE_SETTING_TAG}extensions`, `@${FEATURE_SETTING_TAG}terminal`, `@${FEATURE_SETTING_TAG}task`, `@${FEATURE_SETTING_TAG}problems`, `@${FEATURE_SETTING_TAG}output`, `@${FEATURE_SETTING_TAG}comments`, `@${FEATURE_SETTING_TAG}remote`, `@${FEATURE_SETTING_TAG}timeline` ]; private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean { @@ -173,7 +173,6 @@ export class SettingsEditor2 extends EditorPane { @IStorageService private readonly storageService: IStorageService, @INotificationService private readonly notificationService: INotificationService, @IEditorGroupsService protected editorGroupService: IEditorGroupsService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService ) { @@ -202,8 +201,6 @@ export class SettingsEditor2 extends EditorPane { this.onConfigUpdate(e.affectedKeys); } })); - - storageKeysSyncRegistryService.registerStorageKey({ key: SETTINGS_AUTOSAVE_NOTIFIED_KEY, version: 1 }); } get minimumWidth(): number { return 375; } @@ -439,7 +436,7 @@ export class SettingsEditor2 extends EditorPane { const searchContainer = DOM.append(this.headerContainer, $('.search-container')); - const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), 'codicon-clear-all', false, () => { this.clearSearchResults(); return Promise.resolve(null); }); + const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), ThemeIcon.asClassName(preferencesClearInputIcon), false, () => { this.clearSearchResults(); return Promise.resolve(null); }); this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, { triggerCharacters: ['@'], @@ -664,7 +661,7 @@ export class SettingsEditor2 extends EditorPane { this.settingRenderers = this.instantiationService.createInstance(SettingTreeRenderers); this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type))); this._register(this.settingRenderers.onDidOpenSettings(settingKey => { - this.openSettingsFile({ editSetting: settingKey }); + this.openSettingsFile({ revealSetting: { key: settingKey, edit: true } }); })); this._register(this.settingRenderers.onDidClickSettingLink(settingName => this.onDidClickSetting(settingName))); this._register(this.settingRenderers.onDidFocusSetting(element => { @@ -737,8 +734,8 @@ export class SettingsEditor2 extends EditorPane { private notifyNoSaveNeeded() { if (!this.storageService.getBoolean(SETTINGS_AUTOSAVE_NOTIFIED_KEY, StorageScope.GLOBAL, false)) { - this.storageService.store(SETTINGS_AUTOSAVE_NOTIFIED_KEY, true, StorageScope.GLOBAL); - this.notificationService.info(localize('settingsNoSaveNeeded', "Your changes are automatically saved as you edit.")); + this.storageService.store(SETTINGS_AUTOSAVE_NOTIFIED_KEY, true, StorageScope.GLOBAL, StorageTarget.USER); + this.notificationService.info(localize('settingsNoSaveNeeded', "Changes to settings are saved automatically.")); } } @@ -968,7 +965,7 @@ export class SettingsEditor2 extends EditorPane { const groups = this.defaultSettingsEditorModel.settingsGroups.slice(1); // Without commonlyUsed const dividedGroups = collections.groupBy(groups, g => g.extensionInfo ? 'extension' : 'core'); - const settingsResult = resolveSettingsTree(tocData, dividedGroups.core); + const settingsResult = resolveSettingsTree(tocData, dividedGroups.core, this.logService); const resolvedSettingsRoot = settingsResult.tree; // Warn for settings not included in layout @@ -982,7 +979,7 @@ export class SettingsEditor2 extends EditorPane { this.hasWarnedMissingSettings = true; } - const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core); + const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core, this.logService); resolvedSettingsRoot.children!.unshift(commonlyUsed.tree); resolvedSettingsRoot.children!.push(resolveExtensionsSettings(dividedGroups.extension || [])); @@ -1138,18 +1135,22 @@ export class SettingsEditor2 extends EditorPane { private triggerSearch(query: string): Promise { this.viewState.tagFilters = new Set(); this.viewState.extensionFilters = new Set(); + this.viewState.featureFilters = new Set(); + this.viewState.idFilters = new Set(); if (query) { const parsedQuery = parseQuery(query); query = parsedQuery.query; parsedQuery.tags.forEach(tag => this.viewState.tagFilters!.add(tag)); parsedQuery.extensionFilters.forEach(extensionId => this.viewState.extensionFilters!.add(extensionId)); + parsedQuery.featureFilters!.forEach(feature => this.viewState.featureFilters!.add(feature)); + parsedQuery.idFilters!.forEach(id => this.viewState.idFilters!.add(id)); } if (query && query !== '@') { query = this.parseSettingFromJSON(query) || query; return this.triggerFilterPreferences(query); } else { - if ((this.viewState.tagFilters && this.viewState.tagFilters.size) || (this.viewState.extensionFilters && this.viewState.extensionFilters.size)) { + if (this.viewState.tagFilters.size || this.viewState.extensionFilters.size || this.viewState.featureFilters.size || this.viewState.idFilters.size) { this.searchResultModel = this.createFilterModel(); } else { this.searchResultModel = null; @@ -1306,9 +1307,11 @@ export class SettingsEditor2 extends EditorPane { this.tocTreeModel.update(); } - this.tocTree.setFocus([]); - this.viewState.filterToCategory = undefined; - this.tocTree.expandAll(); + if (type === SearchResultIdx.Local) { + this.tocTree.setFocus([]); + this.viewState.filterToCategory = undefined; + this.tocTree.expandAll(); + } this.refreshTOCTree(); this.renderTree(undefined, true); @@ -1382,12 +1385,12 @@ export class SettingsEditor2 extends EditorPane { } private layoutTrees(dimension: DOM.Dimension): void { - const listHeight = dimension.height - (76 + 11 /* header height + padding*/); + const listHeight = dimension.height - (72 + 11 /* header height + editor padding */); const settingsTreeHeight = listHeight - 14; this.settingsTreeContainer.style.height = `${settingsTreeHeight}px`; this.settingsTree.layout(settingsTreeHeight, dimension.width); - const tocTreeHeight = listHeight - 17; + const tocTreeHeight = settingsTreeHeight - 1; this.tocTreeContainer.style.height = `${tocTreeHeight}px`; this.tocTree.layout(tocTreeHeight); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index b2eb1cb2..86b50e98 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -4,23 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { ISetting } from 'vs/workbench/services/preferences/common/preferences'; - -export interface ITOCEntry { +export interface ITOCEntry { id: string; label: string; - - children?: ITOCEntry[]; - settings?: Array; + children?: ITOCEntry[]; + settings?: Array; } -export const commonlyUsedData: ITOCEntry = { +export const commonlyUsedData: ITOCEntry = { id: 'commonlyUsed', label: localize('commonlyUsed', "Commonly Used"), - settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations'] + settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations', 'workbench.editor.enablePreview'] }; -export const tocData: ITOCEntry = { +export const tocData: ITOCEntry = { id: 'root', label: 'root', children: [ diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 2618f9f4..3b26e64a 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -38,7 +38,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, settingKeyToDisplayFormat, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; @@ -55,6 +55,8 @@ import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ILogService } from 'vs/platform/log/common/log'; +import { settingsMoreActionIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const $ = DOM.$; @@ -257,15 +259,15 @@ function getListDisplayValue(element: SettingsTreeSettingElement): IListDataItem }); } -export function resolveSettingsTree(tocData: ITOCEntry, coreSettingsGroups: ISettingsGroup[]): { tree: ITOCEntry, leftoverSettings: Set } { +export function resolveSettingsTree(tocData: ITOCEntry, coreSettingsGroups: ISettingsGroup[], logService: ILogService): { tree: ITOCEntry, leftoverSettings: Set } { const allSettings = getFlatSettings(coreSettingsGroups); return { - tree: _resolveSettingsTree(tocData, allSettings), + tree: _resolveSettingsTree(tocData, allSettings, logService), leftoverSettings: allSettings }; } -export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { +export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { const settingsGroupToEntry = (group: ISettingsGroup) => { const flatSettings = arrays.flatten( group.sections.map(section => section.settings)); @@ -288,17 +290,17 @@ export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { }; } -function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set): ITOCEntry { - let children: ITOCEntry[] | undefined; +function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set, logService: ILogService): ITOCEntry { + let children: ITOCEntry[] | undefined; if (tocData.children) { children = tocData.children - .map(child => _resolveSettingsTree(child, allSettings)) + .map(child => _resolveSettingsTree(child, allSettings, logService)) .filter(child => (child.children && child.children.length) || (child.settings && child.settings.length)); } let settings: ISetting[] | undefined; if (tocData.settings) { - settings = arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern))); + settings = arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern, logService))); } if (!children && !settings) { @@ -313,7 +315,7 @@ function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set): I }; } -function getMatchingSettings(allSettings: Set, pattern: string): ISetting[] { +function getMatchingSettings(allSettings: Set, pattern: string, logService: ILogService): ISetting[] { const result: ISetting[] = []; allSettings.forEach(s => { @@ -323,17 +325,20 @@ function getMatchingSettings(allSettings: Set, pattern: string): ISett } }); + if (!result.length) { + logService.warn(`Settings pattern "${pattern}" doesn't match any settings`); + } return result.sort((a, b) => a.key.localeCompare(b.key)); } const settingPatternCache = new Map(); -function createSettingMatchRegExp(pattern: string): RegExp { +export function createSettingMatchRegExp(pattern: string): RegExp { pattern = escapeRegExpCharacters(pattern) .replace(/\\\*/g, '.*'); - return new RegExp(`^${pattern}`, 'i'); + return new RegExp(`^${pattern}$`, 'i'); } function settingMatches(s: ISetting, pattern: string): boolean { @@ -631,22 +636,12 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const toolbar = new ToolBar(container, this._contextMenuService, { toggleMenuTitle, - renderDropdownAsChildElement: true + renderDropdownAsChildElement: true, + moreIcon: ThemeIcon.asCSSIcon(settingsMoreActionIcon) // change icon from ellipsis to gear }); return toolbar; } - private fixToolbarIcon(toolbar: ToolBar): void { - const button = toolbar.getElement().querySelector('.codicon-toolbar-more'); - if (button) { - (button).tabIndex = 0; - - // change icon from ellipsis to gear - (button).classList.add('codicon-gear'); - (button).classList.remove('codicon-toolbar-more'); - } - } - protected renderSettingElement(node: ITreeNode, index: number, template: ISettingItemTemplate | ISettingBoolItemTemplate): void { const element = node.element; template.context = element; @@ -654,7 +649,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const actions = this.disposableActionFactory(element.setting); actions.forEach(a => template.elementDisposables?.add(a)); template.toolbar.setActions([], [...this.settingActions, ...actions]); - this.fixToolbarIcon(template.toolbar); const setting = element.setting; @@ -1332,7 +1326,8 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre const description = (enumDescriptions && enumDescriptions[index] && (enumDescriptionsAreMarkdown ? fixSettingLinks(enumDescriptions[index], false) : enumDescriptions[index])); return { text: enumItemLabels && enumItemLabels[index] ? enumItemLabels[index] : data, - description: enumItemLabels && enumItemLabels[index] ? `[${data}] ${description}` : description, + detail: enumItemLabels && enumItemLabels[index] ? data : '', + description, descriptionIsMarkdown: enumDescriptionsAreMarkdown, descriptionMarkdownActionHandler: { callback: (content) => { @@ -1897,8 +1892,11 @@ export class SettingsTree extends WorkbenchObjectTree { // applying an opacity to the link color. const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.9)); collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description { color: ${fgWithOpacity}; }`); - collector.addRule(`.settings-editor > .settings-body .settings-toc-container .monaco-list-row:not(.selected) { color: ${fgWithOpacity}; }`); + + // Hack for subpixel antialiasing + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides, + .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored { color: ${fgWithOpacity}; }`); } const errorColor = theme.getColor(errorForeground); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 12a988e5..e2f231a8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as arrays from 'vs/base/common/arrays'; -import { isFalsyOrWhitespace } from 'vs/base/common/strings'; +import { escapeRegExpCharacters, isFalsyOrWhitespace } from 'vs/base/common/strings'; import { isArray, withUndefinedAsNull, isUndefinedOrNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; -import { ITOCEntry, knownAcronyms, knownTermMappings } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; +import { ITOCEntry, knownAcronyms, knownTermMappings, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -25,6 +25,8 @@ export interface ISettingsEditorViewState { settingsTarget: SettingsTarget; tagFilters?: Set; extensionFilters?: Set; + featureFilters?: Set; + idFilters?: Set; filterToCategory?: SettingsTreeGroupElement; } @@ -288,12 +290,49 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { return Array.from(extensionFilters).some(extensionId => extensionId.toLowerCase() === this.setting.extensionInfo!.id.toLowerCase()); } + + matchesAnyFeature(featureFilters?: Set): boolean { + if (!featureFilters || !featureFilters.size) { + return true; + } + + const features = tocData.children!.find(child => child.id === 'features'); + + return Array.from(featureFilters).some(filter => { + if (features && features.children) { + const feature = features.children.find(feature => 'features/' + filter === feature.id); + if (feature) { + const patterns = feature.settings?.map(setting => createSettingMatchRegExp(setting)); + return patterns && !this.setting.extensionInfo && patterns.some(pattern => pattern.test(this.setting.key.toLowerCase())); + } else { + return false; + } + } else { + return false; + } + }); + } + + matchesAnyId(idFilters?: Set): boolean { + if (!idFilters || !idFilters.size) { + return true; + } + return idFilters.has(this.setting.key); + } +} + + +function createSettingMatchRegExp(pattern: string): RegExp { + pattern = escapeRegExpCharacters(pattern) + .replace(/\\\*/g, '.*'); + + return new RegExp(`^${pattern}$`, 'i'); } export class SettingsTreeModel { protected _root!: SettingsTreeGroupElement; private _treeElementsBySettingName = new Map(); - private _tocRoot!: ITOCEntry; + private _tocRoot!: ITOCEntry; constructor( protected _viewState: ISettingsEditorViewState, @@ -349,14 +388,14 @@ export class SettingsTreeModel { }); } - private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { + private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { const depth = parent ? this.getDepth(parent) + 1 : 0; const element = new SettingsTreeGroupElement(tocEntry.id, undefined, tocEntry.label, depth, false); const children: SettingsTreeGroupChild[] = []; if (tocEntry.settings) { - const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) + const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) .filter(el => el.setting.deprecationMessage ? el.isConfigured : true); children.push(...settingChildren); } @@ -608,8 +647,9 @@ export class SearchResultModel extends SettingsTreeModel { // Save time, filter children in the search model instead of relying on the tree filter, which still requires heights to be calculated. const isRemote = !!this.environmentService.remoteAuthority; + this.root.children = this.root.children - .filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget, isRemote) && child.matchesAnyExtension(this._viewState.extensionFilters)); + .filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget, isRemote) && child.matchesAnyExtension(this._viewState.extensionFilters) && child.matchesAnyId(this._viewState.idFilters) && (this.containsValidFeature() ? child.matchesAnyFeature(this._viewState.featureFilters) : true)); if (this.newExtensionSearchResults && this.newExtensionSearchResults.filterMatches.length) { const resultExtensionIds = this.newExtensionSearchResults.filterMatches @@ -623,6 +663,22 @@ export class SearchResultModel extends SettingsTreeModel { } } + private containsValidFeature(): boolean { + if (!this._viewState.featureFilters || !this._viewState.featureFilters.size || !tocData.children) { + return false; + } + + const features = tocData.children.find(child => child.id === 'features'); + + if (features && features.children) { + return Array.from(this._viewState.featureFilters).some(filter => { + return features.children?.find(feature => 'features/' + filter === feature.id); + }); + } else { + return false; + } + } + private getFlatSettings(): ISetting[] { const flatSettings: ISetting[] = []; arrays.coalesce(this.getUniqueResults()) @@ -639,13 +695,19 @@ export interface IParsedQuery { tags: string[]; query: string; extensionFilters: string[]; + idFilters: string[]; + featureFilters: string[]; } const tagRegex = /(^|\s)@tag:("([^"]*)"|[^"]\S*)/g; const extensionRegex = /(^|\s)@ext:("([^"]*)"|[^"]\S*)?/g; +const featureRegex = /(^|\s)@feature:("([^"]*)"|[^"]\S*)?/g; +const idRegex = /(^|\s)@id:("([^"]*)"|[^"]\S*)?/g; export function parseQuery(query: string): IParsedQuery { const tags: string[] = []; const extensions: string[] = []; + const features: string[] = []; + const ids: string[] = []; query = query.replace(tagRegex, (_, __, quotedTag, tag) => { tags.push(tag || quotedTag); return ''; @@ -664,11 +726,29 @@ export function parseQuery(query: string): IParsedQuery { return ''; }); + query = query.replace(featureRegex, (_, __, quotedFeature, feature) => { + const featureQuery: string = feature || quotedFeature; + if (featureQuery) { + features.push(...featureQuery.split(',').map(s => s.trim()).filter(s => !isFalsyOrWhitespace(s))); + } + return ''; + }); + + query = query.replace(idRegex, (_, __, quotedId, id) => { + const idRegex: string = id || quotedId; + if (idRegex) { + ids.push(...idRegex.split(',').map(s => s.trim()).filter(s => !isFalsyOrWhitespace(s))); + } + return ''; + }); + query = query.trim(); return { tags, extensionFilters: extensions, + featureFilters: features, + idFilters: ids, query }; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 944f05ef..9994ccc1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -23,8 +23,8 @@ import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { editorWidgetBorder, focusBorder, foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listFocusBackground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; -import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { settingsDiscardIcon, settingsEditIcon, settingsRemoveIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; const $ = DOM.$; export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "The foreground color for a section header or active title.")); @@ -496,14 +496,14 @@ export class ListSettingWidget extends AbstractListSettingWidget protected getActionsForItem(item: IListDataItem, idx: number): IAction[] { return [ { - class: preferencesEditIcon.classNames, + class: ThemeIcon.asClassName(settingsEditIcon), enabled: true, id: 'workbench.action.editListItem', tooltip: this.getLocalizedStrings().editActionTooltip, run: () => this.editSetting(idx) }, { - class: 'codicon-close', + class: ThemeIcon.asClassName(settingsRemoveIcon), enabled: true, id: 'workbench.action.removeListItem', tooltip: this.getLocalizedStrings().deleteActionTooltip, @@ -740,7 +740,7 @@ export class ObjectSettingWidget extends AbstractListSettingWidget { styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), collapseByDefault: true, - horizontalScrolling: false + horizontalScrolling: false, + hideTwistiesOfChildlessElements: true }; super( diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index 62f57e04..92810609 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -6,10 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ISettingsEditorModel, ISearchResult } from 'vs/workbench/services/preferences/common/preferences'; -import { IEditorPane } from 'vs/workbench/common/editor'; -import { IKeybindingItemEntry } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event } from 'vs/base/common/event'; export interface IWorkbenchSettingsConfiguration { workbench: { @@ -43,29 +40,6 @@ export interface ISearchProvider { searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise; } -export interface IKeybindingsEditorPane extends IEditorPane { - - readonly activeKeybindingEntry: IKeybindingItemEntry | null; - readonly onDefineWhenExpression: Event; - readonly onLayout: Event; - - search(filter: string): void; - focusSearch(): void; - clearSearchResults(): void; - focusKeybindings(): void; - recordSearchKeys(): void; - toggleSortByPrecedence(): void; - selectKeybinding(keybindingEntry: IKeybindingItemEntry): void; - defineKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - defineWhenExpression(keybindingEntry: IKeybindingItemEntry): void; - updateKeybinding(keybindingEntry: IKeybindingItemEntry, key: string, when: string | undefined): Promise; - removeKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - resetKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - copyKeybinding(keybindingEntry: IKeybindingItemEntry): Promise; - copyKeybindingCommand(keybindingEntry: IKeybindingItemEntry): Promise; - showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): void; -} - export const SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'settings.action.clearSearchResults'; export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; @@ -83,6 +57,7 @@ export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.edit export const KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS = 'keybindings.editor.recordSearchKeys'; export const KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE = 'keybindings.editor.toggleSortByPrecedence'; export const KEYBINDINGS_EDITOR_COMMAND_DEFINE = 'keybindings.editor.defineKeybinding'; +export const KEYBINDINGS_EDITOR_COMMAND_ADD = 'keybindings.editor.addKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN = 'keybindings.editor.defineWhenExpression'; export const KEYBINDINGS_EDITOR_COMMAND_REMOVE = 'keybindings.editor.removeKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_RESET = 'keybindings.editor.resetKeybinding'; @@ -95,5 +70,6 @@ export const KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS = 'keybindings.editor.show export const MODIFIED_SETTING_TAG = 'modified'; export const EXTENSION_SETTING_TAG = 'ext:'; - +export const FEATURE_SETTING_TAG = 'feature:'; +export const ID_SETTING_TAG = 'id:'; export const KEYBOARD_LAYOUT_OPEN_PICKER = 'workbench.action.openKeyboardLayoutPicker'; diff --git a/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts b/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts index 8113f3d3..b2ae0a9e 100644 --- a/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts +++ b/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts @@ -147,7 +147,9 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -155,7 +157,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -163,7 +167,9 @@ suite('SettingsTree', () => { { tags: ['foo'], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -171,7 +177,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: 'foo' + query: 'foo', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -179,7 +187,9 @@ suite('SettingsTree', () => { { tags: ['foo', 'modified'], extensionFilters: [], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -187,7 +197,9 @@ suite('SettingsTree', () => { { tags: ['foo', 'modified'], extensionFilters: [], - query: 'my query' + query: 'my query', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -195,7 +207,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: 'test query' + query: 'test query', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -203,7 +217,9 @@ suite('SettingsTree', () => { { tags: ['modified'], extensionFilters: [], - query: 'test' + query: 'test', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -211,7 +227,9 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: [], - query: 'query has @ for some reason' + query: 'query has @ for some reason', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -219,7 +237,9 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: ['github.vscode-pull-request-github'], - query: '' + query: '', + featureFilters: [], + idFilters: [] }); testParseQuery( @@ -227,7 +247,47 @@ suite('SettingsTree', () => { { tags: [], extensionFilters: ['github.vscode-pull-request-github', 'vscode.git'], - query: '' + query: '', + featureFilters: [], + idFilters: [] + }); + testParseQuery( + '@feature:scm', + { + tags: [], + extensionFilters: [], + featureFilters: ['scm'], + query: '', + idFilters: [] + }); + + testParseQuery( + '@feature:scm,terminal', + { + tags: [], + extensionFilters: [], + featureFilters: ['scm', 'terminal'], + query: '', + idFilters: [] + }); + testParseQuery( + '@id:files.autoSave', + { + tags: [], + extensionFilters: [], + featureFilters: [], + query: '', + idFilters: ['files.autoSave'] + }); + + testParseQuery( + '@id:files.autoSave,terminal.integrated.commandsToSkipShell', + { + tags: [], + extensionFilters: [], + featureFilters: [], + query: '', + idFilters: ['files.autoSave', 'terminal.integrated.commandsToSkipShell'] }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index d24b9139..d742a1d2 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -22,12 +22,14 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; -import { stripCodicons } from 'vs/base/common/codicons'; +import { Codicon, stripCodicons } from 'vs/base/common/codicons'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { TriggerAction } from 'vs/platform/quickinput/browser/pickerQuickAccess'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider { @@ -60,7 +62,8 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @IPreferencesService private readonly preferencesService: IPreferencesService, ) { super({ showAlias: !Language.isDefaultVariant(), @@ -91,7 +94,17 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce return [ ...this.getCodeEditorCommandPicks(), ...this.getGlobalCommandPicks(disposables) - ]; + ].map(c => ({ + ...c, + buttons: [{ + iconClass: Codicon.gear.classNames, + tooltip: localize('configure keybinding', "Configure Keybinding"), + }], + trigger: (): TriggerAction => { + this.preferencesService.openGlobalKeybindingSettings(false, { query: `@command:${c.commandId}` }); + return TriggerAction.CLOSE_PICKER; + }, + })); } private getGlobalCommandPicks(disposables: DisposableStore): ICommandQuickPick[] { @@ -100,7 +113,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce const globalCommandsMenu = this.menuService.createMenu(MenuId.CommandPalette, scopedContextKeyService); const globalCommandsMenuActions = globalCommandsMenu.getActions() .reduce((r, [, actions]) => [...r, ...actions], >[]) - .filter(action => action instanceof MenuItemAction) as MenuItemAction[]; + .filter(action => action instanceof MenuItemAction && action.enabled) as MenuItemAction[]; for (const action of globalCommandsMenuActions) { diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 386eb37b..c5a10561 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -36,7 +36,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private updateMode: string | undefined; private debugConsoleWordWrap: boolean | undefined; private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; - private enableExperimentalProxyLoginDialog: boolean | undefined; + private enableExperimentalProxyLoginDialog = true; // default constructor( @IHostService private readonly hostService: IHostService, diff --git a/src/vs/workbench/contrib/remote/browser/media/tunnelView.css b/src/vs/workbench/contrib/remote/browser/media/tunnelView.css index 659c8754..55776ca9 100644 --- a/src/vs/workbench/contrib/remote/browser/media/tunnelView.css +++ b/src/vs/workbench/contrib/remote/browser/media/tunnelView.css @@ -3,6 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.customview-tree .tunnel-view-label { +.pane-body:not(.wide) .ports-view .monaco-icon-label, +.pane-body:not(.wide) .ports-view .monaco-icon-label { flex: 1; } + +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row:hover:not(.highlighted) .monaco-icon-label, +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row.focused .monaco-icon-label { + flex: 1; +} + +.pane-body.wide .ports-view .monaco-list .monaco-list-row .actionBarContainer, +.ports-view .monaco-list .monaco-list-row .actionBarContainer { + flex: 1 0 auto; +} + +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row .actionBarContainer { + flex: 0 0 auto; +} + +.ports-view.actions-right .monaco-list .monaco-list-row .actionBarContainer, +.pane-body:not(.wide) .ports-view .monaco-list .monaco-list-row .actionBarContainer { + text-align: right; +} + +.pane-body.wide .ports-view .monaco-action-bar { + margin-left: 10px; +} diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index c6d98b60..29ab926a 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -13,7 +13,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { FilterViewPaneContainer } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -54,7 +54,8 @@ import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/e import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator'; import { inQuickPickContextKeyValue } from 'vs/workbench/browser/quickaccess'; -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export interface HelpInformation { extensionDescription: IExtensionDescription; @@ -135,12 +136,12 @@ class HelpTreeRenderer implements ITreeRenderer { - hasChildren(element: any) { +class HelpDataSource implements IAsyncDataSource { + hasChildren(element: HelpModel) { return element instanceof HelpModel; } - getChildren(element: any) { + getChildren(element: HelpModel) { if (element instanceof HelpModel && element.items) { return element.items; } @@ -149,14 +150,15 @@ class HelpDataSource implements IAsyncDataSource { } } -const getStartedIcon = registerIcon('remote-explorer-get-started', Codicon.star); -const documentationIcon = registerIcon('remote-explorer-documentation', Codicon.book); -const feedbackIcon = registerIcon('remote-explorer-feedback', Codicon.twitter); -const reviewIssuesIcon = registerIcon('remote-explorer-review-issues', Codicon.issues); -const reportIssuesIcon = registerIcon('remote-explorer-report-issues', Codicon.comment); +const getStartedIcon = registerIcon('remote-explorer-get-started', Codicon.star, nls.localize('getStartedIcon', 'Getting started icon in the remote explorer view.')); +const documentationIcon = registerIcon('remote-explorer-documentation', Codicon.book, nls.localize('documentationIcon', 'Documentation icon in the remote explorer view.')); +const feedbackIcon = registerIcon('remote-explorer-feedback', Codicon.twitter, nls.localize('feedbackIcon', 'Feedback icon in the remote explorer view.')); +const reviewIssuesIcon = registerIcon('remote-explorer-review-issues', Codicon.issues, nls.localize('reviewIssuesIcon', 'Review issue icon in the remote explorer view.')); +const reportIssuesIcon = registerIcon('remote-explorer-report-issues', Codicon.comment, nls.localize('reportIssuesIcon', 'Report issue icon in the remote explorer view.')); +const remoteExplorerViewIcon = registerIcon('remote-explorer-view-icon', Codicon.remoteExplorer, nls.localize('remoteExplorerViewIcon', 'View icon of the remote explorer view.')); interface IHelpItem { - icon: Codicon, + icon: ThemeIcon, iconClasses: string[]; label: string; handleClick(): Promise; @@ -297,14 +299,14 @@ class HelpItemValue { abstract class HelpItemBase implements IHelpItem { public iconClasses: string[] = []; constructor( - public icon: Codicon, + public icon: ThemeIcon, public label: string, public values: HelpItemValue[], private quickInputService: IQuickInputService, private environmentService: IWorkbenchEnvironmentService, private remoteExplorerService: IRemoteExplorerService ) { - this.iconClasses.push(...icon.classNamesArray); + this.iconClasses.push(...ThemeIcon.asClassNameArray(icon)); this.iconClasses.push('remote-help-tree-node-item-icon'); } @@ -351,7 +353,7 @@ abstract class HelpItemBase implements IHelpItem { class HelpItem extends HelpItemBase { constructor( - icon: Codicon, + icon: ThemeIcon, label: string, values: HelpItemValue[], quickInputService: IQuickInputService, @@ -369,7 +371,7 @@ class HelpItem extends HelpItemBase { class IssueReporterItem extends HelpItemBase { constructor( - icon: Codicon, + icon: ThemeIcon, label: string, values: HelpItemValue[], quickInputService: IQuickInputService, @@ -388,7 +390,7 @@ class IssueReporterItem extends HelpItemBase { class HelpPanel extends ViewPane { static readonly ID = '~remote.helpPanel'; static readonly TITLE = nls.localize('remote.help', "Help and feedback"); - private tree!: WorkbenchAsyncDataTree; + private tree!: WorkbenchAsyncDataTree; constructor( protected viewModel: IViewModel, @@ -418,7 +420,7 @@ class HelpPanel extends ViewPane { treeContainer.classList.add('remote-help-content'); container.appendChild(treeContainer); - this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, + this.tree = >this.instantiationService.createInstance(WorkbenchAsyncDataTree, 'RemoteHelp', treeContainer, new HelpTreeVirtualDelegate(), @@ -439,7 +441,7 @@ class HelpPanel extends ViewPane { this.tree.setInput(model); this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(e => { - e.element.handleClick(); + e.element?.handleClick(); })); } @@ -572,7 +574,7 @@ Registry.as(Extensions.ViewContainersRegistry).register matches = /^details(@(\d+))?$/.exec(group); if (matches) { - return -500; + return -500 + Number(matches[2]); } matches = /^help(@(\d+))?$/.exec(group); @@ -583,7 +585,7 @@ Registry.as(Extensions.ViewContainersRegistry).register return; } }, - icon: 'codicon-remote-explorer', + icon: remoteExplorerViewIcon, order: 4 }, ViewContainerLocation.Sidebar); diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index 4eb69336..aaabe66b 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -5,9 +5,9 @@ import * as nls from 'vs/nls'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Extensions, IViewDescriptorService, IViewsRegistry, IViewsService } from 'vs/workbench/common/views'; -import { IRemoteExplorerService, MakeAddress, mapHasTunnelLocalhostOrAllInterfaces, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanelDescriptor, TunnelViewModel } from 'vs/workbench/contrib/remote/browser/tunnelView'; +import { Extensions, IViewContainersRegistry, IViewDescriptorService, IViewsRegistry, IViewsService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; +import { IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { PORT_AUTO_FORWARD_SETTING, forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -15,25 +15,40 @@ import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarA import { UrlFinder } from 'vs/workbench/contrib/remote/browser/urlFinder'; import Severity from 'vs/base/common/severity'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; +import { INotificationHandle, INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { Codicon } from 'vs/base/common/codicons'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; +import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; export const VIEWLET_ID = 'workbench.view.remote'; export class ForwardedPortsView extends Disposable implements IWorkbenchContribution { private contextKeyListener?: IDisposable; + private _activityBadge?: IDisposable; private entryAccessor: IStatusbarEntryAccessor | undefined; + private readonly tasExperimentService: ITASExperimentService | undefined; constructor( @IContextKeyService private readonly contextKeyService: IContextKeyService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IStatusbarService private readonly statusbarService: IStatusbarService + @IActivityService private readonly activityService: IActivityService, + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, ) { super(); + this.tasExperimentService = tasExperimentService; this._register(Registry.as(Extensions.ViewsRegistry).registerViewWelcomeContent(TUNNEL_VIEW_ID, { content: `Forwarded ports allow you to access your running services locally.\n[Forward a Port](command:${ForwardPortAction.INLINE_ID})`, })); @@ -41,17 +56,41 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu this.enableForwardedPortsView(); } - private enableForwardedPortsView() { + private async usePanelTreatment(): Promise { + if (this.tasExperimentService) { + return !!(await this.tasExperimentService.getTreatment('portspanel')); + } + return false; + } + + private async getViewContainer(): Promise { + if (await this.usePanelTreatment()) { + return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ + id: TunnelPanel.ID, + name: nls.localize('ports', "Ports"), + icon: Codicon.plug, + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [TunnelPanel.ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), + storageId: TunnelPanel.ID, + hideIfEmpty: true, + order: 5 + }, ViewContainerLocation.Panel); + } else { + return this.viewDescriptorService.getViewContainerById(VIEWLET_ID); + } + } + + private async enableForwardedPortsView() { if (this.contextKeyListener) { this.contextKeyListener.dispose(); this.contextKeyListener = undefined; } const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService); + if (this.environmentService.remoteAuthority && viewEnabled) { - const tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService), this.environmentService); + const viewContainer = await this.getViewContainer(); + const tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService, this.configurationService), this.environmentService); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - const viewContainer = this.viewDescriptorService.getViewContainerById(VIEWLET_ID); if (viewContainer) { viewsRegistry.registerViews([tunnelPanelDescriptor!], viewContainer); } @@ -68,18 +107,38 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu const disposable = Registry.as(Extensions.ViewsRegistry).onViewsRegistered(e => { if (e.find(view => view.views.find(viewDescriptor => viewDescriptor.id === TUNNEL_VIEW_ID))) { this._register(this.remoteExplorerService.tunnelModel.onForwardPort(() => { + this.updateActivityBadge(); this.updateStatusBar(); })); this._register(this.remoteExplorerService.tunnelModel.onClosePort(() => { + this.updateActivityBadge(); this.updateStatusBar(); })); + this.updateActivityBadge(); this.updateStatusBar(); disposable.dispose(); } }); } + private async updateActivityBadge() { + if (this._activityBadge) { + this._activityBadge.dispose(); + } + if (!(await this.usePanelTreatment())) { + return; + } + if (this.remoteExplorerService.tunnelModel.forwarded.size > 0) { + const viewContainer = this.viewDescriptorService.getViewContainerByViewId(TUNNEL_VIEW_ID); + if (viewContainer) { + this._activityBadge = this.activityService.showViewContainerActivity(viewContainer.id, { + badge: new NumberBadge(this.remoteExplorerService.tunnelModel.forwarded.size, n => n === 1 ? nls.localize('1forwardedPort', "1 forwarded port") : nls.localize('nForwardedPorts', "{0} forwarded ports", n)) + }); + } + } + } + private updateStatusBar() { if (!this.entryAccessor) { this._register(this.entryAccessor = this.statusbarService.addEntry(this.entry, 'status.forwardedPorts', nls.localize('status.forwardedPorts', "Forwarded Ports"), StatusbarAlignment.LEFT, 40)); @@ -92,18 +151,13 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu let text: string; let tooltip: string; const count = this.remoteExplorerService.tunnelModel.forwarded.size + this.remoteExplorerService.tunnelModel.detected.size; + text = `${count}`; if (count === 0) { - text = nls.localize('remote.forwardedPorts.statusbarTextNone', "No Ports Available"); - tooltip = text; + tooltip = nls.localize('remote.forwardedPorts.statusbarTextNone', "No Ports Forwarded"); } else { - if (count === 1) { - text = nls.localize('remote.forwardedPorts.statusbarTextSingle', "1 Port Available"); - } else { - text = nls.localize('remote.forwardedPorts.statusbarTextMultiple', "{0} Ports Available", count); - } const allTunnels = Array.from(this.remoteExplorerService.tunnelModel.forwarded.values()); allTunnels.push(...Array.from(this.remoteExplorerService.tunnelModel.detected.values())); - tooltip = nls.localize('remote.forwardedPorts.statusbarTooltip', "Available Ports: {0}", + tooltip = nls.localize('remote.forwardedPorts.statusbarTooltip', "Forwarded Ports: {0}", allTunnels.map(forwarded => forwarded.remotePort).join(', ')); } return { @@ -117,40 +171,134 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu export class AutomaticPortForwarding extends Disposable implements IWorkbenchContribution { - private contextServiceListener?: IDisposable; - private urlFinder?: UrlFinder; - private static AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; constructor( - @ITerminalService private readonly terminalService: ITerminalService, - @INotificationService private readonly notificationService: INotificationService, - @IOpenerService private readonly openerService: IOpenerService, - @IViewsService private readonly viewsService: IViewsService, - @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IDebugService private readonly debugService: IDebugService + @ITerminalService readonly terminalService: ITerminalService, + @INotificationService readonly notificationService: INotificationService, + @IOpenerService readonly openerService: IOpenerService, + @IViewsService readonly viewsService: IViewsService, + @IRemoteExplorerService readonly remoteExplorerService: IRemoteExplorerService, + @IWorkbenchEnvironmentService readonly environmentService: IWorkbenchEnvironmentService, + @IContextKeyService readonly contextKeyService: IContextKeyService, + @IConfigurationService readonly configurationService: IConfigurationService, + @IDebugService readonly debugService: IDebugService, + @IRemoteAgentService readonly remoteAgentService: IRemoteAgentService ) { super(); + if (!this.environmentService.remoteAuthority) { + return; + } + + remoteAgentService.getEnvironment().then(environment => { + if (environment?.os === OperatingSystem.Windows) { + this._register(new WindowsAutomaticPortForwarding(terminalService, notificationService, openerService, + remoteExplorerService, contextKeyService, configurationService, debugService)); + } else if (environment?.os === OperatingSystem.Linux) { + this._register(new LinuxAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, openerService, contextKeyService)); + } + }); + } +} + +class ForwardedPortNotifier extends Disposable { + private lastNotifyTime: Date; + private static COOL_DOWN = 5000; // milliseconds + private lastNotification: INotificationHandle | undefined; + + constructor(private readonly notificationService: INotificationService, + private readonly remoteExplorerService: IRemoteExplorerService, + private readonly openerService: IOpenerService) { + super(); + this.lastNotifyTime = new Date(); + this.lastNotifyTime.setFullYear(this.lastNotifyTime.getFullYear() - 1); + } + + public async notify(tunnels: RemoteTunnel[]) { + const tunnel = await this.portNumberHeuristicDelay(tunnels); + if (tunnel) { + if (Date.now() - this.lastNotifyTime.getTime() > ForwardedPortNotifier.COOL_DOWN) { + this.showNotification(tunnel); + } + } + } + + private newerTunnel: RemoteTunnel | undefined; + private async portNumberHeuristicDelay(tunnels: RemoteTunnel[]): Promise { + if (tunnels.length === 0) { + return; + } + tunnels = tunnels.sort((a, b) => a.tunnelRemotePort - b.tunnelRemotePort); + const firstTunnel = tunnels.shift()!; + // Heuristic. + if (firstTunnel.tunnelRemotePort % 1000 === 0) { + this.newerTunnel = firstTunnel; + return firstTunnel; + // 9229 is the node inspect port + } else if (firstTunnel.tunnelRemotePort < 10000 && firstTunnel.tunnelRemotePort !== 9229) { + this.newerTunnel = firstTunnel; + return firstTunnel; + } + + this.newerTunnel = undefined; + return new Promise(resolve => { + setTimeout(() => { + if (this.newerTunnel) { + resolve(undefined); + } else { + resolve(firstTunnel); + } + }, 3000); + }); + } + + private showNotification(tunnel: RemoteTunnel) { + const address = makeAddress(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort); + const message = nls.localize('remote.tunnelsView.automaticForward', "Your service running on port {0} is available. [See all forwarded ports](command:{1}.focus)", + tunnel.tunnelRemotePort, TunnelPanel.ID); + const browserChoice: IPromptChoice = { + label: OpenPortInBrowserAction.LABEL, + run: () => OpenPortInBrowserAction.run(this.remoteExplorerService.tunnelModel, this.openerService, address) + }; + this.lastNotification = this.notificationService.prompt(Severity.Info, message, [browserChoice], { neverShowAgain: { id: 'remote.tunnelsView.autoForwardNeverShow', isSecondary: true } }); + this.lastNotifyTime = new Date(); + this.lastNotification.onDidClose(() => { + this.lastNotification = undefined; + }); + } +} + +class WindowsAutomaticPortForwarding extends Disposable { + private contextServiceListener?: IDisposable; + private urlFinder?: UrlFinder; + private notifier: ForwardedPortNotifier; + + constructor( + private readonly terminalService: ITerminalService, + readonly notificationService: INotificationService, + readonly openerService: IOpenerService, + private readonly remoteExplorerService: IRemoteExplorerService, + private readonly contextKeyService: IContextKeyService, + private readonly configurationService: IConfigurationService, + private readonly debugService: IDebugService + ) { + super(); + this.notifier = new ForwardedPortNotifier(notificationService, remoteExplorerService, openerService); this._register(configurationService.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration(AutomaticPortForwarding.AUTO_FORWARD_SETTING)) { + if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) { this.tryStartStopUrlFinder(); } })); - if (this.environmentService.remoteAuthority) { - this.contextServiceListener = this._register(this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { - this.tryStartStopUrlFinder(); - } - })); - this.tryStartStopUrlFinder(); - } + this.contextServiceListener = this._register(this.contextKeyService.onDidChangeContext(e => { + if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { + this.tryStartStopUrlFinder(); + } + })); + this.tryStartStopUrlFinder(); } private tryStartStopUrlFinder() { - if (this.configurationService.getValue(AutomaticPortForwarding.AUTO_FORWARD_SETTING)) { + if (this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) { this.startUrlFinder(); } else { this.stopUrlFinder(); @@ -166,31 +314,12 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon } this.urlFinder = this._register(new UrlFinder(this.terminalService, this.debugService)); this._register(this.urlFinder.onDidMatchLocalUrl(async (localUrl) => { - if (mapHasTunnelLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, localUrl.host, localUrl.port)) { + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, localUrl.host, localUrl.port)) { return; } const forwarded = await this.remoteExplorerService.forward(localUrl); if (forwarded) { - const address = MakeAddress(forwarded.tunnelRemoteHost, forwarded.tunnelRemotePort); - const message = nls.localize('remote.tunnelsView.automaticForward', "Your service running on port {0} is available.", - forwarded.tunnelRemotePort); - const browserChoice: IPromptChoice = { - label: OpenPortInBrowserAction.LABEL, - run: () => OpenPortInBrowserAction.run(this.remoteExplorerService.tunnelModel, this.openerService, address) - }; - const showChoice: IPromptChoice = { - label: nls.localize('remote.tunnelsView.showView', "Show Forwarded Ports"), - run: () => { - const remoteAuthority = this.environmentService.remoteAuthority; - const explorerType: string[] | undefined = remoteAuthority ? [remoteAuthority.split('+')[0]] : undefined; - if (explorerType) { - this.remoteExplorerService.targetType = explorerType; - } - this.viewsService.openViewContainer(VIEWLET_ID); - }, - isSecondary: true - }; - this.notificationService.prompt(Severity.Info, message, [browserChoice, showChoice], { neverShowAgain: { id: 'remote.tunnelsView.autoForwardNeverShow', isSecondary: true } }); + this.notifier.notify([forwarded]); } })); } @@ -202,3 +331,114 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon } } } + +class LinuxAutomaticPortForwarding extends Disposable { + private candidateListener: IDisposable | undefined; + private autoForwarded: Set = new Set(); + private notifier: ForwardedPortNotifier; + private initialCandidates: Set = new Set(); + private contextServiceListener: IDisposable | undefined; + + constructor( + private readonly configurationService: IConfigurationService, + readonly remoteExplorerService: IRemoteExplorerService, + readonly notificationService: INotificationService, + readonly openerService: IOpenerService, + readonly contextKeyService: IContextKeyService + ) { + super(); + this.notifier = new ForwardedPortNotifier(notificationService, remoteExplorerService, openerService); + this._register(configurationService.onDidChangeConfiguration(async (e) => { + if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) { + await this.startStopCandidateListener(); + } + })); + + this.contextServiceListener = this._register(this.contextKeyService.onDidChangeContext(async (e) => { + if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { + await this.startStopCandidateListener(); + } + })); + + this.startStopCandidateListener(); + } + + private async startStopCandidateListener() { + if (this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) { + await this.startCandidateListener(); + } else { + this.stopCandidateListener(); + } + } + + private stopCandidateListener() { + if (this.candidateListener) { + this.candidateListener.dispose(); + this.candidateListener = undefined; + } + } + + private async startCandidateListener() { + if (this.candidateListener || !forwardedPortsViewEnabled.getValue(this.contextKeyService)) { + return; + } + if (this.contextServiceListener) { + this.contextServiceListener.dispose(); + } + + if (!this.remoteExplorerService.tunnelModel.environmentTunnelsSet) { + await new Promise(resolve => this.remoteExplorerService.tunnelModel.onEnvironmentTunnelsSet(() => resolve())); + } + + // Capture list of starting candidates so we don't auto forward them later. + this.setInitialCandidates(); + + this.candidateListener = this._register(this.remoteExplorerService.tunnelModel.onCandidatesChanged(this.handleCandidateUpdate, this)); + } + + private setInitialCandidates() { + this.remoteExplorerService.tunnelModel.candidates.forEach(async (value) => { + this.initialCandidates.add(makeAddress(value.host, value.port)); + }); + } + + private async forwardCandidates(): Promise { + const allTunnels = (await Promise.all(this.remoteExplorerService.tunnelModel.candidates.map(async (value) => { + const address = makeAddress(value.host, value.port); + if (this.initialCandidates.has(address)) { + return undefined; + } + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.detected, value.host, value.port)) { + return undefined; + } + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, value.host, value.port)) { + return undefined; + } + const forwarded = await this.remoteExplorerService.forward(value); + if (forwarded) { + this.autoForwarded.add(address); + } + return forwarded; + }))).filter(tunnel => !!tunnel); + if (allTunnels.length === 0) { + return undefined; + } + return allTunnels; + } + + private async handleCandidateUpdate(removed: Map) { + removed.forEach((value, key) => { + if (this.autoForwarded.has(key)) { + this.remoteExplorerService.close(value); + this.autoForwarded.delete(key); + } else if (this.initialCandidates.has(key)) { + this.initialCandidates.delete(key); + } + }); + + const tunnels = await this.forwardCandidates(); + if (tunnels) { + this.notifier.notify(tunnels); + } + } +} diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 83e5499b..f3ee1a1f 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -23,21 +23,24 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot import { IHostService } from 'vs/workbench/services/host/browser/host'; import { isWeb } from 'vs/base/common/platform'; import { once } from 'vs/base/common/functional'; +import { truncate } from 'vs/base/common/strings'; export class RemoteStatusIndicator extends Disposable implements IWorkbenchContribution { - private static REMOTE_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; - private static CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; - private static SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command + private static readonly REMOTE_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; + private static readonly CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; + private static readonly SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command + + private static readonly REMOTE_STATUS_LABEL_MAX_LENGTH = 40; private remoteStatusEntry: IStatusbarEntryAccessor | undefined; - private remoteMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); + private readonly remoteMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); private hasRemoteActions = false; - private remoteAuthority = this.environmentService.remoteAuthority; + private readonly remoteAuthority = this.environmentService.remoteAuthority; private connectionState: 'initializing' | 'connected' | 'disconnected' | undefined = undefined; - private connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService); + private readonly connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService); constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, @@ -189,7 +192,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr // Remote indicator: show if provided via options const remoteIndicator = this.environmentService.options?.windowIndicator; if (remoteIndicator) { - this.renderRemoteStatusIndicator(remoteIndicator.label, remoteIndicator.tooltip, remoteIndicator.command); + this.renderRemoteStatusIndicator(truncate(remoteIndicator.label, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH), remoteIndicator.tooltip, remoteIndicator.command); } // Remote Authority: show connection state @@ -200,10 +203,10 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr this.renderRemoteStatusIndicator(nls.localize('host.open', "Opening Remote..."), nls.localize('host.open', "Opening Remote..."), undefined, true /* progress */); break; case 'disconnected': - this.renderRemoteStatusIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from {0}", hostLabel)}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel)); + this.renderRemoteStatusIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from {0}", truncate(hostLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH))}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel)); break; default: - this.renderRemoteStatusIndicator(`$(remote) ${hostLabel}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel)); + this.renderRemoteStatusIndicator(`$(remote) ${truncate(hostLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH)}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel)); } } diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 235dce54..7ed9fa79 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -26,7 +26,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IMenuService, MenuId, IMenu, MenuRegistry, MenuItemAction, ILocalizedString, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions, createAndFillInActionBarActions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel, mapHasTunnelLocalhostOrAllInterfaces, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService, TunnelModel, makeAddress, TunnelType, ITunnelItem, Tunnel, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID, parseAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; @@ -42,8 +42,10 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Codicon } from 'vs/base/common/codicons'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); +export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; class TunnelTreeVirtualDelegate implements IListVirtualDelegate { getHeight(element: ITunnelItem): number { @@ -61,7 +63,7 @@ export interface ITunnelViewModel { readonly detected: TunnelItem[]; readonly candidates: TunnelItem[]; readonly input: TunnelItem; - groups(): Promise; + groupsAndForwarded(): Promise; } export class TunnelViewModel extends Disposable implements ITunnelViewModel { @@ -72,7 +74,8 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { private _candidates: Map = new Map(); constructor( - @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService) { + @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, + @IConfigurationService private readonly configurationService: IConfigurationService) { super(); this.model = remoteExplorerService.tunnelModel; this._register(this.model.onForwardPort(() => this._onForwardedPortsChanged.fire())); @@ -84,22 +87,19 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { tunnelType: TunnelType.Add, remoteHost: 'localhost', remotePort: 0, - description: '' + description: '', + iconClasses: undefined }; } - async groups(): Promise { - const groups: ITunnelGroup[] = []; + async groupsAndForwarded(): Promise { + const groups: (ITunnelGroup | TunnelItem)[] = []; this._candidates = new Map(); (await this.model.candidates).forEach(candidate => { - this._candidates.set(MakeAddress(candidate.host, candidate.port), candidate); + this._candidates.set(makeAddress(candidate.host, candidate.port), candidate); }); if ((this.model.forwarded.size > 0) || this.remoteExplorerService.getEditableData(undefined)) { - groups.push({ - label: nls.localize('remote.tunnelsView.forwarded', "Forwarded"), - tunnelType: TunnelType.Forwarded, - items: this.forwarded - }); + groups.push(...this.forwarded); } if (this.model.detected.size > 0) { groups.push({ @@ -108,13 +108,15 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { items: this.detected }); } - const candidates = await this.candidates; - if (candidates.length > 0) { - groups.push({ - label: nls.localize('remote.tunnelsView.candidates', "Not Forwarded"), - tunnelType: TunnelType.Candidate, - items: candidates - }); + if (!this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) { + const candidates = await this.candidates; + if (candidates.length > 0) { + groups.push({ + label: nls.localize('remote.tunnelsView.candidates', "Not Forwarded"), + tunnelType: TunnelType.Candidate, + items: candidates + }); + } } if (groups.length === 0) { groups.push(this._input); @@ -123,7 +125,7 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { } private addProcessInfoFromCandidate(tunnelItem: ITunnelItem) { - const key = MakeAddress(tunnelItem.remoteHost, tunnelItem.remotePort); + const key = makeAddress(tunnelItem.remoteHost, tunnelItem.remotePort); if (this._candidates.has(key)) { tunnelItem.description = this._candidates.get(key)!.detail; } @@ -158,8 +160,8 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { get candidates(): TunnelItem[] { const candidates: TunnelItem[] = []; this._candidates.forEach(value => { - if (!mapHasTunnelLocalhostOrAllInterfaces(this.model.forwarded, value.host, value.port) && - !mapHasTunnelLocalhostOrAllInterfaces(this.model.detected, value.host, value.port)) { + if (!mapHasAddressLocalhostOrAllInterfaces(this.model.forwarded, value.host, value.port) && + !mapHasAddressLocalhostOrAllInterfaces(this.model.detected, value.host, value.port)) { candidates.push(new TunnelItem(TunnelType.Candidate, value.host, value.port, undefined, undefined, false, undefined, value.detail)); } }); @@ -179,6 +181,7 @@ interface ITunnelTemplateData { elementDisposable: IDisposable; container: HTMLElement; iconLabel: IconLabel; + icon: HTMLElement; actionBar: ActionBar; } @@ -211,6 +214,7 @@ class TunnelTreeRenderer extends Disposable implements ITreeRendererelement).items) { @@ -387,7 +405,7 @@ interface ITunnelGroup { class TunnelItem implements ITunnelItem { static createFromTunnel(tunnel: Tunnel, type: TunnelType = TunnelType.Forwarded, closeable?: boolean) { - return new TunnelItem(type, tunnel.remoteHost, tunnel.remotePort, tunnel.localAddress, tunnel.localPort, closeable === undefined ? tunnel.closeable : closeable, tunnel.name, tunnel.description); + return new TunnelItem(type, tunnel.remoteHost, tunnel.remotePort, tunnel.localAddress, tunnel.localPort, closeable === undefined ? tunnel.closeable : closeable, tunnel.name, tunnel.description ?? tunnel.runningProcess, tunnel.source); } constructor( @@ -399,6 +417,7 @@ class TunnelItem implements ITunnelItem { public closeable?: boolean, public name?: string, private _description?: string, + private source?: string ) { } get label(): string { if (this.name) { @@ -414,13 +433,23 @@ class TunnelItem implements ITunnelItem { if (address.length < 16) { return address; } - let url: URL | undefined; + let displayAddress: string = address; try { - url = new URL(address); + if (!address.startsWith('http')) { + address = `http://${address}`; + } + const url = new URL(address); + if (url && url.host) { + const lastDotIndex = url.host.lastIndexOf('.'); + const secondLastDotIndex = lastDotIndex !== -1 ? url.host.substring(0, lastDotIndex).lastIndexOf('.') : -1; + if (secondLastDotIndex !== -1) { + displayAddress = `${url.protocol}//...${url.host.substring(secondLastDotIndex + 1)}`; + } + } } catch (e) { // Address isn't a valid url and can't be compacted. } - return url && url.host.length > 0 ? url.host : address; + return displayAddress; } set description(description: string | undefined) { @@ -428,13 +457,30 @@ class TunnelItem implements ITunnelItem { } get description(): string | undefined { - if (this._description) { - return this._description; - } else if (this.name) { - return nls.localize('remote.tunnelsView.forwardedPortDescription0', "{0} \u2192 {1}", this.remotePort, this.localAddress); + const description: string[] = []; + + if (this.name && this.localAddress) { + description.push(nls.localize('remote.tunnelsView.forwardedPortDescription0', "{0} \u2192 {1}", this.remotePort, TunnelItem.compactLongAddress(this.localAddress))); } + + if (this._description) { + description.push(this._description); + } + + if (this.source) { + description.push(this.source); + } + + if (description.length > 0) { + return description.join(' \u2022 '); + } + return undefined; } + + get iconClasses(): string | undefined { + return this.tunnelType === TunnelType.Detected || this.tunnelType === TunnelType.Forwarded ? Codicon.plug.classNames : undefined; + } } export const TunnelTypeContextKey = new RawContextKey('tunnelType', TunnelType.Add); @@ -507,6 +553,7 @@ export class TunnelPanel extends ViewPane { const panelContainer = dom.append(container, dom.$('.tree-explorer-viewlet-tree-view')); const treeContainer = dom.append(panelContainer, dom.$('.customview-tree')); + treeContainer.classList.add('ports-view'); treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); const renderer = new TunnelTreeRenderer(TunnelPanel.ID, this.menuService, this.contextKeyService, this.instantiationService, this.contextViewService, this.themeService, this.remoteExplorerService); @@ -682,9 +729,13 @@ export class TunnelPanelDescriptor implements IViewDescriptor { readonly canToggleVisibility = true; readonly hideByDefault = false; readonly workspace = true; + // group is not actually used for views that are not extension contributed. Use order instead. readonly group = 'details@0'; + // -500 comes from the remote explorer viewOrderDelegate + readonly order = -500; readonly remoteAuthority?: string | string[]; readonly canMoveView = true; + readonly containerIcon = Codicon.plug; constructor(viewModel: ITunnelViewModel, environmentService: IWorkbenchEnvironmentService) { this.ctorDescriptor = new SyncDescriptor(TunnelPanel, [viewModel]); @@ -740,16 +791,8 @@ export namespace ForwardPortAction { export const TREEITEM_LABEL = nls.localize('remote.tunnel.forwardItem', "Forward Port"); const forwardPrompt = nls.localize('remote.tunnel.forwardPrompt', "Port number or address (eg. 3000 or 10.10.10.10:2000)."); - function parseInput(value: string): { host: string, port: number } | undefined { - const matches = value.match(/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\:|localhost:)?([0-9]+)$/); - if (!matches) { - return undefined; - } - return { host: matches[1]?.substring(0, matches[1].length - 1) || 'localhost', port: Number(matches[2]) }; - } - function validateInput(value: string): string | null { - const parsed = parseInput(value); + const parsed = parseAddress(value); if (!parsed) { return invalidPortString; } else if (parsed.port >= maxPortNumber) { @@ -774,7 +817,7 @@ export namespace ForwardPortAction { remoteExplorerService.setEditable(undefined, { onFinish: async (value, success) => { let parsed: { host: string, port: number } | undefined; - if (success && (parsed = parseInput(value))) { + if (success && (parsed = parseAddress(value))) { remoteExplorerService.forward({ host: parsed.host, port: parsed.port }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); } remoteExplorerService.setEditable(undefined, null); @@ -798,7 +841,7 @@ export namespace ForwardPortAction { validateInput: (value) => Promise.resolve(validateInput(value)) }); let parsed: { host: string, port: number } | undefined; - if (value && (parsed = parseInput(value))) { + if (value && (parsed = parseAddress(value))) { remoteExplorerService.forward({ host: parsed.host, port: parsed.port }).then(tunnel => error(notificationService, tunnel, parsed!.host, parsed!.port)); } }; @@ -864,10 +907,15 @@ export namespace OpenPortInBrowserAction { export function handler(): ICommandHandler { return async (accessor, arg) => { + let key: string | undefined; if (arg instanceof TunnelItem) { + key = makeAddress(arg.remoteHost, arg.remotePort); + } else if (arg.tunnelRemoteHost && arg.tunnelRemotePort) { + key = makeAddress(arg.tunnelRemoteHost, arg.tunnelRemotePort); + } + if (key) { const model = accessor.get(IRemoteExplorerService).tunnelModel; const openerService = accessor.get(IOpenerService); - const key = MakeAddress(arg.remoteHost, arg.remotePort); return run(model, openerService, key); } }; @@ -877,12 +925,56 @@ export namespace OpenPortInBrowserAction { const tunnel = model.forwarded.get(key) || model.detected.get(key); let address: string | undefined; if (tunnel && tunnel.localAddress && (address = model.address(tunnel.remoteHost, tunnel.remotePort))) { - return openerService.open(URI.parse('http://' + address)); + if (!address.startsWith('http')) { + address = `http://${address}`; + } + return openerService.open(URI.parse(address)); } return Promise.resolve(); } } +namespace OpenPortInBrowserCommandPaletteAction { + export const ID = 'remote.tunnel.openCommandPalette'; + export const LABEL = nls.localize('remote.tunnel.openCommandPalette', "Open Port in Browser"); + + interface QuickPickTunnel extends IQuickPickItem { + tunnel?: TunnelItem; + } + + export function handler(): ICommandHandler { + return async (accessor, arg) => { + const model = accessor.get(IRemoteExplorerService).tunnelModel; + const quickPickService = accessor.get(IQuickInputService); + const openerService = accessor.get(IOpenerService); + const commandService = accessor.get(ICommandService); + const options: QuickPickTunnel[] = [...model.forwarded, ...model.detected].map(value => { + const tunnelItem = TunnelItem.createFromTunnel(value[1]); + return { + label: tunnelItem.label, + description: tunnelItem.description, + tunnel: tunnelItem + }; + }); + if (options.length === 0) { + options.push({ + label: nls.localize('remote.tunnel.openCommandPaletteNone', "No ports currently forwarded. Open the Ports view to get started.") + }); + } else { + options.push({ + label: nls.localize('remote.tunnel.openCommandPaletteView', "Open the Ports view...") + }); + } + const picked = await quickPickService.pick(options, { placeHolder: nls.localize('remote.tunnel.openCommandPalettePick', "Choose the port to open") }); + if (picked && picked.tunnel) { + return OpenPortInBrowserAction.run(model, openerService, makeAddress(picked.tunnel.remoteHost, picked.tunnel.remotePort)); + } else if (picked) { + return commandService.executeCommand(`${TUNNEL_VIEW_ID}.focus`); + } + }; + } +} + namespace CopyAddressAction { export const INLINE_ID = 'remote.tunnel.copyAddressInline'; export const COMMANDPALETTE_ID = 'remote.tunnel.copyAddressCommandPalette'; @@ -923,18 +1015,6 @@ namespace CopyAddressAction { } } -namespace RefreshTunnelViewAction { - export const ID = 'remote.tunnel.refresh'; - export const LABEL = nls.localize('remote.tunnel.refreshView', "Refresh"); - - export function handler(): ICommandHandler { - return (accessor, arg) => { - const remoteExplorerService = accessor.get(IRemoteExplorerService); - return remoteExplorerService.refresh(); - }; - } -} - namespace ChangeLocalPortAction { export const ID = 'remote.tunnel.changeLocalPort'; export const LABEL = nls.localize('remote.tunnel.changeLocalPort', "Change Local Port"); @@ -962,7 +1042,7 @@ namespace ChangeLocalPortAction { const numberValue = Number(value); const newForward = await remoteExplorerService.forward({ host: context.remoteHost, port: context.remotePort }, numberValue, context.name); if (newForward && newForward.tunnelLocalPort !== numberValue) { - notificationService.warn(nls.localize('remote.tunnel.changeLocalPortNumber', "The local port {0} is not available. Port number {1} has been used instead", value, newForward.tunnelLocalPort)); + notificationService.warn(nls.localize('remote.tunnel.changeLocalPortNumber', "The local port {0} is not available. Port number {1} has been used instead", value, newForward.tunnelLocalPort ?? newForward.localAddress)); } } }, @@ -1001,6 +1081,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ CommandsRegistry.registerCommand(ClosePortAction.COMMANDPALETTE_ID, ClosePortAction.commandPaletteHandler()); CommandsRegistry.registerCommand(OpenPortInBrowserAction.ID, OpenPortInBrowserAction.handler()); +CommandsRegistry.registerCommand(OpenPortInBrowserCommandPaletteAction.ID, OpenPortInBrowserCommandPaletteAction.handler()); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CopyAddressAction.INLINE_ID, weight: KeybindingWeight.WorkbenchContrib + tunnelViewCommandsWeightBonus, @@ -1009,7 +1090,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: CopyAddressAction.inlineHandler() }); CommandsRegistry.registerCommand(CopyAddressAction.COMMANDPALETTE_ID, CopyAddressAction.commandPaletteHandler()); -CommandsRegistry.registerCommand(RefreshTunnelViewAction.ID, RefreshTunnelViewAction.handler()); CommandsRegistry.registerCommand(ChangeLocalPortAction.ID, ChangeLocalPortAction.handler()); MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ @@ -1033,22 +1113,20 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ }, when: forwardedPortsViewEnabled })); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, ({ + command: { + id: OpenPortInBrowserCommandPaletteAction.ID, + title: OpenPortInBrowserCommandPaletteAction.LABEL + }, + when: forwardedPortsViewEnabled +})); MenuRegistry.appendMenuItem(MenuId.TunnelTitle, ({ group: 'navigation', order: 0, command: { id: ForwardPortAction.INLINE_ID, title: ForwardPortAction.LABEL, - icon: { id: 'codicon/plus' } - } -})); -MenuRegistry.appendMenuItem(MenuId.TunnelTitle, ({ - group: 'navigation', - order: 1, - command: { - id: RefreshTunnelViewAction.ID, - title: RefreshTunnelViewAction.LABEL, - icon: { id: 'codicon/refresh' } + icon: Codicon.plus } })); MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ @@ -1111,7 +1189,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ command: { id: OpenPortInBrowserAction.ID, title: OpenPortInBrowserAction.LABEL, - icon: { id: 'codicon/globe' } + icon: Codicon.globe }, when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) })); @@ -1120,7 +1198,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ command: { id: ForwardPortAction.INLINE_ID, title: ForwardPortAction.TREEITEM_LABEL, - icon: { id: 'codicon/plus' } + icon: Codicon.plus }, when: TunnelTypeContextKey.isEqualTo(TunnelType.Candidate) })); @@ -1129,7 +1207,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ command: { id: ClosePortAction.INLINE_ID, title: ClosePortAction.LABEL, - icon: { id: 'codicon/x' } + icon: Codicon.x }, when: TunnelCloseableContextKey })); diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts index bc74bb75..e9c21167 100644 --- a/src/vs/workbench/contrib/remote/browser/urlFinder.ts +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -18,6 +18,12 @@ export class UrlFinder extends Disposable { * http://0.0.0.0:4000 - Elixir Phoenix */ private static readonly localUrlRegex = /\b\w{2,20}:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0|:\d{2,5})[\w\-\.\~:\/\?\#[\]\@!\$&\(\)\*\+\,\;\=]*/gim; + /** + * https://github.com/microsoft/vscode-remote-release/issues/3949 + */ + private static readonly localPythonServerRegex = /HTTP\son\s(127\.0\.0\.1|0\.0\.0\.0)\sport\s(\d+)/; + + private static readonly excludeTerminals = ['Dev Containers']; private _onDidMatchLocalUrl: Emitter<{ host: string, port: number }> = new Emitter(); public readonly onDidMatchLocalUrl = this._onDidMatchLocalUrl.event; @@ -27,14 +33,10 @@ export class UrlFinder extends Disposable { super(); // Terminal terminalService.terminalInstances.forEach(instance => { - this.listeners.set(instance, instance.onData(data => { - this.processData(data); - })); + this.registerTerminalInstance(instance); }); this._register(terminalService.onInstanceCreated(instance => { - this.listeners.set(instance, instance.onData(data => { - this.processData(data); - })); + this.registerTerminalInstance(instance); })); this._register(terminalService.onInstanceDisposed(instance => { this.listeners.get(instance)?.dispose(); @@ -57,6 +59,14 @@ export class UrlFinder extends Disposable { })); } + private registerTerminalInstance(instance: ITerminalInstance) { + if (!UrlFinder.excludeTerminals.includes(instance.title)) { + this.listeners.set(instance, instance.onData(data => { + this.processData(data); + })); + } + } + private replPositions: Map = new Map(); private processNewReplElements(session: IDebugSession) { const oldReplPosition = this.replPositions.get(session.getId()); @@ -90,25 +100,33 @@ export class UrlFinder extends Disposable { // strip ANSI terminal codes data = data.replace(UrlFinder.terminalCodesRegex, ''); const urlMatches = data.match(UrlFinder.localUrlRegex) || []; - urlMatches.forEach((match) => { - // check if valid url - const serverUrl = new URL(match); - if (serverUrl) { - // check if the port is a valid integer value - const port = parseFloat(serverUrl.port!); - if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) { - // normalize the host name - let host = serverUrl.hostname; - if (host !== '0.0.0.0' && host !== '127.0.0.1') { - host = 'localhost'; + if (urlMatches && urlMatches.length > 0) { + urlMatches.forEach((match) => { + // check if valid url + const serverUrl = new URL(match); + if (serverUrl) { + // check if the port is a valid integer value + const port = parseFloat(serverUrl.port!); + if (!isNaN(port) && Number.isInteger(port) && port > 0 && port <= 65535) { + // normalize the host name + let host = serverUrl.hostname; + if (host !== '0.0.0.0' && host !== '127.0.0.1') { + host = 'localhost'; + } + // Exclude node inspect, except when using default port + if (port !== 9229 && data.startsWith('Debugger listening on')) { + return; + } + this._onDidMatchLocalUrl.fire({ port, host }); } - // Exclude node inspect, except when using default port - if (port !== 9229 && data.startsWith('Debugger listening on')) { - return; - } - this._onDidMatchLocalUrl.fire({ port, host }); } + }); + } else { + // Try special python case + const pythonMatch = data.match(UrlFinder.localPythonServerRegex); + if (pythonMatch && pythonMatch.length === 3) { + this._onDidMatchLocalUrl.fire({ host: pythonMatch[1], port: Number(pythonMatch[2]) }); } - }); + } } } diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 1728f76e..93911f19 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -131,7 +131,7 @@ Registry.as(ConfigurationExtensions.Configuration) }, 'remote.autoForwardPorts': { type: 'boolean', - markdownDescription: localize('remote.autoForwardPorts', "When enabled, URLs with ports (ex. `http://127.0.0.1:3000`) that are printed to your terminals are automatically forwarded."), + markdownDescription: localize('remote.autoForwardPorts', "When enabled, new running processes are detected and ports that they listen on are automatically forwarded."), default: true } } diff --git a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts index ab8c80e6..6da6a274 100644 --- a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts +++ b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts @@ -3,31 +3,39 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITunnelService, TunnelOptions, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService, TunnelOptions, RemoteTunnel, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; +import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; export class TunnelFactoryContribution extends Disposable implements IWorkbenchContribution { constructor( @ITunnelService tunnelService: ITunnelService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IOpenerService openerService: IOpenerService, + @IRemoteExplorerService remoteExplorerService: IRemoteExplorerService ) { super(); const tunnelFactory = environmentService.options?.tunnelProvider?.tunnelFactory; if (tunnelFactory) { this._register(tunnelService.setTunnelProvider({ - forwardPort: (tunnelOptions: TunnelOptions): Promise | undefined => { - const tunnelPromise = tunnelFactory(tunnelOptions); + forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise | undefined => { + const tunnelPromise = tunnelFactory(tunnelOptions, tunnelCreationOptions); if (!tunnelPromise) { return undefined; } return new Promise(resolve => { - tunnelPromise.then(tunnel => { + tunnelPromise.then(async (tunnel) => { + const localAddress = tunnel.localAddress.startsWith('http') ? tunnel.localAddress : `http://${tunnel.localAddress}`; const remoteTunnel: RemoteTunnel = { tunnelRemotePort: tunnel.remoteAddress.port, tunnelRemoteHost: tunnel.remoteAddress.host, - localAddress: tunnel.localAddress, + // The tunnel factory may give us an inaccessible local address. + // To make sure this doesn't happen, resolve the uri immediately. + localAddress: (await openerService.resolveExternalUri(URI.parse(localAddress))).resolved.toString(), dispose: tunnel.dispose }; resolve(remoteTunnel); @@ -35,6 +43,7 @@ export class TunnelFactoryContribution extends Disposable implements IWorkbenchC }); } })); + remoteExplorerService.setTunnelInformation(undefined); } } } diff --git a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts index c4b13519..c63a2d4b 100644 --- a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts +++ b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts @@ -11,6 +11,8 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { SashSizeController, minSize, maxSize } from 'vs/workbench/contrib/sash/browser/sash'; import { isIPad } from 'vs/base/browser/browser'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { sashHoverBorder } from 'vs/platform/theme/common/colorRegistry'; // Sash size contribution Registry.as(WorkbenchExtensions.Workbench) @@ -30,3 +32,13 @@ Registry.as(ConfigurationExtensions.Configuration) }, } }); + +registerThemingParticipant((theme, collector) => { + const sashHoverBorderColor = theme.getColor(sashHoverBorder); + collector.addRule(` + .monaco-sash:hover, + .monaco-sash.active { + background: ${sashHoverBorderColor} + } + `); +}); diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index b826a548..ffd41e57 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -19,7 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { URI } from 'vs/base/common/uri'; import { ISCMService, ISCMRepository, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { registerThemingParticipant, IColorTheme, ICssStyleCollector, themeColorFromId, IThemeService } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, IColorTheme, ICssStyleCollector, themeColorFromId, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; import { Color, RGBA } from 'vs/base/common/color'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -48,6 +48,7 @@ import { ISplice } from 'vs/base/common/sequence'; import { createStyleSheet } from 'vs/base/browser/dom'; import { ITextFileEditorModel, IResolvedTextFileEditorModel, ITextFileService, isTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode } from 'vs/workbench/common/editor'; +import { gotoNextLocation, gotoPreviousLocation } from 'vs/platform/theme/common/iconRegistry'; class DiffActionRunner extends ActionRunner { @@ -246,8 +247,8 @@ class DirtyDiffWidget extends PeekViewWidget { protected _fillHead(container: HTMLElement): void { super._fillHead(container); - const previous = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowPreviousChangeAction(), 'codicon-arrow-up'); - const next = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowNextChangeAction(), 'codicon-arrow-down'); + const previous = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowPreviousChangeAction(), ThemeIcon.asClassName(gotoPreviousLocation)); + const next = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowNextChangeAction(), ThemeIcon.asClassName(gotoNextLocation)); this._disposables.add(previous); this._disposables.add(next); @@ -563,20 +564,57 @@ export class DirtyDiffController extends Disposable implements IEditorContributi private session: IDisposable = Disposable.None; private mouseDownInfo: { lineNumber: number } | null = null; private enabled = false; + private gutterActionDisposables = new DisposableStore(); + private stylesheet: HTMLStyleElement; constructor( private editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); this.enabled = !contextKeyService.getContextKeyValue('isInDiffEditor'); + this.stylesheet = createStyleSheet(); + this._register(toDisposable(() => this.stylesheet.remove())); if (this.enabled) { this.isDirtyDiffVisible = isDirtyDiffVisible.bindTo(contextKeyService); - this._register(editor.onMouseDown(e => this.onEditorMouseDown(e))); - this._register(editor.onMouseUp(e => this.onEditorMouseUp(e))); this._register(editor.onDidChangeModel(() => this.close())); + + const onDidChangeGutterAction = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorationsGutterAction')); + this._register(onDidChangeGutterAction(this.onDidChangeGutterAction, this)); + this.onDidChangeGutterAction(); + } + } + + private onDidChangeGutterAction(): void { + const gutterAction = this.configurationService.getValue<'diff' | 'none'>('scm.diffDecorationsGutterAction'); + + this.gutterActionDisposables.dispose(); + this.gutterActionDisposables = new DisposableStore(); + + if (gutterAction === 'diff') { + this.gutterActionDisposables.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); + this.gutterActionDisposables.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); + this.stylesheet.textContent = ` + .monaco-editor .dirty-diff-glyph { + cursor: pointer; + } + + .monaco-editor .margin-view-overlays .dirty-diff-glyph:hover::before { + width: 9px; + left: -6px; + } + + .monaco-editor .margin-view-overlays .dirty-diff-deleted:hover::after { + bottom: 0; + border-top-width: 0; + border-bottom-width: 0; + } + `; + } else { + this.stylesheet.textContent = ``; } } @@ -798,6 +836,11 @@ export class DirtyDiffController extends Disposable implements IEditorContributi return model.changes; } + + dispose(): void { + this.gutterActionDisposables.dispose(); + super.dispose(); + } } export const editorGutterModifiedBackground = registerColor('editorGutter.modifiedBackground', { diff --git a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css index 472114ee..dd0921f4 100644 --- a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css +++ b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css @@ -5,7 +5,6 @@ .monaco-editor .dirty-diff-glyph { margin-left: 5px; - cursor: pointer; z-index: 5; } @@ -39,20 +38,6 @@ transition: height 80ms linear; } -.monaco-editor .margin-view-overlays .dirty-diff-glyph:hover::before { - position: absolute; - content: ''; - height: 100%; - width: 9px; - left: -6px; -} - -.monaco-editor .margin-view-overlays .dirty-diff-deleted:hover::after { - bottom: 0; - border-top-width: 0; - border-bottom-width: 0; -} - /* Hide glyph decorations when inside the inline diff editor */ .monaco-editor.modified-in-monaco-diff-editor .margin-view-overlays > div > .dirty-diff-glyph { display: none; diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index db8fb95f..98a0f62f 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -23,6 +23,7 @@ import { SCMViewPaneContainer } from 'vs/workbench/contrib/scm/browser/scmViewPa import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { SCMViewPane } from 'vs/workbench/contrib/scm/browser/scmViewPane'; import { SCMViewService } from 'vs/workbench/contrib/scm/browser/scmViewService'; import { SCMRepositoriesViewPane } from 'vs/workbench/contrib/scm/browser/scmRepositoriesViewPane'; @@ -38,12 +39,14 @@ ModesRegistry.registerLanguage({ Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored); +const sourceControlViewIcon = registerIcon('source-control-view-icon', Codicon.sourceControl, localize('sourceControlViewIcon', 'View icon of the source control view.')); + const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, name: localize('source control', "Source Control"), ctorDescriptor: new SyncDescriptor(SCMViewPaneContainer), storageId: 'workbench.scm.views.state', - icon: Codicon.sourceControl.classNames, + icon: sourceControlViewIcon, alwaysUseContainerInfo: true, order: 2, hideIfEmpty: true @@ -65,7 +68,7 @@ viewsRegistry.registerViews([{ canMoveView: true, weight: 80, order: -999, - containerIcon: Codicon.sourceControl.classNames + containerIcon: sourceControlViewIcon }], viewContainer); viewsRegistry.registerViews([{ @@ -80,7 +83,7 @@ viewsRegistry.registerViews([{ order: -1000, when: ContextKeyExpr.and(ContextKeyExpr.has('scm.providerCount'), ContextKeyExpr.notEquals('scm.providerCount', 0)), // readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); - containerIcon: Codicon.sourceControl.classNames + containerIcon: sourceControlViewIcon }], viewContainer); Registry.as(WorkbenchExtensions.Workbench) @@ -138,6 +141,16 @@ Registry.as(ConfigurationExtensions.Configuration).regis description: localize('scm.diffDecorationsGutterVisibility', "Controls the visibility of the Source Control diff decorator in the gutter."), default: 'always' }, + 'scm.diffDecorationsGutterAction': { + type: 'string', + enum: ['diff', 'none'], + enumDescriptions: [ + localize('scm.diffDecorationsGutterAction.diff', "Show the inline diff peek view on click."), + localize('scm.diffDecorationsGutterAction.none', "Do nothing.") + ], + description: localize('scm.diffDecorationsGutterAction', "Controls the behavior of Source Control diff gutter decorations."), + default: 'diff' + }, 'scm.alwaysShowActions': { type: 'boolean', description: localize('alwaysShowActions', "Controls whether inline actions are always visible in the Source Control view."), diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts index 75d93fa6..ffcb36be 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/scm'; -import { basename } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; import { append, $ } from 'vs/base/browser/dom'; import { ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; @@ -20,6 +19,8 @@ import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IListRenderer } from 'vs/base/browser/ui/list/list'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { basename } from 'vs/base/common/resources'; interface RepositoryTemplate { readonly label: HTMLElement; @@ -42,7 +43,8 @@ export class RepositoryRenderer implements ICompressibleTreeRenderer | ISCMResource; @@ -655,26 +656,30 @@ export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyb } } +function getSCMResourceId(element: TreeElement): string { + if (ResourceTree.isResourceNode(element)) { + const group = element.context; + return `folder:${group.provider.id}/${group.id}/$FOLDER/${element.uri.toString()}`; + } else if (isSCMRepository(element)) { + const provider = element.provider; + return `repo:${provider.id}`; + } else if (isSCMInput(element)) { + const provider = element.repository.provider; + return `input:${provider.id}`; + } else if (isSCMResource(element)) { + const group = element.resourceGroup; + const provider = group.provider; + return `resource:${provider.id}/${group.id}/${element.sourceUri.toString()}`; + } else { + const provider = element.provider; + return `group:${provider.id}/${element.id}`; + } +} + class SCMResourceIdentityProvider implements IIdentityProvider { getId(element: TreeElement): string { - if (ResourceTree.isResourceNode(element)) { - const group = element.context; - return `folder:${group.provider.id}/${group.id}/$FOLDER/${element.uri.toString()}`; - } else if (isSCMRepository(element)) { - const provider = element.provider; - return `repo:${provider.id}`; - } else if (isSCMInput(element)) { - const provider = element.repository.provider; - return `input:${provider.id}`; - } else if (isSCMResource(element)) { - const group = element.resourceGroup; - const provider = group.provider; - return `resource:${provider.id}/${group.id}/${element.sourceUri.toString()}`; - } else { - const provider = element.provider; - return `group:${provider.id}/${element.id}`; - } + return getSCMResourceId(element); } } @@ -728,15 +733,24 @@ interface IRepositoryItem { dispose(): void; } +interface ITreeViewState { + readonly collapsed: string[]; +} + function isRepositoryItem(item: IRepositoryItem | IGroupItem): item is IRepositoryItem { return Array.isArray((item as IRepositoryItem).groupItems); } -function asTreeElement(node: IResourceNode, forceIncompressible: boolean): ICompressedTreeElement { +function asTreeElement(node: IResourceNode, forceIncompressible: boolean, viewState?: ITreeViewState): ICompressedTreeElement { + const element = (node.childrenCount === 0 && node.element) ? node.element : node; + const collapsed = viewState ? viewState.collapsed.indexOf(getSCMResourceId(element)) > -1 : false; + return { - element: (node.childrenCount === 0 && node.element) ? node.element : node, - children: Iterable.map(node.children, node => asTreeElement(node, false)), - incompressible: !!node.element || forceIncompressible + element, + children: Iterable.map(node.children, node => asTreeElement(node, false, viewState)), + incompressible: !!node.element || forceIncompressible, + collapsed, + collapsible: node.childrenCount > 0 }; } @@ -788,12 +802,21 @@ class ViewModel { } } + private _treeViewStateIsStale = false; + get treeViewState(): ITreeViewState | undefined { + if (this.visible && this._treeViewStateIsStale) { + this.updateViewState(); + this._treeViewStateIsStale = false; + } + + return this._treeViewState; + } + private items = new Map(); private visibilityDisposables = new DisposableStore(); private scrollTop: number | undefined; private alwaysShowRepositories = false; private firstVisible = true; - private repositoryCollapseStates: Map | undefined; private viewSubMenuAction: SCMViewSubMenuAction | undefined; private disposables = new DisposableStore(); @@ -802,6 +825,7 @@ class ViewModel { private inputRenderer: InputRenderer, private _mode: ViewModelMode, private _sortKey: ViewModelSortKey, + private _treeViewState: ITreeViewState | undefined, @IInstantiationService protected instantiationService: IInstantiationService, @IEditorService protected editorService: IEditorService, @IConfigurationService protected configurationService: IConfigurationService, @@ -815,6 +839,8 @@ class ViewModel { configurationService.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); this.onDidChangeConfiguration(); + + this.disposables.add(this.tree.onDidChangeCollapseState(() => this._treeViewStateIsStale = true)); } private onDidChangeConfiguration(e?: IConfigurationChangeEvent): void { @@ -862,7 +888,7 @@ class ViewModel { } private createGroupItem(group: ISCMResourceGroup): IGroupItem { - const tree = new ResourceTree(group, group.provider.rootUri || URI.file('/')); + const tree = new ResourceTree(group, group.provider.rootUri || URI.file('/'), this.uriIdentityService.extUri); const resources: ISCMResource[] = [...group.elements]; const disposable = combinedDisposable( group.onDidChange(() => this.tree.refilter()), @@ -907,7 +933,6 @@ class ViewModel { this.visibilityDisposables = new DisposableStore(); this.scmViewService.onDidChangeVisibleRepositories(this._onDidChangeVisibleRepositories, this, this.visibilityDisposables); this._onDidChangeVisibleRepositories({ added: this.scmViewService.visibleRepositories, removed: Iterable.empty() }); - this.repositoryCollapseStates = undefined; if (typeof this.scrollTop === 'number') { this.tree.scrollTop = this.scrollTop; @@ -917,13 +942,7 @@ class ViewModel { this.editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.visibilityDisposables); this.onDidActiveEditorChange(); } else { - if (this.items.size > 1) { - this.repositoryCollapseStates = new Map(); - - for (const [, item] of this.items) { - this.repositoryCollapseStates.set(item.element, this.tree.isCollapsed(item.element)); - } - } + this.updateViewState(); this.visibilityDisposables.dispose(); this._onDidChangeVisibleRepositories({ added: Iterable.empty(), removed: [...this.items.keys()] }); @@ -939,12 +958,12 @@ class ViewModel { if (!this.alwaysShowRepositories && (this.items.size === 1 && (!item || isRepositoryItem(item)))) { const item = Iterable.first(this.items.values())!; - this.tree.setChildren(null, this.render(item).children); + this.tree.setChildren(null, this.render(item, this._treeViewState).children); } else if (item) { - this.tree.setChildren(item.element, this.render(item).children); + this.tree.setChildren(item.element, this.render(item, this._treeViewState).children); } else { const items = coalesce(this.scmViewService.visibleRepositories.map(r => this.items.get(r))); - this.tree.setChildren(null, items.map(item => this.render(item))); + this.tree.setChildren(null, items.map(item => this.render(item, this._treeViewState))); } if (focusedInput) { @@ -958,7 +977,7 @@ class ViewModel { this._onDidChangeRepositoryCollapseState.fire(); } - private render(item: IRepositoryItem | IGroupItem): ICompressedTreeElement { + private render(item: IRepositoryItem | IGroupItem, treeViewState?: ITreeViewState): ICompressedTreeElement { if (isRepositoryItem(item)) { const children: ICompressedTreeElement[] = []; const hasSomeChanges = item.groupItems.some(item => item.element.elements.length > 0); @@ -968,20 +987,40 @@ class ViewModel { children.push({ element: item.element.input, incompressible: true, collapsible: false }); } - children.push(...item.groupItems.map(i => this.render(i))); + children.push(...item.groupItems.map(i => this.render(i, treeViewState))); } - const collapsed = this.repositoryCollapseStates?.get(item.element); + const collapsed = treeViewState ? treeViewState.collapsed.indexOf(getSCMResourceId(item.element)) > -1 : false; + return { element: item.element, children, incompressible: true, collapsed, collapsible: true }; } else { const children = this.mode === ViewModelMode.List ? Iterable.map(item.resources, element => ({ element, incompressible: true })) - : Iterable.map(item.tree.root.children, node => asTreeElement(node, true)); + : Iterable.map(item.tree.root.children, node => asTreeElement(node, true, treeViewState)); - return { element: item.element, children, incompressible: true, collapsible: true }; + const collapsed = treeViewState ? treeViewState.collapsed.indexOf(getSCMResourceId(item.element)) > -1 : false; + + return { element: item.element, children, incompressible: true, collapsed, collapsible: true }; } } + private updateViewState(): void { + const collapsed: string[] = []; + const visit = (node: ITreeNode) => { + if (node.element && node.collapsible && node.collapsed) { + collapsed.push(getSCMResourceId(node.element)); + } + + for (const child of node.children) { + visit(child); + } + }; + + visit(this.tree.getNode()); + + this._treeViewState = { collapsed }; + } + private onDidActiveEditorChange(): void { if (!this.configurationService.getValue('scm.autoReveal')) { return; @@ -1000,13 +1039,17 @@ class ViewModel { } for (const repository of this.scmViewService.visibleRepositories) { - const item = this.items.get(repository)!; + const item = this.items.get(repository); + + if (!item) { + continue; + } // go backwards from last group for (let j = item.groupItems.length - 1; j >= 0; j--) { const groupItem = item.groupItems[j]; const resource = this.mode === ViewModelMode.Tree - ? groupItem.tree.getNode(uri)?.element // TODO@Joao URI identity? + ? groupItem.tree.getNode(uri)?.element : groupItem.resources.find(r => this.uriIdentityService.extUri.isEqual(r.sourceUri, uri)); if (resource) { @@ -1020,12 +1063,14 @@ class ViewModel { } focus() { - for (const repository of this.scmViewService.visibleRepositories) { - const widget = this.inputRenderer.getRenderedInputWidget(repository.input); + if (this.tree.getFocus().length === 0) { + for (const repository of this.scmViewService.visibleRepositories) { + const widget = this.inputRenderer.getRenderedInputWidget(repository.input); - if (widget) { - widget.focus(); - return; + if (widget) { + widget.focus(); + return; + } } } @@ -1184,7 +1229,7 @@ export class ToggleViewModeAction extends Action { } private onDidChangeMode(mode: ViewModelMode): void { - const iconClass = mode === ViewModelMode.List ? 'codicon-list-tree' : 'codicon-list-flat'; + const iconClass = ThemeIcon.asClassName(mode === ViewModelMode.List ? Codicon.listTree : Codicon.listFlat); this.class = `scm-action toggle-view-mode ${iconClass}`; this.checked = this.viewModel.mode === this.mode; } @@ -1444,7 +1489,8 @@ class SCMInputWidget extends Disposable { padding: { top: 3, bottom: 3 }, quickSuggestions: false, scrollbar: { alwaysConsumeMouseWheel: false }, - overflowWidgetsDomNode + overflowWidgetsDomNode, + renderWhitespace: 'none' }; const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { @@ -1612,7 +1658,7 @@ class SCMCollapseAction extends Action { this.enabled = isAnyProviderCollapsible; this.allCollapsed = isAnyProviderCollapsible && this.viewModel.areAllProvidersCollapsed(); this.label = this.allCollapsed ? localize('expand all', "Expand All Repositories") : localize('collapse all', "Collapse All Repositories"); - this.class = this.allCollapsed ? Codicon.expandAll.classNames : Codicon.collapseAll.classNames; + this.class = ThemeIcon.asClassName(this.allCollapsed ? Codicon.expandAll : Codicon.collapseAll); } } @@ -1689,7 +1735,7 @@ export class SCMViewPane extends ViewPane { const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); this._register(actionRunner); - this._register(actionRunner.onDidBeforeRun(() => this.tree.domFocus())); + this._register(actionRunner.onBeforeRun(() => this.tree.domFocus())); const renderers: ICompressibleTreeRenderer[] = [ this.instantiationService.createInstance(RepositoryRenderer, actionViewItemProvider), @@ -1731,13 +1777,22 @@ export class SCMViewPane extends ViewPane { append(this.listContainer, overflowWidgetsDomNode); let viewMode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; - const storageMode = this.storageService.get(`scm.viewMode`, StorageScope.WORKSPACE) as ViewModelMode; + const storageMode = this.storageService.get(`scm.viewMode`, StorageScope.WORKSPACE) as ViewModelMode; if (typeof storageMode === 'string') { viewMode = storageMode; } - this.viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.inputRenderer, viewMode, ViewModelSortKey.Path); + let viewState: ITreeViewState | undefined; + + const storageViewState = this.storageService.get(`scm.viewState`, StorageScope.WORKSPACE); + if (storageViewState) { + try { + viewState = JSON.parse(storageViewState); + } catch {/* noop */ } + } + + this.viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.inputRenderer, viewMode, ViewModelSortKey.Path, viewState); this._register(this.viewModel); this.listContainer.classList.add('file-icon-themable-tree'); @@ -1754,6 +1809,12 @@ export class SCMViewPane extends ViewPane { this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowRepositories'))(this.updateActions, this)); this.updateActions(); + + this._register(this.storageService.onWillSaveState(e => { + if (e.reason === WillSaveStateReason.SHUTDOWN) { + this.storageService.store(`scm.viewState`, JSON.stringify(this.viewModel.treeViewState), StorageScope.WORKSPACE, StorageTarget.MACHINE); + } + })); } private updateIndentStyles(theme: IFileIconTheme): void { @@ -1765,7 +1826,7 @@ export class SCMViewPane extends ViewPane { private onDidChangeMode(): void { this.updateIndentStyles(this.themeService.getFileIconTheme()); - this.storageService.store(`scm.viewMode`, this.viewModel.mode, StorageScope.WORKSPACE); + this.storageService.store(`scm.viewMode`, this.viewModel.mode, StorageScope.WORKSPACE, StorageTarget.USER); } layoutBody(height: number | undefined = this.layoutCache.height, width: number | undefined = this.layoutCache.width): void { @@ -1839,7 +1900,7 @@ export class SCMViewPane extends ViewPane { return this.viewModel.getViewActionsContext(); } - private async open(e: IOpenEvent): Promise { + private async open(e: IOpenEvent): Promise { if (!e.element) { return; } else if (isSCMRepository(e.element)) { @@ -1878,13 +1939,17 @@ export class SCMViewPane extends ViewPane { } // ISCMResource - await e.element.open(!!e.editorOptions.preserveFocus); + if (e.element.command?.id === API_OPEN_EDITOR_COMMAND_ID || e.element.command?.id === API_OPEN_DIFF_EDITOR_COMMAND_ID) { + await this.commandService.executeCommand(e.element.command.id, ...(e.element.command.arguments || []), e); + } else { + await e.element.open(!!e.editorOptions.preserveFocus); - if (e.editorOptions.pinned) { - const activeEditorPane = this.editorService.activeEditorPane; + if (e.editorOptions.pinned) { + const activeEditorPane = this.editorService.activeEditorPane; - if (activeEditorPane) { - activeEditorPane.group.pinEditor(activeEditorPane.input); + if (activeEditorPane) { + activeEditorPane.group.pinEditor(activeEditorPane.input); + } } } @@ -1937,7 +2002,7 @@ export class SCMViewPane extends ViewPane { } const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); - actionRunner.onDidBeforeRun(() => this.tree.domFocus()); + actionRunner.onBeforeRun(() => this.tree.domFocus()); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index 2704f6a7..78ac23a1 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -5,10 +5,21 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; import { Iterable } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { SCMMenus } from 'vs/workbench/contrib/scm/browser/menus'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { debounce } from 'vs/base/common/decorators'; + +function getProviderStorageKey(provider: ISCMProvider): string { + return `${provider.contextValue}:${provider.label}${provider.rootUri ? `:${provider.rootUri.toString()}` : ''}`; +} + +export interface ISCMViewServiceState { + readonly all: string[]; + readonly visible: number[]; +} export class SCMViewService implements ISCMViewService { @@ -16,6 +27,9 @@ export class SCMViewService implements ISCMViewService { readonly menus: ISCMMenus; + private didFinishLoading: boolean = false; + private provisionalVisibleRepository: ISCMRepository | undefined; + private previousState: ISCMViewServiceState | undefined; private disposables = new DisposableStore(); private _visibleRepositoriesSet = new Set(); @@ -84,7 +98,8 @@ export class SCMViewService implements ISCMViewService { constructor( @ISCMService private readonly scmService: ISCMService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IStorageService private readonly storageService: IStorageService ) { this.menus = instantiationService.createInstance(SCMMenus); @@ -94,13 +109,63 @@ export class SCMViewService implements ISCMViewService { for (const repository of scmService.repositories) { this.onDidAddRepository(repository); } + + try { + this.previousState = JSON.parse(storageService.get('scm:view:visibleRepositories', StorageScope.WORKSPACE, '')); + this.eventuallyFinishLoading(); + } catch { + // noop + } + + storageService.onWillSaveState(this.onWillSaveState, this, this.disposables); } private onDidAddRepository(repository: ISCMRepository): void { + if (!this.didFinishLoading) { + this.eventuallyFinishLoading(); + } + + let removed: Iterable = Iterable.empty(); + + if (this.previousState) { + const index = this.previousState.all.indexOf(getProviderStorageKey(repository.provider)); + + if (index === -1) { // saw a repo we did not expect + const added: ISCMRepository[] = []; + for (const repo of this.scmService.repositories) { // all should be visible + if (!this._visibleRepositoriesSet.has(repo)) { + added.push(repository); + } + } + + this._visibleRepositories = [...this.scmService.repositories]; + this._visibleRepositoriesSet = new Set(this.scmService.repositories); + this._onDidChangeRepositories.fire({ added, removed: Iterable.empty() }); + this.finishLoading(); + return; + } + + const visible = this.previousState.visible.indexOf(index) > -1; + + if (!visible) { + if (this._visibleRepositories.length === 0) { // should make it visible, until other repos come along + this.provisionalVisibleRepository = repository; + } else { + return; + } + } else { + if (this.provisionalVisibleRepository) { + this._visibleRepositories = []; + this._visibleRepositoriesSet = new Set(); + removed = [this.provisionalVisibleRepository]; + this.provisionalVisibleRepository = undefined; + } + } + } + this._visibleRepositories.push(repository); this._visibleRepositoriesSet.add(repository); - - this._onDidChangeRepositories.fire({ added: [repository], removed: Iterable.empty() }); + this._onDidChangeRepositories.fire({ added: [repository], removed }); if (!this._focusedRepository) { this.focus(repository); @@ -108,6 +173,10 @@ export class SCMViewService implements ISCMViewService { } private onDidRemoveRepository(repository: ISCMRepository): void { + if (!this.didFinishLoading) { + this.eventuallyFinishLoading(); + } + const index = this._visibleRepositories.indexOf(repository); if (index > -1) { @@ -166,6 +235,32 @@ export class SCMViewService implements ISCMViewService { this._onDidFocusRepository.fire(repository); } + private onWillSaveState(): void { + if (!this.didFinishLoading) { // don't remember state, if the workbench didn't really finish loading + return; + } + + const all = this.scmService.repositories.map(r => getProviderStorageKey(r.provider)); + const visible = this.visibleRepositories.map(r => all.indexOf(getProviderStorageKey(r.provider))); + const raw = JSON.stringify({ all, visible }); + + this.storageService.store('scm:view:visibleRepositories', raw, StorageScope.WORKSPACE, StorageTarget.MACHINE); + } + + @debounce(2000) + private eventuallyFinishLoading(): void { + this.finishLoading(); + } + + private finishLoading(): void { + if (this.didFinishLoading) { + return; + } + + this.didFinishLoading = true; + this.previousState = undefined; + } + dispose(): void { this.disposables.dispose(); this._onDidChangeRepositories.dispose(); diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 7ce0990a..407c5084 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -34,7 +34,8 @@ export interface ISCMResource { readonly resourceGroup: ISCMResourceGroup; readonly sourceUri: URI; readonly decorations: ISCMResourceDecorations; - readonly contextValue?: string; + readonly contextValue: string | undefined; + readonly command: Command | undefined; open(preserveFocus: boolean): Promise; } diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index 43da9ea7..48af74fb 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator, ISCMInputChangeEvent, SCMInputChangeReason } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { HistoryNavigator2 } from 'vs/base/common/history'; class SCMInput implements ISCMInput { @@ -97,7 +97,7 @@ class SCMInput implements ISCMInput { } if (this.repository.provider.rootUri) { - this.storageService.store(historyKey, JSON.stringify([...this.historyNavigator]), StorageScope.GLOBAL); + this.storageService.store(historyKey, JSON.stringify([...this.historyNavigator]), StorageScope.GLOBAL, StorageTarget.USER); } }); } diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 83a6db8d..c85764d0 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -24,6 +24,8 @@ import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/b import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { mergeSort } from 'vs/base/common/arrays'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { dirname } from 'vs/base/common/resources'; const REPLACE_PREVIEW = 'replacePreview'; @@ -93,7 +95,8 @@ export class ReplaceService implements IReplaceService { @ITextFileService private readonly textFileService: ITextFileService, @IEditorService private readonly editorService: IEditorService, @ITextModelService private readonly textModelResolverService: ITextModelService, - @IBulkEditService private readonly bulkEditorService: IBulkEditService + @IBulkEditService private readonly bulkEditorService: IBulkEditService, + @ILabelService private readonly labelService: ILabelService ) { } replace(match: Match): Promise; @@ -113,6 +116,7 @@ export class ReplaceService implements IReplaceService { leftResource: fileMatch.resource, rightResource: toReplaceResource(fileMatch.resource), label: nls.localize('fileReplaceChanges', "{0} ↔ {1} (Replace Preview)", fileMatch.name(), fileMatch.name()), + description: this.labelService.getUriLabel(dirname(fileMatch.resource), { relative: true }), options: { preserveFocus, pinned, diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 041daae2..b0ccc884 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -28,8 +28,8 @@ import { defaultQuickAccessContextKeyValue } from 'vs/workbench/browser/quickacc import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Extensions as ViewExtensions, IViewsRegistry, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; -import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; -import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition, IExplorerService, VIEWLET_ID as VIEWLET_ID_FILES } from 'vs/workbench/contrib/files/common/files'; +import { getMultiSelectedResources, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; +import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition, VIEWLET_ID as VIEWLET_ID_FILES } from 'vs/workbench/contrib/files/common/files'; import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, togglePreserveCaseCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, ExpandAllAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; @@ -509,11 +509,11 @@ const viewContainer = Registry.as(ViewExtensions.ViewCo name: nls.localize('name', "Search"), ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), hideIfEmpty: true, - icon: searchViewIcon.classNames, + icon: searchViewIcon, order: 1 }, ViewContainerLocation.Sidebar); -const viewDescriptor = { id: VIEW_ID, containerIcon: 'codicon-search', name: nls.localize('search', "Search"), ctorDescriptor: new SyncDescriptor(SearchView), canToggleVisibility: false, canMoveView: true }; +const viewDescriptor = { id: VIEW_ID, containerIcon: searchViewIcon, name: nls.localize('search', "Search"), ctorDescriptor: new SyncDescriptor(SearchView), canToggleVisibility: false, canMoveView: true }; // Register search default location to sidebar Registry.as(ViewExtensions.ViewsRegistry).registerViews([viewDescriptor], viewContainer); @@ -824,7 +824,7 @@ configurationRegistry.registerConfiguration({ nls.localize('search.actionsPositionAuto', "Position the actionbar to the right when the search view is narrow, and immediately after the content when the search view is wide."), nls.localize('search.actionsPositionRight', "Always position the actionbar to the right."), ], - default: 'auto', + default: 'right', description: nls.localize('search.actionsPosition', "Controls the positioning of the actionbar on rows in the search view.") }, 'search.searchOnType': { @@ -861,7 +861,7 @@ configurationRegistry.registerConfiguration({ 'search.searchEditor.reusePriorSearchConfiguration': { type: 'boolean', default: false, - markdownDescription: nls.localize({ key: 'search.searchEditor.reusePriorSearchConfiguration', comment: ['"Search Editor" is a type that editor that can display search results. "includes, excludes, and flags" just refers to settings that affect search. For example, the "search.exclude" setting.'] }, "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor") + markdownDescription: nls.localize({ key: 'search.searchEditor.reusePriorSearchConfiguration', comment: ['"Search Editor" is a type of editor that can display search results. "includes, excludes, and flags" refers to the "files to include" and "files to exclude" input boxes, and the flags that control whether a query is case-sensitive or a regex.'] }, "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor") }, 'search.searchEditor.defaultNumberOfContextLines': { type: ['number', 'null'], diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index d2f10840..4e136901 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -29,6 +29,7 @@ import { SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/sea import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { searchRefreshIcon, searchCollapseAllIcon, searchExpandAllIcon, searchClearIcon, searchReplaceAllIcon, searchReplaceIcon, searchRemoveIcon, searchStopIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export function isSearchViewFocused(viewsService: IViewsService): boolean { const searchView = getSearchView(viewsService); @@ -153,7 +154,7 @@ export abstract class FindOrReplaceInFilesAction extends Action { const searchAndReplaceWidget = openedView.searchAndReplaceWidget; searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); - const updatedText = openedView.updateTextFromSelection({ allowUnselectedWord: !this.expandSearchReplaceWidget }); + const updatedText = openedView.updateTextFromFindWidgetOrSelection({ allowUnselectedWord: !this.expandSearchReplaceWidget }); openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText); } }); @@ -182,7 +183,7 @@ export const FindInFilesCommand: ICommandHandler = (accessor, args: IFindInFiles if (typeof args.query === 'string') { openedView.setSearchParameters(args); } else { - updatedText = openedView.updateTextFromSelection({ allowUnselectedWord: typeof args.replace !== 'string' }); + updatedText = openedView.updateTextFromFindWidgetOrSelection({ allowUnselectedWord: typeof args.replace !== 'string' }); } openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText); } @@ -279,7 +280,7 @@ export class RefreshAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchRefreshIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchRefreshIcon)); } get enabled(): boolean { @@ -309,7 +310,7 @@ export class CollapseDeepestExpandedLevelAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchCollapseAllIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchCollapseAllIcon)); this.update(); } @@ -365,7 +366,7 @@ export class ExpandAllAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchExpandAllIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchExpandAllIcon)); this.update(); } @@ -449,7 +450,7 @@ export class ClearSearchResultsAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchClearIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchClearIcon)); this.update(); } @@ -475,7 +476,7 @@ export class CancelSearchAction extends Action { constructor(id: string, label: string, @IViewsService private readonly viewsService: IViewsService ) { - super(id, label, 'search-action ' + searchStopIcon.classNames); + super(id, label, 'search-action ' + ThemeIcon.asClassName(searchStopIcon)); this.update(); } @@ -610,7 +611,7 @@ export class RemoveAction extends AbstractSearchAndReplaceAction { private viewer: WorkbenchObjectTree, private element: RenderableMatch ) { - super('remove', RemoveAction.LABEL, searchRemoveIcon.classNames); + super('remove', RemoveAction.LABEL, ThemeIcon.asClassName(searchRemoveIcon)); } run(): Promise { @@ -650,7 +651,7 @@ export class ReplaceAllAction extends AbstractSearchAndReplaceAction { private fileMatch: FileMatch, @IKeybindingService keyBindingService: IKeybindingService ) { - super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(ReplaceAllAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), searchReplaceAllIcon.classNames); + super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(ReplaceAllAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceAllIcon)); } run(): Promise { @@ -674,7 +675,7 @@ export class ReplaceAllInFolderAction extends AbstractSearchAndReplaceAction { constructor(private viewer: WorkbenchObjectTree, private folderMatch: FolderMatch, @IKeybindingService keyBindingService: IKeybindingService ) { - super(Constants.ReplaceAllInFolderActionId, appendKeyBindingLabel(ReplaceAllInFolderAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFolderActionId), keyBindingService), searchReplaceAllIcon.classNames); + super(Constants.ReplaceAllInFolderActionId, appendKeyBindingLabel(ReplaceAllInFolderAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFolderActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceAllIcon)); } run(): Promise { @@ -701,7 +702,7 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { @IConfigurationService private readonly configurationService: IConfigurationService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { - super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), searchReplaceIcon.classNames); + super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), ThemeIcon.asClassName(searchReplaceIcon)); } async run(): Promise { diff --git a/src/vs/workbench/contrib/search/browser/searchIcons.ts b/src/vs/workbench/contrib/search/browser/searchIcons.ts index 2138fbaa..8c6d633b 100644 --- a/src/vs/workbench/contrib/search/browser/searchIcons.ts +++ b/src/vs/workbench/contrib/search/browser/searchIcons.ts @@ -3,25 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -export const searchDetailsIcon = registerIcon('search-details', Codicon.ellipsis); +export const searchDetailsIcon = registerIcon('search-details', Codicon.ellipsis, localize('searchDetailsIcon', 'Icon to make search details visible.')); -export const searchShowContextIcon = registerIcon('search-show-context', Codicon.listSelection); -export const searchHideReplaceIcon = registerIcon('search-hide-replace', Codicon.chevronRight); -export const searchShowReplaceIcon = registerIcon('search-show-replace', Codicon.chevronDown); -export const searchReplaceAllIcon = registerIcon('search-replace-all', Codicon.replaceAll); -export const searchReplaceIcon = registerIcon('search-replace', Codicon.replace); -export const searchRemoveIcon = registerIcon('search-remove', Codicon.close); +export const searchShowContextIcon = registerIcon('search-show-context', Codicon.listSelection, localize('searchShowContextIcon', 'Icon for toggle the context in the search editor.')); +export const searchHideReplaceIcon = registerIcon('search-hide-replace', Codicon.chevronRight, localize('searchHideReplaceIcon', 'Icon to collapse the replace section in the search view.')); +export const searchShowReplaceIcon = registerIcon('search-show-replace', Codicon.chevronDown, localize('searchShowReplaceIcon', 'Icon to expand the replace section in the search view.')); +export const searchReplaceAllIcon = registerIcon('search-replace-all', Codicon.replaceAll, localize('searchReplaceAllIcon', 'Icon for replace all in the search view.')); +export const searchReplaceIcon = registerIcon('search-replace', Codicon.replace, localize('searchReplaceIcon', 'Icon for replace in the search view.')); +export const searchRemoveIcon = registerIcon('search-remove', Codicon.close, localize('searchRemoveIcon', 'Icon to remove a search result.')); -export const searchRefreshIcon = registerIcon('search-refresh', Codicon.refresh); -export const searchCollapseAllIcon = registerIcon('search-collapse-results', Codicon.collapseAll); -export const searchExpandAllIcon = registerIcon('search-expand-results', Codicon.expandAll); -export const searchClearIcon = registerIcon('search-clear-results', Codicon.clearAll); -export const searchStopIcon = Codicon.searchStop; +export const searchRefreshIcon = registerIcon('search-refresh', Codicon.refresh, localize('searchRefreshIcon', 'Icon for refresh in the search view.')); +export const searchCollapseAllIcon = registerIcon('search-collapse-results', Codicon.collapseAll, localize('searchCollapseAllIcon', 'Icon for collapse results in the search view.')); +export const searchExpandAllIcon = registerIcon('search-expand-results', Codicon.expandAll, localize('searchExpandAllIcon', 'Icon for expand results in the search view.')); +export const searchClearIcon = registerIcon('search-clear-results', Codicon.clearAll, localize('searchClearIcon', 'Icon for clear results in the search view.')); +export const searchStopIcon = registerIcon('search-stop', Codicon.searchStop, localize('searchStopIcon', 'Icon for stop in the search view.')); -export const searchViewIcon = Codicon.search; - -export const searchNewEditorIcon = registerIcon('search-new-editor', Codicon.newFile); -export const searchGotoFileIcon = registerIcon('search-goto-file', Codicon.goToFile); +export const searchViewIcon = registerIcon('search-view-icon', Codicon.search, localize('searchViewIcon', 'View icon of the search view.')); +export const searchNewEditorIcon = registerIcon('search-new-editor', Codicon.newFile, localize('searchNewEditorIcon', 'Icon for the action to open a new search editor.')); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 71a2cae4..5a42e7b8 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -22,6 +22,7 @@ import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchview'; import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { CommonFindController } from 'vs/editor/contrib/find/findController'; import * as nls from 'vs/nls'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -37,7 +38,7 @@ import { IProgressService, IProgressStep, IProgress } from 'vs/platform/progress import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, SearchSortOrder, SearchCompletionExitCode } from 'vs/workbench/services/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground, foreground } from 'vs/platform/theme/common/colorRegistry'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ResourceLabels } from 'vs/workbench/browser/labels'; @@ -59,7 +60,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/multicursor'; import { Selection } from 'vs/editor/common/core/selection'; @@ -219,7 +220,7 @@ export class SearchView extends ViewPane { this.viewModel = this._register(this.searchWorkbenchService.searchModel); this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); this.memento = new Memento(this.id, storageService); - this.viewletState = this.memento.getMemento(StorageScope.WORKSPACE); + this.viewletState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.USER); this._register(this.fileService.onDidFilesChange(e => this.onFilesChanged(e))); this._register(this.textFileService.untitled.onDidDispose(model => this.onUntitledDidDispose(model.resource))); @@ -282,7 +283,7 @@ export class SearchView extends ViewPane { // Toggle query details button this.toggleQueryDetailsButton = dom.append(this.queryDetails, - $('.more' + searchDetailsIcon.cssSelector, { tabindex: 0, role: 'button', title: nls.localize('moreSearch', "Toggle Search Details") })); + $('.more' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button', title: nls.localize('moreSearch', "Toggle Search Details") })); this._register(dom.addDisposableListener(this.toggleQueryDetailsButton, dom.EventType.CLICK, e => { dom.EventHelper.stop(e); @@ -888,28 +889,63 @@ export class SearchView extends ViewPane { } } - updateTextFromSelection({ allowUnselectedWord = true, allowSearchOnType = true }): boolean { - let updatedText = false; - const seedSearchStringFromSelection = this.configurationService.getValue('editor').find!.seedSearchStringFromSelection; - if (seedSearchStringFromSelection) { - let selectedText = this.getSearchTextFromEditor(allowUnselectedWord); - if (selectedText) { - if (this.searchWidget.searchInput.getRegex()) { - selectedText = strings.escapeRegExpCharacters(selectedText); - } - - if (allowSearchOnType && !this.viewModel.searchResult.isDirty) { - this.searchWidget.setValue(selectedText); - } else { - this.pauseSearching = true; - this.searchWidget.setValue(selectedText); - this.pauseSearching = false; - } - updatedText = true; + updateTextFromFindWidgetOrSelection({ allowUnselectedWord = true, allowSearchOnType = true }): boolean { + const activeEditor = this.editorService.activeTextEditorControl; + if (isCodeEditor(activeEditor)) { + const controller = CommonFindController.get(activeEditor as ICodeEditor); + if (controller.isFindInputFocused()) { + return this.updateTextFromFindWidget(controller, { allowSearchOnType }); } } - return updatedText; + return this.updateTextFromSelection({ allowUnselectedWord, allowSearchOnType }); + } + + private updateTextFromFindWidget(controller: CommonFindController, { allowSearchOnType = true }): boolean { + if (!this.searchConfig.seedWithNearestWord && (window.getSelection()?.toString() ?? '') === '') { + return false; + } + + const searchString = controller.getState().searchString; + if (searchString === '') { + return false; + } + + this.searchWidget.searchInput.setCaseSensitive(controller.getState().matchCase); + this.searchWidget.searchInput.setWholeWords(controller.getState().wholeWord); + this.searchWidget.searchInput.setRegex(controller.getState().isRegex); + this.updateText(searchString, allowSearchOnType); + + return true; + } + + private updateTextFromSelection({ allowUnselectedWord = true, allowSearchOnType = true }): boolean { + const seedSearchStringFromSelection = this.configurationService.getValue('editor').find!.seedSearchStringFromSelection; + if (!seedSearchStringFromSelection) { + return false; + } + + let selectedText = this.getSearchTextFromEditor(allowUnselectedWord); + if (selectedText === null) { + return false; + } + + if (this.searchWidget.searchInput.getRegex()) { + selectedText = strings.escapeRegExpCharacters(selectedText); + } + + this.updateText(selectedText, allowSearchOnType); + return true; + } + + private updateText(text: string, allowSearchOnType: boolean = true) { + if (allowSearchOnType && !this.viewModel.searchResult.isDirty) { + this.searchWidget.setValue(text); + } else { + this.pauseSearching = true; + this.searchWidget.setValue(text); + this.pauseSearching = false; + } } focusNextInputBox(): void { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index c566e5d3..a7dbe426 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -25,7 +25,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import { attachFindReplaceInputBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { appendKeyBindingLabel, isSearchViewFocused, getSearchView } from 'vs/workbench/contrib/search/browser/searchActions'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -34,6 +34,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { IViewsService } from 'vs/workbench/common/views'; import { searchReplaceAllIcon, searchHideReplaceIcon, searchShowContextIcon, searchShowReplaceIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; +import { ToggleSearchEditorContextLinesCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants'; /** Specified in searchview.css */ export const SingleLineInputHeight = 24; @@ -56,7 +57,7 @@ class ReplaceAllAction extends Action { static readonly ID: string = 'search.action.replaceAll'; constructor(private _searchWidget: SearchWidget) { - super(ReplaceAllAction.ID, '', searchReplaceAllIcon.classNames, false); + super(ReplaceAllAction.ID, '', ThemeIcon.asClassName(searchReplaceAllIcon), false); } set searchWidget(searchWidget: SearchWidget) { @@ -293,9 +294,9 @@ export class SearchWidget extends Widget { }; this.toggleReplaceButton = this._register(new Button(parent, opts)); this.toggleReplaceButton.element.setAttribute('aria-expanded', 'false'); - this.toggleReplaceButton.element.classList.add(...searchHideReplaceIcon.classNamesArray); - this.toggleReplaceButton.icon = 'toggle-replace-button'; - // TODO@joh need to dispose this listener eventually + this.toggleReplaceButton.element.classList.add('toggle-replace-button'); + this.toggleReplaceButton.icon = ThemeIcon.asCSSIcon(searchHideReplaceIcon); + // TODO@joao need to dispose this listener eventually this.toggleReplaceButton.onDidClick(() => this.onToggleReplaceButton()); this.toggleReplaceButton.element.title = nls.localize('search.replace.toggle.button.title', "Toggle Replace"); } @@ -351,7 +352,11 @@ export class SearchWidget extends Widget { this._register(this.searchInputFocusTracker.onDidBlur(() => this.searchInputBoxFocused.set(false))); - this.showContextCheckbox = new Checkbox({ isChecked: false, title: nls.localize('showContext', "Show Context"), icon: searchShowContextIcon }); + this.showContextCheckbox = new Checkbox({ + isChecked: false, + title: appendKeyBindingLabel(nls.localize('showContext', "Toggle Context Lines"), this.keyBindingService.lookupKeybinding(ToggleSearchEditorContextLinesCommandId), this.keyBindingService), + icon: ThemeIcon.asCSSIcon(searchShowContextIcon) + }); this._register(this.showContextCheckbox.onChange(() => this.onContextLinesChanged())); if (options.showContextToggle) { @@ -431,11 +436,11 @@ export class SearchWidget extends Widget { private onToggleReplaceButton(): void { this.replaceContainer.classList.toggle('disabled'); if (this.isReplaceShown()) { - this.toggleReplaceButton.element.classList.remove(...searchHideReplaceIcon.classNamesArray); - this.toggleReplaceButton.element.classList.add(...searchShowReplaceIcon.classNamesArray); + this.toggleReplaceButton.element.classList.remove(...ThemeIcon.asClassNameArray(searchHideReplaceIcon)); + this.toggleReplaceButton.element.classList.add(...ThemeIcon.asClassNameArray(searchShowReplaceIcon)); } else { - this.toggleReplaceButton.element.classList.remove(...searchShowReplaceIcon.classNamesArray); - this.toggleReplaceButton.element.classList.add(...searchHideReplaceIcon.classNamesArray); + this.toggleReplaceButton.element.classList.remove(...ThemeIcon.asClassNameArray(searchShowReplaceIcon)); + this.toggleReplaceButton.element.classList.add(...ThemeIcon.asClassNameArray(searchHideReplaceIcon)); } this.toggleReplaceButton.element.setAttribute('aria-expanded', this.isReplaceShown() ? 'true' : 'false'); this.updateReplaceActiveState(); diff --git a/src/vs/workbench/contrib/search/common/searchHistoryService.ts b/src/vs/workbench/contrib/search/common/searchHistoryService.ts index bb5a9209..018799b9 100644 --- a/src/vs/workbench/contrib/search/common/searchHistoryService.ts +++ b/src/vs/workbench/contrib/search/common/searchHistoryService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { isEmptyObject } from 'vs/base/common/types'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -61,7 +61,7 @@ export class SearchHistoryService implements ISearchHistoryService { if (isEmptyObject(history)) { this.storageService.remove(SearchHistoryService.SEARCH_HISTORY_KEY, StorageScope.WORKSPACE); } else { - this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE); + this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE, StorageTarget.USER); } } } diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 0c6e0d19..3c7044d0 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -31,6 +31,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { compareFileNames, compareFileExtensions, comparePaths } from 'vs/base/common/comparers'; import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; export class Match { @@ -698,7 +699,7 @@ export class SearchResult extends Disposable { private _folderMatches: FolderMatchWithResource[] = []; private _otherFilesMatch: FolderMatch | null = null; - private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forUris(); + private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); private _showHighlights: boolean = false; private _query: ITextQuery | null = null; @@ -713,6 +714,7 @@ export class SearchResult extends Disposable { @ITelemetryService private readonly telemetryService: ITelemetryService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, ) { super(); this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); @@ -743,7 +745,7 @@ export class SearchResult extends Disposable { .then(() => this._isDirty = false); this._rangeHighlightDecorations.removeHighlightRange(); - this._folderMatchesMap = TernarySearchTree.forUris(); + this._folderMatchesMap = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); if (!query) { return; @@ -965,7 +967,7 @@ export class SearchResult extends Disposable { private disposeMatches(): void { this.folderMatches().forEach(folderMatch => folderMatch.dispose()); this._folderMatches = []; - this._folderMatchesMap = TernarySearchTree.forUris(); + this._folderMatchesMap = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); this._rangeHighlightDecorations.removeHighlightRange(); } diff --git a/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts b/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts index 0a86fa7f..9bd262f9 100644 --- a/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/queryBuilder.test.ts @@ -10,13 +10,14 @@ import { URI as uri } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IWorkspaceContextService, toWorkspaceFolder, toWorkspaceFolders, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, toWorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IFileQuery, IFolderQuery, IPatternInfo, ITextQuery, QueryType } from 'vs/workbench/services/search/common/search'; import { TestPathService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index 363eab38..9dc2993c 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -17,6 +17,10 @@ import { isWindows } from 'vs/base/common/platform'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; suite('Search - Viewlet', () => { let instantiation: TestInstantiationService; @@ -25,6 +29,7 @@ suite('Search - Viewlet', () => { instantiation = new TestInstantiationService(); instantiation.stub(IModelService, stubModelService(instantiation)); instantiation.set(IWorkspaceContextService, new TestContextService(TestWorkspace)); + instantiation.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); }); test('Data Source', function () { diff --git a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts index 545d7de9..0a99a6a7 100644 --- a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts @@ -21,6 +21,10 @@ import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; import * as process from 'vs/base/common/process'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; const nullEvent = new class { id: number = -1; @@ -72,6 +76,7 @@ suite('SearchModel', () => { instantiationService.stub(IModelService, stubModelService(instantiationService)); instantiationService.stub(ISearchService, {}); instantiationService.stub(ISearchService, 'textSearch', Promise.resolve({ results: [] })); + instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); const config = new TestConfigurationService(); config.setUserConfiguration('search', { searchOnType: true }); diff --git a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts b/src/vs/workbench/contrib/search/test/common/searchResult.test.ts index f46f158a..e147ce75 100644 --- a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchResult.test.ts @@ -18,6 +18,10 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; const lineOneRange = new OneLineRange(1, 0, 1); @@ -29,6 +33,7 @@ suite('SearchResult', () => { instantiationService = new TestInstantiationService(); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); instantiationService.stubPromise(IReplaceService, {}); instantiationService.stubPromise(IReplaceService, 'replace', null); }); diff --git a/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts b/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts index 92b450d2..e6a021cc 100644 --- a/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts +++ b/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts @@ -6,12 +6,13 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IWorkspaceContextService, toWorkspaceFolder, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { TestEnvironmentService, TestNativePathService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { assertEqualSearchPathResults, getUri, patternsToIExpression, globalGlob, fixPath } from 'vs/workbench/contrib/search/test/browser/queryBuilder.test'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index b37c41a7..e4d4b412 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -15,3 +15,4 @@ export const SearchEditorID = 'workbench.editor.searchEditor'; export const OpenNewEditorCommandId = 'search.action.openNewEditor'; export const OpenEditorCommandId = 'search.action.openEditor'; +export const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 08518d3e..cc6faa97 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -15,7 +15,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -41,7 +41,6 @@ const FocusQueryEditorWidgetCommandId = 'search.action.focusQueryEditorWidget'; const ToggleSearchEditorCaseSensitiveCommandId = 'toggleSearchEditorCaseSensitive'; const ToggleSearchEditorWholeWordCommandId = 'toggleSearchEditorWholeWord'; const ToggleSearchEditorRegexCommandId = 'toggleSearchEditorRegex'; -const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; const IncreaseSearchEditorContextLinesCommandId = 'increaseSearchEditorContextLines'; const DecreaseSearchEditorContextLinesCommandId = 'decreaseSearchEditorContextLines'; @@ -161,60 +160,6 @@ Registry.as(EditorInputExtensions.EditorInputFactor //#endregion //#region Commands -KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ - id: ToggleSearchEditorCaseSensitiveCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey), - handler: toggleSearchEditorCaseSensitiveCommand -}, ToggleCaseSensitiveKeybinding)); - -KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ - id: ToggleSearchEditorWholeWordCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey), - handler: toggleSearchEditorWholeWordCommand -}, ToggleWholeWordKeybinding)); - -KeybindingsRegistry.registerCommandAndKeybindingRule(Object.assign({ - id: ToggleSearchEditorRegexCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor, SearchConstants.SearchInputBoxFocusedKey), - handler: toggleSearchEditorRegexCommand -}, ToggleRegexKeybinding)); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: ToggleSearchEditorContextLinesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - handler: toggleSearchEditorContextLinesCommand, - primary: KeyMod.Alt | KeyCode.KEY_L, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: IncreaseSearchEditorContextLinesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - handler: (accessor: ServicesAccessor) => modifySearchEditorContextLinesCommand(accessor, true), - primary: KeyMod.Alt | KeyCode.US_EQUAL -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: DecreaseSearchEditorContextLinesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - handler: (accessor: ServicesAccessor) => modifySearchEditorContextLinesCommand(accessor, false), - primary: KeyMod.Alt | KeyCode.US_MINUS -}); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: SelectAllSearchEditorMatchesCommandId, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, - handler: selectAllSearchEditorMatchesCommand -}); - CommandsRegistry.registerCommand( CleanSearchEditorStateCommandId, (accessor: ServicesAccessor) => { @@ -289,7 +234,6 @@ registerAction2(class extends Action2 { title: { value: localize('searchEditor.deleteResultBlock', "Delete File Results"), original: 'Delete File Results' }, keybinding: { weight: KeybindingWeight.EditorContrib, - when: SearchEditorConstants.InSearchEditor, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Backspace, }, precondition: SearchEditorConstants.InSearchEditor, @@ -416,13 +360,10 @@ registerAction2(class extends Action2 { id: FocusQueryEditorWidgetCommandId, title: { value: localize('search.action.focusQueryEditorWidget', "Focus Search Editor Input"), original: 'Focus Search Editor Input' }, category, - menu: { - id: MenuId.CommandPalette, - when: ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID) - }, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, keybinding: { primary: KeyCode.Escape, - when: SearchEditorConstants.InSearchEditor, weight: KeybindingWeight.EditorContrib } }); @@ -435,4 +376,134 @@ registerAction2(class extends Action2 { } } }); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: ToggleSearchEditorCaseSensitiveCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorCaseSensitive', "Toggle Match Case"), original: 'Toggle Match Case' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: Object.assign({ + weight: KeybindingWeight.WorkbenchContrib, + when: SearchConstants.SearchInputBoxFocusedKey, + }, ToggleCaseSensitiveKeybinding) + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorCaseSensitiveCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: ToggleSearchEditorWholeWordCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorWholeWord', "Toggle Match Whole Word"), original: 'Toggle Match Whole Word' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: Object.assign({ + weight: KeybindingWeight.WorkbenchContrib, + when: SearchConstants.SearchInputBoxFocusedKey, + }, ToggleWholeWordKeybinding) + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorWholeWordCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: ToggleSearchEditorRegexCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorRegex', "Toggle Use Regular Expression"), original: 'Toggle Use Regular Expression"' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: Object.assign({ + weight: KeybindingWeight.WorkbenchContrib, + when: SearchConstants.SearchInputBoxFocusedKey, + }, ToggleRegexKeybinding) + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorRegexCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: SearchEditorConstants.ToggleSearchEditorContextLinesCommandId, + title: { value: localize('searchEditor.action.toggleSearchEditorContextLines', "Toggle Context Lines"), original: 'Toggle Context Lines"' }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Alt | KeyCode.KEY_L, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } + } + }); + } + run(accessor: ServicesAccessor) { + toggleSearchEditorContextLinesCommand(accessor); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: IncreaseSearchEditorContextLinesCommandId, + title: { original: 'Increase Context Lines', value: localize('searchEditor.action.increaseSearchEditorContextLines', "Increase Context Lines") }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Alt | KeyCode.US_EQUAL + } + }); + } + run(accessor: ServicesAccessor) { modifySearchEditorContextLinesCommand(accessor, true); } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: DecreaseSearchEditorContextLinesCommandId, + title: { original: 'Decrease Context Lines', value: localize('searchEditor.action.decreaseSearchEditorContextLines', "Decrease Context Lines") }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Alt | KeyCode.US_MINUS + } + }); + } + run(accessor: ServicesAccessor) { modifySearchEditorContextLinesCommand(accessor, false); } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: SelectAllSearchEditorMatchesCommandId, + title: { original: 'Select All Matches', value: localize('searchEditor.action.selectAllSearchEditorMatches', "Select All Matches") }, + category, + f1: true, + precondition: SearchEditorConstants.InSearchEditor, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, + } + }); + } + run(accessor: ServicesAccessor) { + selectAllSearchEditorMatchesCommand(accessor); + } +}); //#endregion diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 7b7ddcc6..caf6d801 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -34,7 +34,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { inputBorder, registerColor, searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; @@ -139,7 +139,7 @@ export class SearchEditor extends BaseTextEditor { this.includesExcludesContainer = DOM.append(this.queryEditorContainer, DOM.$('.includes-excludes')); // // Toggle query details button - this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand' + searchDetailsIcon.cssSelector, { tabindex: 0, role: 'button', title: localize('moreSearch', "Toggle Search Details") })); + this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button', title: localize('moreSearch', "Toggle Search Details") })); this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.CLICK, e => { DOM.EventHelper.stop(e); this.toggleIncludesExcludes(); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index 0c124a1f..f1d1426f 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -28,6 +28,7 @@ import { OpenNewEditorCommandId } from 'vs/workbench/contrib/searchEditor/browse import { OpenSearchEditorArgs } from 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution'; import { EditorsOrder } from 'vs/workbench/common/editor'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export const toggleSearchEditorCaseSensitiveCommand = (accessor: ServicesAccessor) => { const editorService = accessor.get(IEditorService); @@ -85,7 +86,7 @@ export class OpenSearchEditorAction extends Action { constructor(id: string, label: string, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { - super(id, label, searchNewEditorIcon.classNames); + super(id, label, ThemeIcon.asClassName(searchNewEditorIcon)); } update() { diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index a75dbd61..c5a551a4 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -16,7 +16,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EditorInput, GroupIdentifier, IEditorInput, IMoveResult, IRevertOptions, ISaveOptions, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, EditorResourceAccessor } from 'vs/workbench/common/editor'; import { Memento } from 'vs/workbench/common/memento'; @@ -65,7 +65,7 @@ export class SearchEditorInput extends EditorInput { public get config(): Readonly { return this._config; } public set config(value: Readonly) { this._config = value; - this.memento.getMemento(StorageScope.WORKSPACE).searchConfig = value; + this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE).searchConfig = value; this._onDidChangeLabel.fire(); } @@ -276,7 +276,7 @@ export class SearchEditorInput extends EditorInput { const searchFileName = (query.replace(/[^\w \-_]+/g, '_') || 'Search') + SEARCH_EDITOR_EXT; - return joinPath(this.fileDialogService.defaultFilePath(this.pathService.defaultUriScheme) || (await this.pathService.userHome()), searchFileName); + return joinPath(await this.fileDialogService.defaultFilePath(this.pathService.defaultUriScheme), searchFileName); } } @@ -298,7 +298,7 @@ export const getOrMakeSearchEditorInput = ( const reuseOldSettings = searchEditorSettings.reusePriorSearchConfiguration; const defaultNumberOfContextLines = searchEditorSettings.defaultNumberOfContextLines; - const priorConfig: SearchConfiguration = reuseOldSettings ? new Memento(SearchEditorInput.ID, storageService).getMemento(StorageScope.WORKSPACE).searchConfig : {}; + const priorConfig: SearchConfiguration = reuseOldSettings ? new Memento(SearchEditorInput.ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE).searchConfig : {}; const defaultConfig = defaultSearchConfig(); const config = { ...defaultConfig, ...priorConfig, ...existingData.config }; diff --git a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts index fd8440e4..48b60f97 100644 --- a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts @@ -15,10 +15,9 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { IQuickPickItem, IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { Codicon } from 'vs/base/common/codicons'; +import { Event } from 'vs/base/common/event'; -interface ISnippetPick extends IQuickPickItem { - snippet: Snippet; -} class Args { @@ -91,7 +90,7 @@ class InsertSnippetAction extends EditorAction { const clipboardService = accessor.get(IClipboardService); const quickInputService = accessor.get(IQuickInputService); - const snippet = await new Promise(async (resolve, reject) => { + const snippet = await new Promise(async (resolve) => { const { lineNumber, column } = editor.getPosition(); let { snippet, name, langId } = Args.fromUser(arg); @@ -129,44 +128,13 @@ class InsertSnippetAction extends EditorAction { if (name) { // take selected snippet - (await snippetService.getSnippets(languageId)).every(snippet => { - if (snippet.name !== name) { - return true; - } - resolve(snippet); - return false; - }); + const snippet = (await snippetService.getSnippets(languageId)).find(snippet => snippet.name === name); + resolve(snippet); + } else { // let user pick a snippet - const snippets = (await snippetService.getSnippets(languageId)).sort(Snippet.compare); - const picks: QuickPickInput[] = []; - let prevSnippet: Snippet | undefined; - for (const snippet of snippets) { - const pick: ISnippetPick = { - label: snippet.prefix, - detail: snippet.description, - snippet - }; - if (!prevSnippet || prevSnippet.snippetSource !== snippet.snippetSource) { - let label = ''; - switch (snippet.snippetSource) { - case SnippetSource.User: - label = nls.localize('sep.userSnippet', "User Snippets"); - break; - case SnippetSource.Extension: - label = nls.localize('sep.extSnippet', "Extension Snippets"); - break; - case SnippetSource.Workspace: - label = nls.localize('sep.workspaceSnippet', "Workspace Snippets"); - break; - } - picks.push({ type: 'separator', label }); - - } - picks.push(pick); - prevSnippet = snippet; - } - return quickInputService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); + const snippet = await this._pickSnippet(snippetService, quickInputService, languageId); + resolve(snippet); } }); @@ -179,6 +147,80 @@ class InsertSnippetAction extends EditorAction { } SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); } + + private async _pickSnippet(snippetService: ISnippetsService, quickInputService: IQuickInputService, languageId: LanguageId): Promise { + + interface ISnippetPick extends IQuickPickItem { + snippet: Snippet; + } + + const snippets = (await snippetService.getSnippets(languageId, { includeDisabledSnippets: true, includeNoPrefixSnippets: true })).sort(Snippet.compare); + + const makeSnippetPicks = () => { + const result: QuickPickInput[] = []; + let prevSnippet: Snippet | undefined; + for (const snippet of snippets) { + const pick: ISnippetPick = { + label: snippet.prefix || snippet.name, + detail: snippet.description, + snippet + }; + if (!prevSnippet || prevSnippet.snippetSource !== snippet.snippetSource) { + let label = ''; + switch (snippet.snippetSource) { + case SnippetSource.User: + label = nls.localize('sep.userSnippet', "User Snippets"); + break; + case SnippetSource.Extension: + label = nls.localize('sep.extSnippet', "Extension Snippets"); + break; + case SnippetSource.Workspace: + label = nls.localize('sep.workspaceSnippet', "Workspace Snippets"); + break; + } + result.push({ type: 'separator', label }); + } + + if (snippet.snippetSource === SnippetSource.Extension) { + const isEnabled = snippetService.isEnabled(snippet); + if (isEnabled) { + pick.buttons = [{ + iconClass: Codicon.eyeClosed.classNames, + tooltip: nls.localize('disableSnippet', 'Hide from IntelliSense') + }]; + } else { + pick.description = nls.localize('isDisabled', "(hidden from IntelliSense)"); + pick.buttons = [{ + iconClass: Codicon.eye.classNames, + tooltip: nls.localize('enable.snippet', 'Show in IntelliSense') + }]; + } + } + + result.push(pick); + prevSnippet = snippet; + } + return result; + }; + + const picker = quickInputService.createQuickPick(); + picker.placeholder = nls.localize('pick.placeholder', "Select a snippet"); + picker.matchOnDescription = true; + picker.ignoreFocusOut = false; + picker.onDidTriggerItemButton(ctx => { + const isEnabled = snippetService.isEnabled(ctx.item.snippet); + snippetService.updateEnablement(ctx.item.snippet, !isEnabled); + picker.items = makeSnippetPicks(); + }); + picker.items = makeSnippetPicks(); + picker.show(); + + // wait for an item to be picked or the picker to become hidden + await Promise.race([Event.toPromise(picker.onDidAccept), Event.toPromise(picker.onDidHide)]); + const result = picker.selectedItems[0]?.snippet; + picker.dispose(); + return result; + } } registerEditorAction(InsertSnippetAction); diff --git a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts index c7adf5c4..7ccba80f 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippets.contribution.ts @@ -13,15 +13,24 @@ import { SnippetFile, Snippet } from 'vs/workbench/contrib/snippets/browser/snip export const ISnippetsService = createDecorator('snippetService'); +export interface ISnippetGetOptions { + includeDisabledSnippets?: boolean; + includeNoPrefixSnippets?: boolean; +} + export interface ISnippetsService { readonly _serviceBrand: undefined; getSnippetFiles(): Promise>; - getSnippets(languageId: LanguageId): Promise; + isEnabled(snippet: Snippet): boolean; - getSnippetsSync(languageId: LanguageId): Snippet[]; + updateEnablement(snippet: Snippet, enabled: boolean): void; + + getSnippets(languageId: LanguageId, opt?: ISnippetGetOptions): Promise; + + getSnippetsSync(languageId: LanguageId, opt?: ISnippetGetOptions): Snippet[]; } const languageScopeSchemaId = 'vscode://schemas/snippets'; @@ -56,7 +65,7 @@ const languageScopeSchema: IJSONSchema = { description: nls.localize('snippetSchema.json', 'User snippet configuration'), additionalProperties: { type: 'object', - required: ['prefix', 'body'], + required: ['body'], properties: snippetSchemaProperties, additionalProperties: false } @@ -76,7 +85,7 @@ const globalSchema: IJSONSchema = { description: nls.localize('snippetSchema.json', 'User snippet configuration'), additionalProperties: { type: 'object', - required: ['prefix', 'body'], + required: ['body'], properties: { ...snippetSchemaProperties, scope: { diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts index a9e0f87a..98d24e31 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsFile.ts @@ -15,6 +15,9 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IdleValue } from 'vs/base/common/async'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { relativePath } from 'vs/base/common/resources'; +import { isObject } from 'vs/base/common/types'; +import { Iterable } from 'vs/base/common/iterator'; class SnippetBodyInsights { @@ -86,9 +89,9 @@ export class Snippet { readonly body: string, readonly source: string, readonly snippetSource: SnippetSource, + readonly snippetIdentifier?: string ) { - // - this.prefixLow = prefix ? prefix.toLowerCase() : prefix; + this.prefixLow = prefix.toLowerCase(); this._bodyInsights = new IdleValue(() => new SnippetBodyInsights(this.body)); } @@ -121,14 +124,14 @@ export class Snippet { interface JsonSerializedSnippet { - body: string; + body: string | string[]; scope: string; - prefix: string | string[]; + prefix: string | string[] | undefined; description: string; } function isJsonSerializedSnippet(thing: any): thing is JsonSerializedSnippet { - return Boolean((thing).body) && Boolean((thing).prefix); + return isObject(thing) && Boolean((thing).body); } interface JsonSerializedSnippets { @@ -242,18 +245,21 @@ export class SnippetFile { let { prefix, body, description } = snippet; + if (!prefix) { + prefix = ''; + } + if (Array.isArray(body)) { body = body.join('\n'); } + if (typeof body !== 'string') { + return; + } if (Array.isArray(description)) { description = description.join('\n'); } - if ((typeof prefix !== 'string' && !Array.isArray(prefix)) || typeof body !== 'string') { - return; - } - let scopes: string[]; if (this.defaultScopes) { scopes = this.defaultScopes; @@ -280,17 +286,17 @@ export class SnippetFile { } } - let prefixes = Array.isArray(prefix) ? prefix : [prefix]; - prefixes.forEach(p => { + for (const _prefix of Array.isArray(prefix) ? prefix : Iterable.single(prefix)) { bucket.push(new Snippet( scopes, name, - p, + _prefix, description, body, source, - this.source + this.source, + this._extension && `${relativePath(this._extension.extensionLocation, this.location)}/${name}` )); - }); + } } } diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 3976dd12..a1bace7e 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -19,12 +19,16 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { IWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution'; +import { ISnippetGetOptions, ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution'; import { Snippet, SnippetFile, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService'; import { SnippetCompletionProvider } from './snippetCompletionProvider'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +import { ResourceMap } from 'vs/base/common/map'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { isStringArray } from 'vs/base/common/types'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; namespace snippetExt { @@ -123,13 +127,52 @@ function watch(service: IFileService, resource: URI, callback: () => any): IDisp ); } +class SnippetEnablement { + + private static _key = 'snippets.ignoredSnippets'; + + private readonly _ignored: Set; + + constructor( + @IStorageService private readonly _storageService: IStorageService, + ) { + + const raw = _storageService.get(SnippetEnablement._key, StorageScope.GLOBAL, ''); + let data: string[] | undefined; + try { + data = JSON.parse(raw); + } catch { } + + this._ignored = isStringArray(data) ? new Set(data) : new Set(); + } + + isIgnored(id: string): boolean { + return this._ignored.has(id); + } + + updateIgnored(id: string, value: boolean): void { + let changed = false; + if (this._ignored.has(id) && !value) { + this._ignored.delete(id); + changed = true; + } else if (!this._ignored.has(id) && value) { + this._ignored.add(id); + changed = true; + } + if (changed) { + this._storageService.store(SnippetEnablement._key, JSON.stringify(Array.from(this._ignored)), StorageScope.GLOBAL, StorageTarget.USER); + } + } +} + class SnippetsService implements ISnippetsService { - readonly _serviceBrand: undefined; + declare readonly _serviceBrand: undefined; private readonly _disposables = new DisposableStore(); private readonly _pendingWork: Promise[] = []; - private readonly _files = new Map(); + private readonly _files = new ResourceMap(); + private readonly _enablement: SnippetEnablement; constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService, @@ -139,6 +182,7 @@ class SnippetsService implements ISnippetsService { @IFileService private readonly _fileService: IFileService, @IExtensionResourceLoaderService private readonly _extensionResourceLoaderService: IExtensionResourceLoaderService, @ILifecycleService lifecycleService: ILifecycleService, + @IInstantiationService instantiationService: IInstantiationService, ) { this._pendingWork.push(Promise.resolve(lifecycleService.when(LifecyclePhase.Restored).then(() => { this._initExtensionSnippets(); @@ -147,12 +191,24 @@ class SnippetsService implements ISnippetsService { }))); setSnippetSuggestSupport(new SnippetCompletionProvider(this._modeService, this)); + + this._enablement = instantiationService.createInstance(SnippetEnablement); } dispose(): void { this._disposables.dispose(); } + isEnabled(snippet: Snippet): boolean { + return !snippet.snippetIdentifier || !this._enablement.isIgnored(snippet.snippetIdentifier); + } + + updateEnablement(snippet: Snippet, enabled: boolean): void { + if (snippet.snippetIdentifier) { + this._enablement.updateIgnored(snippet.snippetIdentifier, !enabled); + } + } + private _joinSnippets(): Promise { const promises = this._pendingWork.slice(0); this._pendingWork.length = 0; @@ -164,26 +220,27 @@ class SnippetsService implements ISnippetsService { return this._files.values(); } - getSnippets(languageId: LanguageId): Promise { - return this._joinSnippets().then(() => { - const result: Snippet[] = []; - const promises: Promise[] = []; + async getSnippets(languageId: LanguageId, opts?: ISnippetGetOptions): Promise { + await this._joinSnippets(); - const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); - if (languageIdentifier) { - const langName = languageIdentifier.language; - for (const file of this._files.values()) { - promises.push(file.load() - .then(file => file.select(langName, result)) - .catch(err => this._logService.error(err, file.location.toString())) - ); - } + const result: Snippet[] = []; + const promises: Promise[] = []; + + const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); + if (languageIdentifier) { + const langName = languageIdentifier.language; + for (const file of this._files.values()) { + promises.push(file.load() + .then(file => file.select(langName, result)) + .catch(err => this._logService.error(err, file.location.toString())) + ); } - return Promise.all(promises).then(() => result); - }); + } + await Promise.all(promises); + return this._filterSnippets(result, opts); } - getSnippetsSync(languageId: LanguageId): Snippet[] { + getSnippetsSync(languageId: LanguageId, opts?: ISnippetGetOptions): Snippet[] { const result: Snippet[] = []; const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); if (languageIdentifier) { @@ -191,11 +248,18 @@ class SnippetsService implements ISnippetsService { for (const file of this._files.values()) { // kick off loading (which is a noop in case it's already loaded) // and optimistically collect snippets - file.load().catch(err => { /*ignore*/ }); + file.load().catch(_err => { /*ignore*/ }); file.select(langName, result); } } - return result; + return this._filterSnippets(result, opts); + } + + private _filterSnippets(snippets: Snippet[], opts?: ISnippetGetOptions): Snippet[] { + return snippets.filter(snippet => { + return (snippet.prefix || opts?.includeNoPrefixSnippets) // prefix or no-prefix wanted + && (this.isEnabled(snippet) || opts?.includeDisabledSnippets); // enabled or disabled wanted + }); } // --- loading, watching @@ -203,7 +267,7 @@ class SnippetsService implements ISnippetsService { private _initExtensionSnippets(): void { snippetExt.point.setHandler(extensions => { - for (let [key, value] of this._files) { + for (const [key, value] of this._files) { if (value.source === SnippetSource.Extension) { this._files.delete(key); } @@ -216,8 +280,7 @@ class SnippetsService implements ISnippetsService { continue; } - const resource = validContribution.location.toString(); - const file = this._files.get(resource); + const file = this._files.get(validContribution.location); if (file) { if (file.defaultScopes) { file.defaultScopes.push(validContribution.language); @@ -226,7 +289,7 @@ class SnippetsService implements ISnippetsService { } } else { const file = new SnippetFile(SnippetSource.Extension, validContribution.location, validContribution.language ? [validContribution.language] : undefined, extension.description, this._fileService, this._extensionResourceLoaderService); - this._files.set(file.location.toString(), file); + this._files.set(file.location, file); if (this._environmentService.isExtensionDevelopment) { file.load().then(file => { @@ -267,28 +330,28 @@ class SnippetsService implements ISnippetsService { updateWorkspaceSnippets(); } - private _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: DisposableStore): Promise { - let promises = workspace.folders.map(folder => { + private async _initWorkspaceFolderSnippets(workspace: IWorkspace, bucket: DisposableStore): Promise { + const promises = workspace.folders.map(async folder => { const snippetFolder = folder.toResource('.vscode'); - return this._fileService.exists(snippetFolder).then(value => { - if (value) { - this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); - } else { - // watch - bucket.add(this._fileService.onDidFilesChange(e => { - if (e.contains(snippetFolder, FileChangeType.ADDED)) { - this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); - } - })); - } - }); + const value = await this._fileService.exists(snippetFolder); + if (value) { + this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); + } else { + // watch + bucket.add(this._fileService.onDidFilesChange(e => { + if (e.contains(snippetFolder, FileChangeType.ADDED)) { + this._initFolderSnippets(SnippetSource.Workspace, snippetFolder, bucket); + } + })); + } }); - return Promise.all(promises); + await Promise.all(promises); } - private _initUserSnippets(): Promise { + private async _initUserSnippets(): Promise { const userSnippetsFolder = this._environmentService.snippetsHome; - return this._fileService.createFolder(userSnippetsFolder).then(() => this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables)); + await this._fileService.createFolder(userSnippetsFolder); + return await this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables); } private _initFolderSnippets(source: SnippetSource, folder: URI, bucket: DisposableStore): Promise { @@ -315,15 +378,14 @@ class SnippetsService implements ISnippetsService { private _addSnippetFile(uri: URI, source: SnippetSource): IDisposable { const ext = resources.extname(uri); - const key = uri.toString(); if (source === SnippetSource.User && ext === '.json') { const langName = resources.basename(uri).replace(/\.json/, ''); - this._files.set(key, new SnippetFile(source, uri, [langName], undefined, this._fileService, this._extensionResourceLoaderService)); + this._files.set(uri, new SnippetFile(source, uri, [langName], undefined, this._fileService, this._extensionResourceLoaderService)); } else if (ext === '.code-snippets') { - this._files.set(key, new SnippetFile(source, uri, undefined, undefined, this._fileService, this._extensionResourceLoaderService)); + this._files.set(uri, new SnippetFile(source, uri, undefined, undefined, this._fileService, this._extensionResourceLoaderService)); } return { - dispose: () => this._files.delete(key) + dispose: () => this._files.delete(uri) }; } } diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index cb3b8cbc..10419ba6 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -16,8 +16,7 @@ import { CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes class SimpleSnippetService implements ISnippetsService { declare readonly _serviceBrand: undefined; - constructor(readonly snippets: Snippet[]) { - } + constructor(readonly snippets: Snippet[]) { } getSnippets() { return Promise.resolve(this.getSnippetsSync()); } @@ -27,6 +26,12 @@ class SimpleSnippetService implements ISnippetsService { getSnippetFiles(): any { throw new Error(); } + isEnabled(): boolean { + throw new Error(); + } + updateEnablement(): void { + throw new Error(); + } } suite('SnippetsService', function () { diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index dc2cdaea..76c12fa6 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -102,7 +102,7 @@ class PartsSplash { layoutInfo, baseTheme }), - { encoding: 'utf8', overwriteEncoding: true } + { encoding: 'utf8' } ); if (baseTheme !== this._lastBaseTheme || colorInfo.editorBackground !== this._lastBackground) { diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index 273f4cc8..d7452466 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -9,8 +9,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ISurveyData, IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; @@ -26,7 +25,6 @@ class LanguageSurvey extends Disposable { constructor( data: ISurveyData, storageService: IStorageService, - storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, notificationService: INotificationService, telemetryService: ITelemetryService, modelService: IModelService, @@ -43,14 +41,6 @@ class LanguageSurvey extends Disposable { const EDITED_LANGUAGE_COUNT_KEY = `${data.surveyId}.editedCount`; const EDITED_LANGUAGE_DATE_KEY = `${data.surveyId}.editedDate`; - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: SESSION_COUNT_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: LAST_SESSION_DATE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: SKIP_VERSION_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: IS_CANDIDATE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: EDITED_LANGUAGE_COUNT_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: EDITED_LANGUAGE_DATE_KEY, version: 1 }); - const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); if (skipVersion) { return; @@ -65,8 +55,8 @@ class LanguageSurvey extends Disposable { models.forEach(m => { if (m.getMode() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) { const editedCount = storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; - storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL); - storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL); + storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER); } }); }, 250)); @@ -80,8 +70,8 @@ class LanguageSurvey extends Disposable { } const sessionCount = storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) + 1; - storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); - storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); + storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL, StorageTarget.USER); if (sessionCount < 9) { return; @@ -94,10 +84,10 @@ class LanguageSurvey extends Disposable { const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false) || Math.random() < data.userProbability; - storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL, StorageTarget.USER); if (!isCandidate) { - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); return; } @@ -113,23 +103,23 @@ class LanguageSurvey extends Disposable { telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`); telemetryService.getTelemetryInfo().then(info => { openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`)); - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); }); } }, { label: nls.localize('remindLater', "Remind Me later"), run: () => { telemetryService.publicLog(`${data.surveyId}.survey/remindMeLater`); - storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL); + storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL, StorageTarget.USER); } }, { label: nls.localize('neverAgain', "Don't Show Again"), isSecondary: true, run: () => { telemetryService.publicLog(`${data.surveyId}.survey/dontShowAgain`); - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); } }], { sticky: true } @@ -141,7 +131,6 @@ class LanguageSurveysContribution implements IWorkbenchContribution { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IModelService modelService: IModelService, @@ -155,7 +144,7 @@ class LanguageSurveysContribution implements IWorkbenchContribution { productService.surveys .filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability) - .map(surveyData => new LanguageSurvey(surveyData, storageService, storageKeysSyncRegistryService, notificationService, telemetryService, modelService, textFileService, openerService, productService)); + .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService, productService)); } } diff --git a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index 7f8e32d2..243eb750 100644 --- a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -8,8 +8,7 @@ import { language } from 'vs/base/common/platform'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; @@ -27,7 +26,6 @@ class NPSContribution implements IWorkbenchContribution { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IOpenerService openerService: IOpenerService, @@ -37,12 +35,6 @@ class NPSContribution implements IWorkbenchContribution { return; } - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: SESSION_COUNT_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: LAST_SESSION_DATE_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: SKIP_VERSION_KEY, version: 1 }); - storageKeysSyncRegistryService.registerStorageKey({ key: IS_CANDIDATE_KEY, version: 1 }); - const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); if (skipVersion) { return; @@ -56,8 +48,8 @@ class NPSContribution implements IWorkbenchContribution { } const sessionCount = (storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) || 0) + 1; - storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL); - storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL); + storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL, StorageTarget.USER); if (sessionCount < 9) { return; @@ -66,10 +58,10 @@ class NPSContribution implements IWorkbenchContribution { const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false) || Math.random() < PROBABILITY; - storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL, StorageTarget.USER); if (!isCandidate) { - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); return; } @@ -81,18 +73,18 @@ class NPSContribution implements IWorkbenchContribution { run: () => { telemetryService.getTelemetryInfo().then(info => { openerService.open(URI.parse(`${productService.npsSurveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`)); - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); }); } }, { label: nls.localize('remindLater', "Remind Me later"), - run: () => storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL) + run: () => storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL, StorageTarget.USER) }, { label: nls.localize('neverAgain', "Don't Show Again"), run: () => { - storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); + storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER); } }], { sticky: true } diff --git a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts index 2aecc635..0fbc6faa 100644 --- a/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/browser/workspaceTagsService.ts @@ -16,7 +16,7 @@ export class NoOpWorkspaceTagsService implements IWorkspaceTagsService { return Promise.resolve({}); } - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { + async getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise { return undefined; } diff --git a/src/vs/workbench/contrib/tags/common/workspaceTags.ts b/src/vs/workbench/contrib/tags/common/workspaceTags.ts index d1683b97..487eb6ca 100644 --- a/src/vs/workbench/contrib/tags/common/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/common/workspaceTags.ts @@ -20,7 +20,7 @@ export interface IWorkspaceTagsService { * Returns an id for the workspace, different from the id returned by the context service. A hash based * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. */ - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise; getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise; } diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts index df781b92..726575e8 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTags.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as crypto from 'crypto'; +import { sha1Hex } from 'vs/base/browser/hash'; import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { IFileService, IFileStat } from 'vs/platform/files/common/files'; @@ -20,10 +20,8 @@ import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsSer import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { IProductService } from 'vs/platform/product/common/productService'; -export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: boolean = false): string[] { - return getRemotes(text, stripEndingDotGit).map(r => { - return crypto.createHash('sha1').update(r).digest('hex'); - }); +export async function getHashedRemotesFromConfig(text: string, stripEndingDotGit: boolean = false): Promise { + return Promise.all(getRemotes(text, stripEndingDotGit).map(remote => sha1Hex(remote))); } export class WorkspaceTags implements IWorkbenchContribution { @@ -76,7 +74,7 @@ export class WorkspaceTags implements IWorkbenchContribution { private async getWorkspaceInformation(): Promise { const workspace = this.contextService.getWorkspace(); const state = this.contextService.getWorkbenchState(); - const telemetryId = this.workspaceTagsService.getTelemetryWorkspaceId(workspace, state); + const telemetryId = await this.workspaceTagsService.getTelemetryWorkspaceId(workspace, state); return this.telemetryService.getTelemetryInfo().then(info => { return { id: workspace.id, diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts index 35b943f8..bf203f81 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as crypto from 'crypto'; +import { sha1Hex } from 'vs/base/browser/hash'; import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -14,6 +14,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/tags/electron-browser/workspaceTags'; import { IProductService } from 'vs/platform/product/common/productService'; +import { splitLines } from 'vs/base/common/strings'; const MetaModulesToLookFor = [ // Azure packages @@ -137,9 +138,9 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { return this._tags; } - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { - function createHash(uri: URI): string { - return crypto.createHash('sha1').update(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()).digest('hex'); + async getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise { + function createHash(uri: URI): Promise { + return sha1Hex(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()); } let workspaceId: string | undefined; @@ -148,11 +149,11 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { workspaceId = undefined; break; case WorkbenchState.FOLDER: - workspaceId = createHash(workspace.folders[0].uri); + workspaceId = await createHash(workspace.folders[0].uri); break; case WorkbenchState.WORKSPACE: if (workspace.configuration) { - workspaceId = createHash(workspace.configuration); + workspaceId = await createHash(workspace.configuration); } } @@ -291,13 +292,13 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.playwright" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ - private resolveWorkspaceTags(): Promise { + private async resolveWorkspaceTags(): Promise { const tags: Tags = Object.create(null); const state = this.contextService.getWorkbenchState(); const workspace = this.contextService.getWorkspace(); - tags['workspace.id'] = this.getTelemetryWorkspaceId(workspace, state); + tags['workspace.id'] = await this.getTelemetryWorkspaceId(workspace, state); const { filesToOpenOrCreate, filesToDiff } = this.environmentService.configuration; tags['workbench.filesToOpenOrCreate'] = filesToOpenOrCreate && filesToOpenOrCreate.length || 0; @@ -412,7 +413,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { } const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, this.textFileService, content => { - const dependencies: string[] = content.value.split(/\r\n|\r|\n/); + const dependencies: string[] = splitLines(content.value); for (let dependency of dependencies) { // Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo` const format1 = dependency.split('=='); @@ -423,7 +424,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { }); const pipfilePromises = getFilePromises('pipfile', this.fileService, this.textFileService, content => { - let dependencies: string[] = content.value.split(/\r\n|\r|\n/); + let dependencies: string[] = splitLines(content.value); // We're only interested in the '[packages]' section of the Pipfile dependencies = dependencies.slice(dependencies.indexOf('[packages]') + 1); diff --git a/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts b/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts index ed3c428d..faad40a9 100644 --- a/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts +++ b/src/vs/workbench/contrib/tags/test/electron-browser/workspaceTags.test.ts @@ -13,35 +13,35 @@ function hash(value: string): string { suite('Telemetry - WorkspaceTags', () => { - test('Single remote hashed', function () { - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git')), [hash('github3.com/username/repository.git')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git')), [hash('git.server.org/project.git')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('user@git.server.org:project.git')), [hash('git.server.org/project.git')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('/opt/git/project.git')), []); + test('Single remote hashed', async function () { + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git')), [hash('github3.com/username/repository.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git')), [hash('git.server.org/project.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('user@git.server.org:project.git')), [hash('git.server.org/project.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('/opt/git/project.git')), []); // Strip .git - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), [hash('github3.com/username/repository')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), [hash('git.server.org/project')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), []); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), [hash('github3.com/username/repository')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), [hash('git.server.org/project')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), []); // Compare Striped .git with no .git - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository'))); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project'))); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); - assert.deepStrictEqual(getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), getHashedRemotesFromConfig(remote('/opt/git/project'))); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository.git'), true), await getHashedRemotesFromConfig(remote('https://username:password@github3.com/username/repository'))); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project.git'), true), await getHashedRemotesFromConfig(remote('ssh://user@git.server.org/project'))); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('user@git.server.org:project.git'), true), [hash('git.server.org/project')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(remote('/opt/git/project.git'), true), await getHashedRemotesFromConfig(remote('/opt/git/project'))); }); - test('Multiple remotes hashed', function () { + test('Multiple remotes hashed', async function () { const config = ['https://github.com/microsoft/vscode.git', 'https://git.example.com/gitproject.git'].map(remote).join(' '); - assert.deepStrictEqual(getHashedRemotesFromConfig(config), [hash('github.com/microsoft/vscode.git'), hash('git.example.com/gitproject.git')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(config), [hash('github.com/microsoft/vscode.git'), hash('git.example.com/gitproject.git')]); // Strip .git - assert.deepStrictEqual(getHashedRemotesFromConfig(config, true), [hash('github.com/microsoft/vscode'), hash('git.example.com/gitproject')]); + assert.deepStrictEqual(await getHashedRemotesFromConfig(config, true), [hash('github.com/microsoft/vscode'), hash('git.example.com/gitproject')]); // Compare Striped .git with no .git const noDotGitConfig = ['https://github.com/microsoft/vscode', 'https://git.example.com/gitproject'].map(remote).join(' '); - assert.deepStrictEqual(getHashedRemotesFromConfig(config, true), getHashedRemotesFromConfig(noDotGitConfig)); + assert.deepStrictEqual(await getHashedRemotesFromConfig(config, true), await getHashedRemotesFromConfig(noDotGitConfig)); }); function remote(url: string): string { diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 54b59c57..64f74982 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -19,8 +19,6 @@ import { ValidationStatus, ValidationState } from 'vs/base/common/parsers'; import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import { LRUCache, Touch } from 'vs/base/common/map'; - -import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -28,13 +26,13 @@ import { IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IProgressService, IProgressOptions, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IDialogService, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -42,7 +40,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; @@ -78,9 +76,10 @@ import { ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; -import { isWorkspaceFolder, TaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG } from 'vs/workbench/contrib/tasks/browser/taskQuickPick'; +import { isWorkspaceFolder, TaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG, configureTaskIcon } from 'vs/workbench/contrib/tasks/browser/taskQuickPick'; import { ILogService } from 'vs/platform/log/common/log'; import { once } from 'vs/base/common/functional'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt'; @@ -238,7 +237,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IWorkspaceContextService protected readonly contextService: IWorkspaceContextService, @ITelemetryService protected readonly telemetryService: ITelemetryService, @ITextFileService private readonly textFileService: ITextFileService, - @ILifecycleService lifecycleService: ILifecycleService, @IModelService protected readonly modelService: IModelService, @IExtensionService private readonly extensionService: IExtensionService, @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -248,7 +246,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IProgressService private readonly progressService: IProgressService, @IOpenerService private readonly openerService: IOpenerService, @IHostService private readonly _hostService: IHostService, - @IDialogService private readonly dialogService: IDialogService, + @IDialogService protected readonly dialogService: IDialogService, @INotificationService private readonly notificationService: INotificationService, @IContextKeyService protected readonly contextKeyService: IContextKeyService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @@ -308,7 +306,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.updateWorkspaceTasks(TaskRunSource.ConfigurationChange); })); this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService); - this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown()))); this._onDidStateChange = this._register(new Emitter()); this.registerCommands(); this.configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise => { @@ -518,7 +515,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private disposeTaskSystemListeners(): void { + protected disposeTaskSystemListeners(): void { if (this._taskSystemListener) { this._taskSystemListener.dispose(); } @@ -580,7 +577,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!values) { return undefined; } - return values.find(task => task.matches(key, compareId)); + values = values.filter(task => task.matches(key, compareId)).sort(task => task._source.kind === TaskSourceKind.Extension ? 1 : -1); + return values.length > 0 ? values[0] : undefined; }); } @@ -801,6 +799,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return tasks; } + public removeRecentlyUsedTask(taskRecentlyUsedKey: string) { + if (this.getRecentlyUsedTasks().has(taskRecentlyUsedKey)) { + this.getRecentlyUsedTasks().delete(taskRecentlyUsedKey); + this.saveRecentlyUsedTasks(); + } + } + private setTaskLRUCacheLimit() { const quickOpenHistoryLimit = this.configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); if (this._recentlyUsedTasks) { @@ -845,7 +850,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (const key of keys) { keyValues.push([key, this._recentlyUsedTasks.get(key, Touch.None)!]); } - this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE); + this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE, StorageTarget.USER); } private openDocumentation(): void { @@ -981,7 +986,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer newValue = Object.create(null); } newValue[type] = true; - return this.configurationService.updateValue(PROBLEM_MATCHER_NEVER_CONFIG, newValue, ConfigurationTarget.USER); + return this.configurationService.updateValue(PROBLEM_MATCHER_NEVER_CONFIG, newValue); } private attachProblemMatcher(task: ContributedTask | CustomTask): Promise { @@ -1593,7 +1598,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.modelService, this.configurationResolverService, this.telemetryService, this.contextService, this.environmentService, AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService, - this.pathService, this.viewDescriptorService, this.logService, + this.pathService, this.viewDescriptorService, this.logService, this.configurationService, (workspaceFolder: IWorkspaceFolder | undefined) => { if (workspaceFolder) { return this.getTaskSystemInfo(workspaceFolder.uri.scheme); @@ -1699,7 +1704,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } - if (!contributed) { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { result.add(key, ...folderTasks.set.tasks); } else { let configurations = folderTasks.configurations; @@ -1864,31 +1869,35 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected abstract updateWorkspaceTasks(runSource: TaskRunSource | void): void; protected computeWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { - if (this.workspaceFolders.length === 0) { - return Promise.resolve(new Map()); - } else { - let promises: Promise[] = []; - for (let folder of this.workspaceFolders) { - promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); + let promises: Promise[] = []; + for (let folder of this.workspaceFolders) { + promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); + } + return Promise.all(promises).then(async (values) => { + let result = new Map(); + for (let value of values) { + if (value) { + result.set(value.workspaceFolder.uri.toString(), value); + } } - return Promise.all(promises).then(async (values) => { - let result = new Map(); - for (let value of values) { - if (value) { - result.set(value.workspaceFolder.uri.toString(), value); - } - } - const userTasks = await this.computeUserTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); - if (userTasks) { - result.set(USER_TASKS_GROUP_KEY, userTasks); - } - const workspaceFileTasks = await this.computeWorkspaceFileTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); + let folder = this.workspaceFolders.length > 0 ? this.workspaceFolders[0] : undefined; + if (!folder) { + const userhome = await this.pathService.userHome(); + folder = new WorkspaceFolder({ uri: userhome, name: resources.basename(userhome), index: 0 }); + } + const userTasks = await this.computeUserTasks(folder, runSource).then((value) => value, () => undefined); + if (userTasks) { + result.set(USER_TASKS_GROUP_KEY, userTasks); + } + + if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + const workspaceFileTasks = await this.computeWorkspaceFileTasks(folder, runSource).then((value) => value, () => undefined); if (workspaceFileTasks && this._workspace && this._workspace.configuration) { result.set(this._workspace.configuration.toString(), workspaceFileTasks); } - return result; - }); - } + } + return result; + }); } private get jsonTasksSupported(): boolean { @@ -2129,64 +2138,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }; } - public beforeShutdown(): boolean | Promise { - if (!this._taskSystem) { - return false; - } - if (!this._taskSystem.isActiveSync()) { - return false; - } - // The terminal service kills all terminal on shutdown. So there - // is nothing we can do to prevent this here. - if (this._taskSystem instanceof TerminalTaskSystem) { - return false; - } - - let terminatePromise: Promise; - if (this._taskSystem.canAutoTerminate()) { - terminatePromise = Promise.resolve({ confirmed: true }); - } else { - terminatePromise = this.dialogService.confirm({ - message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'), - primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"), - type: 'question' - }); - } - - return terminatePromise.then(res => { - if (res.confirmed) { - return this._taskSystem!.terminateAll().then((responses) => { - let success = true; - let code: number | undefined = undefined; - for (let response of responses) { - success = success && response.success; - // We only have a code in the old output runner which only has one task - // So we can use the first code. - if (code === undefined && response.code !== undefined) { - code = response.code; - } - } - if (success) { - this._taskSystem = undefined; - this.disposeTaskSystemListeners(); - return false; // no veto - } else if (code && code === TerminateResponseCode.ProcessNotFound) { - return this.dialogService.confirm({ - message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'), - primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"), - type: 'info' - }).then(res => !res.confirmed); - } - return true; // veto - }, (err) => { - return true; // veto - }); - } - - return true; // veto - }); - } - private handleError(err: any): void { let showOutput = true; if (err instanceof TaskError) { @@ -2222,17 +2173,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private canRunCommand(): boolean { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.notificationService.prompt( - Severity.Info, - nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."), - [{ - label: nls.localize('TaskService.learnMore', "Learn More"), - run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks')) - }] - ); - return false; - } return true; } @@ -2262,7 +2202,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } for (let task of tasks) { let entry: TaskQuickPickEntry = TaskQuickPickEntry(task); - entry.buttons = [{ iconClass: 'codicon-gear', tooltip: nls.localize('configureTask', "Configure Task") }]; + entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }]; if (selectedEntry && (task === selectedEntry.task)) { entries.unshift(selectedEntry); } else { @@ -2446,7 +2386,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer label: nls.localize('TaskService.notAgain', "Don't Show Again"), isSecondary: true, run: () => { - this.storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE); + this.storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE, StorageTarget.USER); this._showIgnoreMessage = false; } }] @@ -2541,7 +2481,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } this.showQuickPick(tasks ? tasks : taskResult!.tasks, placeholder, { - label: nls.localize('TaskService.noEntryToRunSlow', 'No task to run found. Configure Tasks...'), + label: nls.localize('TaskService.noEntryToRunSlow', '$(plus) Configure a Task'), task: null }, true). @@ -2551,7 +2491,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } else { this.showTwoLevelQuickPick(placeholder, { - label: nls.localize('TaskService.noEntryToRun', 'No configured tasks. Configure Tasks...'), + label: nls.localize('TaskService.noEntryToRun', '$(plus) Configure a Task'), task: null }). then(pickThen); @@ -2926,8 +2866,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } if (this.isTaskEntry(selection)) { this.configureTask(selection.task); - } else { + } else if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace); + } else { + const resource = this.getResourceForKind(TaskSourceKind.User); + if (resource) { + this.openTaskFile(resource, TaskSourceKind.User); + } } } @@ -2969,46 +2914,22 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return taskPromise.then((taskMap) => { let entries: QuickPickInput[] = []; let needsCreateOrOpen: boolean = true; - if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - let tasks = taskMap.all(); - if (tasks.length > 0) { - tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); - for (let task of tasks) { - entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined }); - if (!ContributedTask.is(task)) { - needsCreateOrOpen = false; - } + let tasks = taskMap.all(); + if (tasks.length > 0) { + tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); + for (let task of tasks) { + entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined }); + if (!ContributedTask.is(task)) { + needsCreateOrOpen = false; } } - if (needsCreateOrOpen) { - let label = stats[0] !== undefined ? openLabel : createLabel; - if (entries.length) { - entries.push({ type: 'separator' }); - } - entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); - } - } else { - let folders = this.contextService.getWorkspace().folders; - let index = 0; - for (let folder of folders) { - let tasks = taskMap.get(folder); - if (tasks.length > 0) { - tasks = tasks.slice().sort((a, b) => a._label.localeCompare(b._label)); - for (let i = 0; i < tasks.length; i++) { - let entry: TaskQuickPickEntryType = { label: tasks[i]._label, task: tasks[i], description: this.getTaskDescription(tasks[i]) }; - if (i === 0) { - entries.push({ type: 'separator', label: folder.name }); - } - entries.push(entry); - } - } else { - let label = stats[index] !== undefined ? openLabel : createLabel; - let entry: TaskQuickPickEntryType = { label, folder: folder }; - entries.push({ type: 'separator', label: folder.name }); - entries.push(entry); - } - index++; + } + if (needsCreateOrOpen) { + let label = stats[0] !== undefined ? openLabel : createLabel; + if (entries.length) { + entries.push({ type: 'separator' }); } + entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); } if ((entries.length === 1) && !needsCreateOrOpen) { tokenSource.cancel(); diff --git a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts index 4b20d3ac..40899eca 100644 --- a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts +++ b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts @@ -9,7 +9,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITaskService, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; import { forEach } from 'vs/base/common/collections'; import { RunOnOptions, Task, TaskRunSource, TASKS_CATEGORY } from 'vs/workbench/contrib/tasks/common/tasks'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Action2 } from 'vs/platform/actions/common/actions'; @@ -110,14 +110,14 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut label: nls.localize('allow', "Allow and run"), run: () => { resolve(true); - storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, true, StorageScope.WORKSPACE); + storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, true, StorageScope.WORKSPACE, StorageTarget.MACHINE); } }, { label: nls.localize('disallow', "Disallow"), run: () => { resolve(false); - storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, false, StorageScope.WORKSPACE); + storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, false, StorageScope.WORKSPACE, StorageTarget.MACHINE); } }, { @@ -156,6 +156,6 @@ export class ManageAutomaticTaskRunning extends Action2 { return; } - storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, value === allowItem, StorageScope.WORKSPACE); + storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, value === allowItem, StorageScope.WORKSPACE, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts index cd19578c..a423d766 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts @@ -9,12 +9,15 @@ import { Task, ContributedTask, CustomTask, ConfiguringTask, TaskSorter, KeyedTa import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Types from 'vs/base/common/types'; import { ITaskService, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; -import { IQuickPickItem, QuickPickInput, IQuickPick } from 'vs/base/parts/quickinput/common/quickInput'; +import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton } from 'vs/base/parts/quickinput/common/quickInput'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Codicon } from 'vs/base/common/codicons'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; export const QUICKOPEN_SKIP_CONFIG = 'task.quickOpen.skip'; @@ -32,6 +35,9 @@ export interface TaskTwoLevelQuickPickEntry extends IQuickPickItem { const SHOW_ALL: string = nls.localize('taskQuickPick.showAll', "Show All Tasks..."); +export const configureTaskIcon = registerIcon('tasks-list-configure', Codicon.gear, nls.localize('configureTaskIcon', 'Configration icon in the tasks selection list.')); +const removeTaskIcon = registerIcon('tasks-remove', Codicon.close, nls.localize('removeTaskIcon', 'Icon for remove in the tasks selection list.')); + export class TaskQuickPick extends Disposable { private sorter: TaskSorter; private topLevelEntries: QuickPickInput[] | undefined; @@ -63,16 +69,17 @@ export class TaskQuickPick extends Disposable { return ''; } - private createTaskEntry(task: Task | ConfiguringTask): TaskTwoLevelQuickPickEntry { + private createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): TaskTwoLevelQuickPickEntry { const entry: TaskTwoLevelQuickPickEntry = { label: this.guessTaskLabel(task), description: this.taskService.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; - entry.buttons = [{ iconClass: 'codicon-gear', tooltip: nls.localize('configureTask', "Configure Task") }]; + entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }, ...extraButtons]; return entry; } - private createEntriesForGroup(entries: QuickPickInput[], tasks: (Task | ConfiguringTask)[], groupLabel: string) { + private createEntriesForGroup(entries: QuickPickInput[], tasks: (Task | ConfiguringTask)[], + groupLabel: string, extraButtons: IQuickInputButton[] = []) { entries.push({ type: 'separator', label: groupLabel }); tasks.forEach(task => { - entries.push(this.createTaskEntry(task)); + entries.push(this.createTaskEntry(task, extraButtons)); }); } @@ -143,7 +150,11 @@ export class TaskQuickPick extends Disposable { let dedupedConfiguredTasks: (Task | ConfiguringTask)[] = dedupeAndPrune.configuredTasks; recentTasks = dedupeAndPrune.recentTasks; if (recentTasks.length > 0) { - this.createEntriesForGroup(this.topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used')); + const removeRecentButton: IQuickInputButton = { + iconClass: ThemeIcon.asClassName(removeTaskIcon), + tooltip: nls.localize('removeRecent', 'Remove Recently Used Task') + }; + this.createEntriesForGroup(this.topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used'), [removeRecentButton]); } if (configuredTasks.length > 0) { if (dedupedConfiguredTasks.length > 0) { @@ -171,6 +182,18 @@ export class TaskQuickPick extends Disposable { picker.onDidTriggerItemButton(async (context) => { let task = context.item.task; + if (task && !Types.isString(task) && context.button.iconClass === ThemeIcon.asClassName(removeTaskIcon)) { + const key = task.getRecentlyUsedKey(); + if (key) { + this.taskService.removeRecentlyUsedTask(key); + const indexToRemove = picker.items.indexOf(context.item); + if (indexToRemove >= 0) { + picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)]; + } + } + return; + } + this.quickInputService.cancel(); if (ContributedTask.is(task)) { this.taskService.customize(task, undefined, true); @@ -226,10 +249,10 @@ export class TaskQuickPick extends Disposable { private async doPickerSecondLevel(picker: IQuickPick, type: string) { picker.busy = true; - picker.value = ''; if (type === SHOW_ALL) { picker.items = (await this.taskService.tasks()).sort((a, b) => this.sorter.compare(a, b)).map(task => this.createTaskEntry(task)); } else { + picker.value = ''; picker.items = await this.getEntriesForProvider(type); } picker.busy = false; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index f9622690..b011e99c 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -47,6 +47,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { env as processEnv, cwd as processCwd } from 'vs/base/common/process'; import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { ILogService } from 'vs/platform/log/common/log'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; interface TerminalData { terminal: ITerminalInstance; @@ -204,6 +205,7 @@ export class TerminalTaskSystem implements ITaskSystem { private pathService: IPathService, private viewDescriptorService: IViewDescriptorService, private logService: ILogService, + private configurationService: IConfigurationService, taskSystemInfoResolver: TaskSystemInfoResolver, ) { @@ -247,7 +249,7 @@ export class TerminalTaskSystem implements ITaskSystem { } try { - const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger) }; + const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger, new Set()) }; executeResult.promise.then(summary => { this.lastTask = this.currentTask; }); @@ -435,7 +437,21 @@ export class TerminalTaskSystem implements ITaskSystem { return Promise.all(promises); } - private async executeTask(task: Task, resolver: ITaskResolver, trigger: string, alreadyResolved?: Map): Promise { + + private showDependencyCycleMessage(task: Task) { + this.log(nls.localize('dependencyCycle', + 'There is a dependency cycle. See task "{0}".', + task._label + )); + this.showOutput(); + } + + private async executeTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { + if (encounteredDependencies.has(task.getCommonTaskId())) { + this.showDependencyCycleMessage(task); + return {}; + } + alreadyResolved = alreadyResolved ?? new Map(); let promises: Promise[] = []; if (task.configurationProperties.dependsOn) { @@ -446,7 +462,8 @@ export class TerminalTaskSystem implements ITaskSystem { let promise = this.activeTasks[key] ? this.activeTasks[key].promise : undefined; if (!promise) { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); - promise = this.executeDependencyTask(dependencyTask, resolver, trigger, alreadyResolved); + encounteredDependencies.add(task.getCommonTaskId()); + promise = this.executeDependencyTask(dependencyTask, resolver, trigger, encounteredDependencies, alreadyResolved); } promises.push(promise); if (task.configurationProperties.dependsOrder === DependsOrder.sequence) { @@ -472,6 +489,7 @@ export class TerminalTaskSystem implements ITaskSystem { if ((ContributedTask.is(task) || CustomTask.is(task)) && (task.command)) { return Promise.all(promises).then((summaries): Promise | ITaskSummary => { + encounteredDependencies.delete(task.getCommonTaskId()); for (let summary of summaries) { if (summary.exitCode !== 0) { this.removeInstances(task); @@ -486,6 +504,7 @@ export class TerminalTaskSystem implements ITaskSystem { }); } else { return Promise.all(promises).then((summaries): ITaskSummary => { + encounteredDependencies.delete(task.getCommonTaskId()); for (let summary of summaries) { if (summary.exitCode !== 0) { return { exitCode: summary.exitCode }; @@ -496,11 +515,11 @@ export class TerminalTaskSystem implements ITaskSystem { } } - private async executeDependencyTask(task: Task, resolver: ITaskResolver, trigger: string, alreadyResolved?: Map): Promise { + private async executeDependencyTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { // If the task is a background task with a watching problem matcher, we don't wait for the whole task to finish, // just for the problem matcher to go inactive. if (!task.configurationProperties.isBackground) { - return this.executeTask(task, resolver, trigger, alreadyResolved); + return this.executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved); } const inactivePromise = new Promise(resolve => { @@ -511,7 +530,7 @@ export class TerminalTaskSystem implements ITaskSystem { } }); }); - return Promise.race([inactivePromise, this.executeTask(task, resolver, trigger, alreadyResolved)]); + return Promise.race([inactivePromise, this.executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved)]); } private async resolveAndFindExecutable(systemInfo: TaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, cwd: string | undefined, envPath: string | undefined): Promise { @@ -856,7 +875,6 @@ export class TerminalTaskSystem implements ITaskSystem { }); promise = new Promise((resolve, reject) => { const onExit = terminal!.onExit((exitCode) => { - onData.dispose(); onExit.dispose(); let key = task.getMapKey(); this.removeFromActiveTasks(task); @@ -887,8 +905,12 @@ export class TerminalTaskSystem implements ITaskSystem { // There is nothing else to do here. } } - startStopProblemMatcher.done(); - startStopProblemMatcher.dispose(); + // Hack to work around #92868 until terminal is fixed. + setTimeout(() => { + onData.dispose(); + startStopProblemMatcher.done(); + startStopProblemMatcher.dispose(); + }, 100); if (!processStartedSignaled && terminal) { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId!)); processStartedSignaled = true; @@ -1019,9 +1041,17 @@ export class TerminalTaskSystem implements ITaskSystem { if (!shellSpecified) { // Under Mac remove -l to not start it as a login shell. if (platform === Platform.Platform.Mac) { - let index = shellArgs.indexOf('-l'); - if (index !== -1) { - shellArgs.splice(index, 1); + // Background on -l on osx https://github.com/microsoft/vscode/issues/107563 + const osxShellArgs = this.configurationService.inspect('terminal.integrated.shellArgs.osx'); + if ((osxShellArgs.user === undefined) && (osxShellArgs.userLocal === undefined) && (osxShellArgs.userLocalValue === undefined) + && (osxShellArgs.userRemote === undefined) && (osxShellArgs.userRemoteValue === undefined) + && (osxShellArgs.userValue === undefined) && (osxShellArgs.workspace === undefined) + && (osxShellArgs.workspaceFolder === undefined) && (osxShellArgs.workspaceFolderValue === undefined) + && (osxShellArgs.workspaceValue === undefined)) { + let index = shellArgs.indexOf('-l'); + if (index !== -1) { + shellArgs.splice(index, 1); + } } } toAdd.push('-c'); diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 46574e02..5e6c6f74 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -14,6 +14,7 @@ import { ILineMatcher, createLineMatcher, ProblemMatcher, ProblemMatch, ApplyToK import { IMarkerService, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { generateUuid } from 'vs/base/common/uuid'; import { IFileService } from 'vs/platform/files/common/files'; +import { isWindows } from 'vs/base/common/platform'; export const enum ProblemCollectorEventKind { BackgroundProcessingBegins = 'backgroundProcessingBegins', @@ -282,7 +283,7 @@ export abstract class AbstractProblemCollector implements IDisposable { let existingMarker; if (!markersPerResource.has(key)) { markersPerResource.set(key, marker); - } else if (((existingMarker = markersPerResource.get(key)) !== undefined) && existingMarker.message.length < marker.message.length) { + } else if (((existingMarker = markersPerResource.get(key)) !== undefined) && (existingMarker.message.length < marker.message.length) && isWindows) { // Most likely https://github.com/microsoft/vscode/issues/77475 // Heuristic dictates that when the key is the same and message is smaller, we have hit this limitation. markersPerResource.set(key, marker); diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 19e45d10..8b38d041 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -77,6 +77,7 @@ export interface ITaskService { taskTypes(): string[]; getWorkspaceTasks(runSource?: TaskRunSource): Promise>; readRecentTasks(): Promise<(Task | ConfiguringTask)[]>; + removeRecentlyUsedTask(taskRecentlyUsedKey: string): void; /** * @param alias The task's name, label or defined identifier. */ diff --git a/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts b/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts index 70145f1b..64fe0389 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/taskService.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as Objects from 'vs/base/common/objects'; import * as semver from 'vs/base/common/semver/semver'; import { IStringDictionary } from 'vs/base/common/collections'; -import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { WorkbenchState, IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ITaskSystem } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { ExecutionEngine, TaskRunSource } from 'vs/workbench/contrib/tasks/common/tasks'; import * as TaskConfig from '../common/taskConfiguration'; @@ -15,6 +16,36 @@ import { ProcessRunnerDetector } from 'vs/workbench/contrib/tasks/node/processRu import { AbstractTaskService } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; import { TaskFilter, ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { TerminalTaskSystem } from 'vs/workbench/contrib/tasks/browser/terminalTaskSystem'; +import { IConfirmationResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { TerminateResponseCode } from 'vs/base/common/processes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { ITerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; interface WorkspaceFolderConfigurationResult { workspaceFolder: IWorkspaceFolder; @@ -24,6 +55,69 @@ interface WorkspaceFolderConfigurationResult { export class TaskService extends AbstractTaskService { private _configHasErrors: boolean = false; + constructor(@IConfigurationService configurationService: IConfigurationService, + @IMarkerService markerService: IMarkerService, + @IOutputService outputService: IOutputService, + @IPanelService panelService: IPanelService, + @IViewsService viewsService: IViewsService, + @ICommandService commandService: ICommandService, + @IEditorService editorService: IEditorService, + @IFileService fileService: IFileService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @ITelemetryService telemetryService: ITelemetryService, + @ITextFileService textFileService: ITextFileService, + @ILifecycleService lifecycleService: ILifecycleService, + @IModelService modelService: IModelService, + @IExtensionService extensionService: IExtensionService, + @IQuickInputService quickInputService: IQuickInputService, + @IConfigurationResolverService configurationResolverService: IConfigurationResolverService, + @ITerminalService terminalService: ITerminalService, + @IStorageService storageService: IStorageService, + @IProgressService progressService: IProgressService, + @IOpenerService openerService: IOpenerService, + @IHostService _hostService: IHostService, + @IDialogService dialogService: IDialogService, + @INotificationService notificationService: INotificationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @ITerminalInstanceService terminalInstanceService: ITerminalInstanceService, + @IPathService pathService: IPathService, + @ITextModelService textModelResolverService: ITextModelService, + @IPreferencesService preferencesService: IPreferencesService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService) { + super(configurationService, + markerService, + outputService, + panelService, + viewsService, + commandService, + editorService, + fileService, + contextService, + telemetryService, + textFileService, + modelService, + extensionService, + quickInputService, + configurationResolverService, + terminalService, + storageService, + progressService, + openerService, + _hostService, + dialogService, + notificationService, + contextKeyService, + environmentService, + terminalInstanceService, + pathService, + textModelResolverService, + preferencesService, + viewDescriptorService, + logService); + this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown()))); + } protected getTaskSystem(): ITaskSystem { if (this._taskSystem) { @@ -133,6 +227,64 @@ export class TaskService extends AbstractTaskService { } return result; } + + public beforeShutdown(): boolean | Promise { + if (!this._taskSystem) { + return false; + } + if (!this._taskSystem.isActiveSync()) { + return false; + } + // The terminal service kills all terminal on shutdown. So there + // is nothing we can do to prevent this here. + if (this._taskSystem instanceof TerminalTaskSystem) { + return false; + } + + let terminatePromise: Promise; + if (this._taskSystem.canAutoTerminate()) { + terminatePromise = Promise.resolve({ confirmed: true }); + } else { + terminatePromise = this.dialogService.confirm({ + message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'), + primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"), + type: 'question' + }); + } + + return terminatePromise.then(res => { + if (res.confirmed) { + return this._taskSystem!.terminateAll().then((responses) => { + let success = true; + let code: number | undefined = undefined; + for (let response of responses) { + success = success && response.success; + // We only have a code in the old output runner which only has one task + // So we can use the first code. + if (code === undefined && response.code !== undefined) { + code = response.code; + } + } + if (success) { + this._taskSystem = undefined; + this.disposeTaskSystemListeners(); + return false; // no veto + } else if (code && code === TerminateResponseCode.ProcessNotFound) { + return this.dialogService.confirm({ + message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'), + primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"), + type: 'info' + }).then(res => !res.confirmed); + } + return true; // veto + }, (err) => { + return true; // veto + }); + } + + return true; // veto + }); + } } registerSingleton(ITaskService, TaskService, true); diff --git a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts index 77841094..9e1ecd64 100644 --- a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts @@ -10,12 +10,13 @@ import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import { ValidationStatus } from 'vs/base/common/parsers'; import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'vs/workbench/contrib/tasks/common/problemMatcher'; -import { WorkspaceFolder, Workspace, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { WorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks'; import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask, TaskConfigSource } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContext } from 'vs/platform/contextkey/common/contextkey'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; const workspaceFolder: WorkspaceFolder = new WorkspaceFolder({ uri: URI.file('/workspace/folderOne'), diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 004f9217..511c8296 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -31,7 +31,7 @@ type TelemetryData = { ext: string; path: number; reason?: number; - whitelistedjson?: string; + allowlistedjson?: string; }; type FileTelemetryDataFragment = { @@ -39,13 +39,13 @@ type FileTelemetryDataFragment = { ext: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; path: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; reason?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - whitelistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + allowlistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; }; export class TelemetryContribution extends Disposable implements IWorkbenchContribution { - private static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; - private static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; + private static ALLOWLIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; + private static ALLOWLIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; constructor( @ITelemetryService private readonly telemetryService: ITelemetryService, @@ -184,7 +184,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr for (const folder of folders) { if (isEqualOrParent(resource, folder.toResource('.vscode'))) { const filename = basename(resource); - if (TelemetryContribution.WHITELIST_WORKSPACE_JSON.indexOf(filename) > -1) { + if (TelemetryContribution.ALLOWLIST_WORKSPACE_JSON.indexOf(filename) > -1) { return `.vscode/${filename}`; } } @@ -202,11 +202,11 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr ext, path: hash(path), reason, - whitelistedjson: undefined as string | undefined + allowlistedjson: undefined as string | undefined }; - if (ext === '.json' && TelemetryContribution.WHITELIST_JSON.indexOf(fileName) > -1) { - telemetryData['whitelistedjson'] = fileName; + if (ext === '.json' && TelemetryContribution.ALLOWLIST_JSON.indexOf(fileName) > -1) { + telemetryData['allowlistedjson'] = fileName; } return telemetryData; diff --git a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts index 09750a14..d9150166 100644 --- a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts +++ b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts @@ -7,6 +7,8 @@ import { IEnvironmentVariableInfo, IMergedEnvironmentVariableCollection, IMerged import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { Codicon } from 'vs/base/common/codicons'; export class EnvironmentVariableInfoStale implements IEnvironmentVariableInfo { readonly requiresAction = true; @@ -53,8 +55,8 @@ export class EnvironmentVariableInfoStale implements IEnvironmentVariableInfo { return info; } - getIcon(): string { - return 'warning'; + getIcon(): ThemeIcon { + return Codicon.warning; } getActions(): { label: string, iconClass?: string, run: () => void, commandId: string }[] { @@ -83,8 +85,8 @@ export class EnvironmentVariableInfoChangesActive implements IEnvironmentVariabl return message + '\n\n```\n' + changes.join('\n') + '\n```'; } - getIcon(): string { - return 'info'; + getIcon(): ThemeIcon { + return Codicon.info; } } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts index 5c0e8243..fb420ef5 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts @@ -49,6 +49,8 @@ export const unixLineAndColumnMatchIndex = 11; // Each line and column clause have 6 groups (ie no. of expressions in round brackets) export const lineAndColumnClauseGroupCount = 6; +const MAX_LENGTH = 2000; + export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider { constructor( private readonly _xterm: Terminal, @@ -85,6 +87,9 @@ export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider } const text = getXtermLineContent(this._xterm.buffer.active, startLine, endLine, this._xterm.cols); + if (text.length > MAX_LENGTH) { + return []; + } // clone regex to do a global search on text const rex = new RegExp(this._localLinkRegex, 'g'); diff --git a/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css index da4ecfc4..5732fb11 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css +++ b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css @@ -11,6 +11,10 @@ transition: background-color 800ms linear; } +.monaco-workbench .pane-body.integrated-terminal .xterm-viewport { + scrollbar-width: thin; +} + .monaco-workbench .pane-body.integrated-terminal .xterm-viewport::-webkit-scrollbar { width: 10px; } diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 8c394244..43de91e6 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -196,7 +196,3 @@ padding: 0 22px 0 6px; } -/* HACK: Can remove when fixed upstream https://github.com/xtermjs/xterm.js/issues/3058 */ -.xterm-helper-textarea { - border: 0px; -} diff --git a/src/vs/workbench/contrib/terminal/browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css index 712baa28..1ac20b3d 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/xterm.css +++ b/src/vs/workbench/contrib/terminal/browser/media/xterm.css @@ -62,10 +62,10 @@ } .xterm .xterm-helper-textarea { - /* - * HACK: to fix IE's blinking cursor - * Move textarea out of the screen to the far left, so that the cursor is not visible. - */ + padding: 0; + border: 0; + margin: 0; + /* Move textarea out of the screen to the far left, so that the cursor is not visible */ position: absolute; opacity: 0; left: -9999em; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 34e4327a..61d5525a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -36,6 +36,7 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; import { terminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { terminalViewIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; // Register services registerSingleton(ITerminalService, TerminalService, true); @@ -63,7 +64,7 @@ configurationRegistry.registerConfiguration(terminalConfiguration); const VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: TERMINAL_VIEW_ID, name: nls.localize('terminal', "Terminal"), - icon: 'codicon-terminal', + icon: terminalViewIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [TERMINAL_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: TERMINAL_VIEW_ID, focusCommand: { id: TERMINAL_COMMAND_ID.FOCUS }, @@ -74,7 +75,7 @@ Registry.as(panel.Extensions.Panels).setDefaultPanelId(TERM Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: TERMINAL_VIEW_ID, name: nls.localize('terminal', "Terminal"), - containerIcon: 'codicon-terminal', + containerIcon: terminalViewIcon, canToggleVisibility: false, canMoveView: true, ctorDescriptor: new SyncDescriptor(TerminalViewPane) @@ -150,16 +151,17 @@ if (BrowserFeatures.clipboard.readText) { win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V] }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED); - // An extra Windows-only ctrl+v keybinding is used for pwsh that sends ctrl+v directly to the - // shell, this gets handled by PSReadLine which properly handles multi-line pastes. This is - // disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen - // reader. - if (platform.isWindows) { - registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - CTRL_LETTER_OFFSET), { // ctrl+v - when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.PowerShell), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), - primary: KeyMod.CtrlCmd | KeyCode.KEY_V - }); - } +} + +// An extra Windows-only ctrl+v keybinding is used for pwsh that sends ctrl+v directly to the +// shell, this gets handled by PSReadLine which properly handles multi-line pastes. This is +// disabled in accessibility mode as PowerShell does not run PSReadLine when it detects a screen +// reader. This works even when clipboard.readText is not supported. +if (platform.isWindows) { + registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - CTRL_LETTER_OFFSET), { // ctrl+v + when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.PowerShell), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), + primary: KeyMod.CtrlCmd | KeyCode.KEY_V + }); } // Delete word left: ctrl+w diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index bb382e0f..1f58d65e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -59,7 +59,6 @@ export interface ITerminalTab { onDisposed: Event; onInstancesChanged: Event; - setWillFocus(focus: boolean): void; focusPreviousPane(): void; focusNextPane(): void; resizePane(direction: Direction): void; @@ -71,6 +70,11 @@ export interface ITerminalTab { split(shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; } +export const enum TerminalConnectionState { + Connecting, + Connected +} + export interface ITerminalService { readonly _serviceBrand: undefined; @@ -79,6 +83,7 @@ export interface ITerminalService { terminalInstances: ITerminalInstance[]; terminalTabs: ITerminalTab[]; isProcessSupportRegistered: boolean; + readonly connectionState: TerminalConnectionState; initializeTerminals(): Promise; onActiveTabChanged: Event; @@ -95,6 +100,7 @@ export interface ITerminalService { onActiveInstanceChanged: Event; onRequestAvailableShells: Event; onDidRegisterProcessSupport: Event; + onDidChangeConnectionState: Event; /** * Creates a terminal. @@ -416,6 +422,11 @@ export interface ITerminalInstance { */ notifyFindWidgetFocusChanged(isFocused: boolean): void; + /** + * Notifies the terminal to refresh its focus state based on the active document elemnet in DOM + */ + refreshFocusState(): void; + /** * Focuses the terminal instance if it's able to (xterm.js instance exists). * diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 953b2249..63264f9a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -9,7 +9,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { TERMINAL_VIEW_ID, ITerminalConfigHelper, TitleEventSource, TERMINAL_COMMAND_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, IRemoteTerminalAttachTarget } from 'vs/workbench/contrib/terminal/common/terminal'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IQuickInputService, IPickOptions, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -24,7 +24,7 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { ITerminalInstance, ITerminalService, Direction, IRemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance, ITerminalService, Direction, IRemoteTerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; import { Action2, registerAction2, ILocalizedString } from 'vs/platform/actions/common/actions'; import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions'; @@ -41,6 +41,9 @@ import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewIte import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; import { ILabelService } from 'vs/platform/label/common/label'; import { RemoteNameContext } from 'vs/workbench/browser/contextkeys'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { killTerminalIcon, newTerminalIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; +import { Codicon } from 'vs/base/common/codicons'; async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { @@ -95,7 +98,7 @@ export class KillTerminalAction extends Action { id: string, label: string, @ITerminalService private readonly _terminalService: ITerminalService ) { - super(id, label, 'terminal-action codicon-trash'); + super(id, label, 'terminal-action ' + ThemeIcon.asClassName(killTerminalIcon)); } async run() { @@ -175,7 +178,7 @@ export class CreateNewTerminalAction extends Action { @ICommandService private readonly _commandService: ICommandService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService ) { - super(id, label, 'terminal-action codicon-add'); + super(id, label, 'terminal-action ' + ThemeIcon.asClassName(newTerminalIcon)); } async run(event?: any) { @@ -216,8 +219,8 @@ export class SplitTerminalAction extends Action { public static readonly ID = TERMINAL_COMMAND_ID.SPLIT; public static readonly LABEL = localize('workbench.action.terminal.split', "Split Terminal"); public static readonly SHORT_LABEL = localize('workbench.action.terminal.split.short', "Split"); - public static readonly HORIZONTAL_CLASS = 'terminal-action codicon-split-horizontal'; - public static readonly VERTICAL_CLASS = 'terminal-action codicon-split-vertical'; + public static readonly HORIZONTAL_CLASS = 'terminal-action ' + Codicon.splitHorizontal.classNames; + public static readonly VERTICAL_CLASS = 'terminal-action ' + Codicon.splitVertical.classNames; constructor( id: string, label: string, @@ -295,6 +298,23 @@ export class SelectDefaultShellWindowsTerminalAction extends Action { } } +export class ConfigureTerminalSettingsAction extends Action { + + public static readonly ID = TERMINAL_COMMAND_ID.CONFIGURE_TERMINAL_SETTINGS; + public static readonly LABEL = localize('workbench.action.terminal.openSettings', "Configure Terminal Settings"); + + constructor( + id: string, label: string, + @IPreferencesService private readonly _preferencesService: IPreferencesService + ) { + super(id, label); + } + + async run() { + this._preferencesService.openSettings(false, '@feature:terminal'); + } +} + const terminalIndexRe = /^([0-9]+): /; export class SwitchTerminalAction extends Action { @@ -307,6 +327,7 @@ export class SwitchTerminalAction extends Action { @ITerminalService private readonly _terminalService: ITerminalService, @ITerminalContributionService private readonly _contributions: ITerminalContributionService, @ICommandService private readonly _commands: ICommandService, + @IPreferencesService private readonly preferencesService: IPreferencesService ) { super(id, label, 'terminal-action switch-terminal'); } @@ -323,7 +344,11 @@ export class SwitchTerminalAction extends Action { this._terminalService.refreshActiveTab(); return this._terminalService.selectDefaultShell(); } - + if (item === ConfigureTerminalSettingsAction.LABEL) { + const settingsAction = new ConfigureTerminalSettingsAction(ConfigureTerminalSettingsAction.ID, ConfigureTerminalSettingsAction.LABEL, this.preferencesService); + settingsAction.run(); + this._terminalService.refreshActiveTab(); + } const indexMatches = terminalIndexRe.exec(item); if (indexMatches) { this._terminalService.setActiveTabByIndex(Number(indexMatches[1]) - 1); @@ -357,6 +382,7 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { this._register(_terminalService.onActiveTabChanged(this._updateItems, this)); this._register(_terminalService.onInstanceTitleChanged(this._updateItems, this)); this._register(_terminalService.onTabDisposed(this._updateItems, this)); + this._register(_terminalService.onDidChangeConnectionState(this._updateItems, this)); this._register(attachSelectBoxStyler(this.selectBox, this._themeService)); } @@ -374,7 +400,10 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { } function getTerminalSelectOpenItems(terminalService: ITerminalService, contributions: ITerminalContributionService): ISelectOptionItem[] { - const items = terminalService.getTabLabels().map(label => { text: label }); + const items = terminalService.connectionState === TerminalConnectionState.Connected ? + terminalService.getTabLabels().map(label => { text: label }) : + [{ text: localize('terminalConnectingLabel', "Starting...") }]; + items.push({ text: SwitchTerminalActionViewItem.SEPARATOR, isDisabled: true }); for (const contributed of contributions.terminalTypes) { @@ -382,6 +411,7 @@ function getTerminalSelectOpenItems(terminalService: ITerminalService, contribut } items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL }); + items.push({ text: ConfigureTerminalSettingsAction.LABEL }); return items; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 6dc11347..105d4cbf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_FONT_WEIGHT, DEFAULT_BOLD_FONT_WEIGHT, FontWeight } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; @@ -20,7 +20,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IProductService } from 'vs/platform/product/common/productService'; import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; const MINIMUM_FONT_SIZE = 6; const MAXIMUM_FONT_SIZE = 25; @@ -51,7 +50,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { @ITelemetryService private readonly telemetryService: ITelemetryService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IProductService private readonly productService: IProductService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { this._updateConfig(); this._configurationService.onDidChangeConfiguration(e => { @@ -59,9 +57,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { this._updateConfig(); } }); - - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: 'terminalConfigHelper/launchRecommendationsIgnore', version: 1 }); } public setLinuxDistro(linuxDistro: LinuxDistro) { @@ -211,7 +206,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { public setWorkspaceShellAllowed(isAllowed: boolean): void { this._onWorkspacePermissionsChanged.fire(isAllowed); - this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE); + this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE, StorageTarget.MACHINE); } public isWorkspaceShellAllowed(defaultValue: boolean | undefined = undefined): boolean | undefined { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalIcons.ts b/src/vs/workbench/contrib/terminal/browser/terminalIcons.ts new file mode 100644 index 00000000..88d2b313 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalIcons.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const terminalViewIcon = registerIcon('terminal-view-icon', Codicon.terminal, localize('terminalViewIcon', 'View icon of the terminal view.')); + +export const renameTerminalIcon = registerIcon('terminal-rename', Codicon.gear, localize('renameTerminalIcon', 'Icon for rename in the terminal quick menu.')); +export const killTerminalIcon = registerIcon('terminal-kill', Codicon.trash, localize('killTerminalIcon', 'Icon for killing a terminal instance.')); +export const newTerminalIcon = registerIcon('terminal-new', Codicon.add, localize('newTerminalIcon', 'Icon for creating a new terminal instance.')); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 67599011..08dfc59e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -20,12 +20,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { IShellLaunchConfig, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride, SHOW_TERMINAL_CONFIG_PROMPT } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; @@ -45,6 +45,8 @@ import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/bro import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -186,6 +188,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @INotificationService private readonly _notificationService: INotificationService, + @IPreferencesService private readonly _preferencesService: IPreferencesService, @IViewsService private readonly _viewsService: IViewsService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IClipboardService private readonly _clipboardService: IClipboardService, @@ -541,9 +544,29 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; } - // Skip processing by xterm.js of keyboard events that resolve to commands described - // within commandsToSkipShell - if (resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { + // for keyboard events that resolve to commands described + // within commandsToSkipShell, either alert or skip processing by xterm.js + if (resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId) && !this._configHelper.config.sendKeybindingsToShell) { + // don't alert when terminal is opened or closed + if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT, StorageScope.GLOBAL, true) && + resolveResult.commandId !== 'workbench.action.terminal.toggleTerminal' && + resolveResult.commandId !== 'workbench.action.terminal.new' && + resolveResult.commandId !== 'workbench.action.togglePanel' && + resolveResult.commandId !== 'workbench.action.terminal.focus') { + this._notificationService.prompt( + Severity.Info, + nls.localize('configure terminal settings', "Some keybindings are dispatched to the workbench by default."), + [ + { + label: nls.localize('configureTerminalSettings', "Configure Terminal Settings"), + run: () => { + this._preferencesService.openSettings(false, '@id:terminal.integrated.commandsToSkipShell,terminal.integrated.sendKeybindingsToShell,terminal.integrated.allowChords'); + } + } as IPromptChoice + ] + ); + this._storageService.store(SHOW_TERMINAL_CONFIG_PROMPT, false, StorageScope.GLOBAL, StorageTarget.USER); + } event.preventDefault(); return false; } @@ -564,6 +587,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; } + // Fallback to force ctrl+v to paste on browsers that do not support + // navigator.clipboard.readText + if (!BrowserFeatures.clipboard.readText && event.key === 'v' && event.ctrlKey) { + return false; + } + return true; }); this._register(dom.addDisposableListener(xterm.element, 'mousedown', () => { @@ -656,7 +685,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { { label: nls.localize('dontShowAgain', "Don't Show Again"), isSecondary: true, - run: () => this._storageService.store(NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, true, StorageScope.GLOBAL) + run: () => this._storageService.store(NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, true, StorageScope.GLOBAL, StorageTarget.MACHINE) } as IPromptChoice ]; this._notificationService.prompt( @@ -728,6 +757,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._terminalFocusContextKey.set(terminalFocused); } + public refreshFocusState() { + this.notifyFindWidgetFocusChanged(false); + } + public dispose(immediate?: boolean): void { this._logService.trace(`terminalInstance#dispose (id: ${this.id})`); @@ -1404,7 +1437,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // maximize on Windows/Linux would fire an event saying that the terminal was not // visible. if (this._xterm.getOption('rendererType') === 'canvas') { - this._xtermCore._renderService._onIntersectionChange({ intersectionRatio: 1 }); + this._xtermCore._renderService?._onIntersectionChange({ intersectionRatio: 1 }); // HACK: Force a refresh of the screen to ensure links are refresh corrected. // This can probably be removed when the above hack is fixed in Chromium. this._xterm.refresh(0, this._xterm.rows - 1); @@ -1594,13 +1627,17 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = .monaco-workbench .pane-body.integrated-terminal .find-focused .xterm .xterm-viewport, .monaco-workbench .pane-body.integrated-terminal .xterm.focus .xterm-viewport, .monaco-workbench .pane-body.integrated-terminal .xterm:focus .xterm-viewport, - .monaco-workbench .pane-body.integrated-terminal .xterm:hover .xterm-viewport { background-color: ${scrollbarSliderBackgroundColor} !important; }` - ); + .monaco-workbench .pane-body.integrated-terminal .xterm:hover .xterm-viewport { background-color: ${scrollbarSliderBackgroundColor} !important; } + .monaco-workbench .pane-body.integrated-terminal .xterm-viewport { scrollbar-color: ${scrollbarSliderBackgroundColor} transparent; } + `); } const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); if (scrollbarSliderHoverBackgroundColor) { - collector.addRule(`.monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { background-color: ${scrollbarSliderHoverBackgroundColor}; }`); + collector.addRule(` + .monaco-workbench .pane-body.integrated-terminal .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { background-color: ${scrollbarSliderHoverBackgroundColor}; } + .monaco-workbench .pane-body.integrated-terminal .xterm-viewport:hover { scrollbar-color: ${scrollbarSliderHoverBackgroundColor} transparent; } + `); } const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts index 6c4af635..7a283761 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts @@ -10,6 +10,8 @@ import { matchesFuzzy } from 'vs/base/common/filters'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { killTerminalIcon, renameTerminalIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; export class TerminalQuickAccessProvider extends PickerQuickAccessProvider { @@ -39,11 +41,11 @@ export class TerminalQuickAccessProvider extends PickerQuickAccessProvider | undefined; + private _connectionState: TerminalConnectionState; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } @@ -97,6 +99,9 @@ export class TerminalService implements ITerminalService { public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } private readonly _onDidRegisterProcessSupport = new Emitter(); public get onDidRegisterProcessSupport(): Event { return this._onDidRegisterProcessSupport.event; } + private readonly _onDidChangeConnectionState = new Emitter(); + public get onDidChangeConnectionState(): Event { return this._onDidChangeConnectionState.event; } + public get connectionState(): TerminalConnectionState { return this._connectionState; } constructor( @IContextKeyService private _contextKeyService: IContextKeyService, @@ -134,6 +139,42 @@ export class TerminalService implements ITerminalService { this._handleInstanceContextKeys(); this._processSupportContextKey = KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED.bindTo(this._contextKeyService); this._processSupportContextKey.set(!isWeb || this._remoteAgentService.getConnection() !== null); + + const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; + const serverSpawn = this.configHelper.config.serverSpawn; + if (!!this._environmentService.remoteAuthority && enableTerminalReconnection && serverSpawn) { + this._remoteTerminalsInitialized = this._reconnectToRemoteTerminals(); + this._connectionState = TerminalConnectionState.Connecting; + } else { + this._connectionState = TerminalConnectionState.Connected; + } + } + + private async _reconnectToRemoteTerminals(): Promise { + const remoteTerms = await this._remoteTerminalService.listTerminals(true); + const workspace = this._workspaceContextService.getWorkspace(); + const unattachedWorkspaceRemoteTerms = remoteTerms + .filter(term => term.workspaceId === workspace.id) + .filter(term => !this.isAttachedToTerminal(term)); + + /* __GDPR__ + "terminalReconnection" : { + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + const data = { + count: unattachedWorkspaceRemoteTerms.length + }; + this._telemetryService.publicLog('terminalReconnection', data); + if (unattachedWorkspaceRemoteTerms.length > 0) { + // Reattach to all remote terminals + for (let term of unattachedWorkspaceRemoteTerms) { + this.createTerminal({ remoteAttach: term }); + } + } + + this._connectionState = TerminalConnectionState.Connected; + this._onDidChangeConnectionState.fire(); } public setNativeWindowsDelegate(delegate: ITerminalNativeWindowsDelegate): void { @@ -226,7 +267,7 @@ export class TerminalService implements ITerminalService { } public getTabLabels(): string[] { - return this._terminalTabs.map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); + return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); } public getFindState(): FindReplaceState { @@ -349,45 +390,13 @@ export class TerminalService implements ITerminalService { } public async initializeTerminals(): Promise { - const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; - const serverSpawn = this.configHelper.config.serverSpawn; - if (!!this._environmentService.remoteAuthority && enableTerminalReconnection && serverSpawn) { - let emptyTab: TerminalTab | undefined; + if (this._remoteTerminalsInitialized) { + await this._remoteTerminalsInitialized; + if (!this.terminalTabs.length) { - emptyTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, undefined); - this._terminalTabs.push(emptyTab); - this._onInstanceTitleChanged.fire(undefined); + this.createTerminal(undefined); } - - const remoteTerms = await this._remoteTerminalService.listTerminals(true); - const workspace = this._workspaceContextService.getWorkspace(); - const unattachedWorkspaceRemoteTerms = remoteTerms - .filter(term => term.workspaceId === workspace.id) - .filter(term => !this.isAttachedToTerminal(term)); - - /* __GDPR__ - "terminalReconnection" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - const data = { - count: unattachedWorkspaceRemoteTerms.length - }; - this._telemetryService.publicLog('terminalReconnection', data); - if (unattachedWorkspaceRemoteTerms.length > 0) { - // Reattach to all remote terminals - this.createTerminal({ remoteAttach: unattachedWorkspaceRemoteTerms[0] }, emptyTab); - for (let term of unattachedWorkspaceRemoteTerms.slice(1)) { - this.createTerminal({ remoteAttach: term }); - } - } else if (this.terminalTabs.length === 1 && this.terminalTabs[0] === emptyTab) { - // No terminals to reconnect to, and the only terminal tab is our placeholder - this.createTerminal(undefined, emptyTab); - } else if (emptyTab) { - // There is another tab, need to remove the empty tab - this._removeTab(emptyTab); - } - } else if (this.terminalTabs.length === 0) { + } else if (!this._environmentService.remoteAuthority && this.terminalTabs.length === 0) { // Local, just create a terminal this.createTerminal(); } @@ -664,7 +673,7 @@ export class TerminalService implements ITerminalService { return instance; } - public createTerminal(shell: IShellLaunchConfig = {}, terminalTab?: TerminalTab): ITerminalInstance { + public createTerminal(shell: IShellLaunchConfig = {}): ITerminalInstance { if (!this.isProcessSupportRegistered) { throw new Error('Could not create terminal when process support is not registered'); } @@ -675,14 +684,11 @@ export class TerminalService implements ITerminalService { return instance; } - if (terminalTab) { - terminalTab.addInstance(shell); - } else { - terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell); - this._terminalTabs.push(terminalTab); - } + const terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell); + this._terminalTabs.push(terminalTab); const instance = terminalTab.terminalInstances[0]; + terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); this._initInstanceListeners(instance); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index ac96823f..6e8e1f58 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IShellLaunchConfig, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; -import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; @@ -219,7 +218,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { private _activeInstanceIndex: number; private _isVisible: boolean = false; - private _willFocus: boolean = false; public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } @@ -249,16 +247,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { } } - /** - * Focus the current active instance, or if there isn't one yet, the first instance added - */ - setWillFocus(focus: boolean): void { - this._willFocus = focus; - if (focus && this.activeInstance) { - this.activeInstance.focusWhenReady(); - } - } - public addInstance(shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance): void { let instance: ITerminalInstance; if ('id' in shellLaunchConfigOrInstance) { @@ -274,10 +262,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { } this._onInstancesChanged.fire(); - - if (this.terminalInstances.length === 1 && this._willFocus) { - instance.focusWhenReady(); - } } public dispose(): void { @@ -387,10 +371,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { } public get title(): string { - if (!this.terminalInstances.length) { - return nls.localize('terminal.integrated.starting', "Starting..."); - } - let title = this.terminalInstances[0].title; for (let i = 1; i < this.terminalInstances.length; i++) { if (this.terminalInstances[i].title) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index ca60145b..5a9b02d8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -3,15 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { disposableTimeout } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; import { debounce } from 'vs/base/common/decorators'; import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { XTermAttributes, XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; -import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; -import type { IBuffer, IBufferCell, ITerminalAddon, Terminal } from 'xterm'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm'; const ESC = '\x1b'; const CSI = `${ESC}[`; @@ -21,7 +23,6 @@ const DELETE_CHAR = `${CSI}X`; const DELETE_REST_OF_LINE = `${CSI}K`; const CSI_STYLE_RE = /^\x1b\[[0-9;]*m/; const CSI_MOVE_RE = /^\x1b\[([0-9]*)(;[35])?O?([DC])/; -const PASSWORD_INPUT_RE = /(?:\W|^)(?:pat|token|password|passphrase|passwd)(\W.*:|:)/i; const NOT_WORD_RE = /[^a-z0-9]/i; const statsBufferSize = 24; @@ -171,6 +172,12 @@ export interface IPrediction { */ readonly affectsStyle?: boolean; + /** + * If set to false, the prediction will not be cleared if no input is + * received from the server. + */ + readonly clearAfterTimeout?: boolean; + /** * Returns a sequence to apply the prediction. * @param buffer to write to @@ -292,6 +299,8 @@ class StringReader { * after it. */ class HardBoundary implements IPrediction { + public readonly clearAfterTimeout = false; + public apply() { return ''; } @@ -314,12 +323,10 @@ class HardBoundary implements IPrediction { * through its `matches` request. */ class TentativeBoundary implements IPrediction { - private expected?: Cursor; - constructor(private readonly inner: IPrediction) { } + constructor(public readonly inner: IPrediction) { } public apply(buffer: IBuffer, cursor: Cursor) { - this.expected = cursor.clone(); - this.inner.apply(buffer, this.expected); + this.inner.apply(buffer, cursor); return ''; } @@ -328,10 +335,6 @@ class TentativeBoundary implements IPrediction { } public rollForwards(cursor: Cursor, withInput: string) { - if (this.expected) { - cursor.moveTo(this.expected); - } - return withInput; } @@ -340,13 +343,16 @@ class TentativeBoundary implements IPrediction { } } +export const isTenativeCharacterPrediction = (p: unknown): p is (TentativeBoundary & { inner: CharacterPrediction }) => + p instanceof TentativeBoundary && p.inner instanceof CharacterPrediction; + /** * Prediction for a single alphanumeric character. */ class CharacterPrediction implements IPrediction { public readonly affectsStyle = true; - protected appliedAt?: { + public appliedAt?: { pos: ICoordinate; oldAttributes: string; oldChar: string; @@ -407,7 +413,7 @@ class BackspacePrediction implements IPrediction { pos: ICoordinate; oldAttributes: string; oldChar: string; - eol: boolean; + isLastChar: boolean; }; constructor(private readonly terminal: Terminal) { } @@ -415,13 +421,13 @@ class BackspacePrediction implements IPrediction { public apply(_: IBuffer, cursor: Cursor) { // at eol if everything to the right is whitespace (zsh will emit a "clear line" code in this case) // todo: can be optimized if `getTrimmedLength` is exposed from xterm - const eol = !cursor.getLine()?.translateToString(undefined, cursor.x).trim(); + const isLastChar = !cursor.getLine()?.translateToString(undefined, cursor.x).trim(); const pos = cursor.coordinate; const move = cursor.shift(-1); const cell = cursor.getCell(); this.appliedAt = cell - ? { eol, pos, oldAttributes: attributesToSeq(cell), oldChar: cell.getChars() } - : { eol, pos, oldAttributes: '', oldChar: '' }; + ? { isLastChar, pos, oldAttributes: attributesToSeq(cell), oldChar: cell.getChars() } + : { isLastChar, pos, oldAttributes: '', oldChar: '' }; return move + DELETE_CHAR; } @@ -444,7 +450,7 @@ class BackspacePrediction implements IPrediction { } public matches(input: StringReader) { - if (this.appliedAt?.eol) { + if (this.appliedAt?.isLastChar) { const r1 = input.eatGradually(`\b${CSI}K`); if (r1 !== MatchResult.Failure) { return r1; @@ -637,6 +643,20 @@ export class PredictionStats extends Disposable { }; } + /** + * Gets the maximum observed latency. + */ + public get maxLatency() { + let max = -Infinity; + for (const [latency, correct] of this.stats) { + if (correct) { + max = Math.max(latency, max); + } + } + + return max; + } + constructor(timeline: PredictionTimeline) { super(); this._register(timeline.onPredictionAdded(p => this.addedAtTime.set(p, Date.now()))); @@ -690,10 +710,18 @@ export class PredictionTimeline { private readonly succeededEmitter = new Emitter(); public readonly onPredictionSucceeded = this.succeededEmitter.event; + private get currentGenerationPredictions() { + return this.expected.filter(({ gen }) => gen === this.expected[0].gen).map(({ p }) => p); + } + public get isShowingPredictions() { return this.showPredictions; } + public get length() { + return this.expected.length; + } + constructor(public readonly terminal: Terminal, private readonly style: TypeAheadStyle) { } public setShowPredictions(show: boolean) { @@ -709,7 +737,7 @@ export class PredictionTimeline { return; } - const toApply = this.expected.filter(({ gen }) => gen === this.expected[0].gen).map(({ p }) => p); + const toApply = this.currentGenerationPredictions; if (show) { this.cursor = undefined; this.style.expectIncomingStyle(toApply.reduce((count, p) => p.affectsStyle ? count + 1 : count, 0)); @@ -719,6 +747,19 @@ export class PredictionTimeline { } } + /** + * Undoes any predictions written and resets expectations. + */ + public undoAllPredictions() { + const buffer = this.getActiveBuffer(); + if (this.showPredictions && buffer) { + this.terminal.write(this.currentGenerationPredictions.reverse() + .map(p => p.rollback(this.getCursor(buffer))).join('')); + } + + this.expected = []; + } + /** * Should be called when input is incoming to the temrinal. */ @@ -754,7 +795,7 @@ export class PredictionTimeline { ReadLoop: while (this.expected.length && reader.remaining > 0) { emitPredictionOmitted(); - const prediction = this.expected[0].p; + const { p: prediction, gen } = this.expected[0]; const cursor = this.getCursor(buffer); let beforeTestReaderIndex = reader.index; switch (prediction.matches(reader)) { @@ -762,7 +803,13 @@ export class PredictionTimeline { // if the input character matches what the next prediction expected, undo // the prediction and write the real character out. const eaten = input.slice(beforeTestReaderIndex, reader.index); - output += prediction.rollForwards?.(cursor, eaten); + if (gen === startingGen) { + output += prediction.rollForwards?.(cursor, eaten); + } else { + prediction.apply(buffer, this.getCursor(buffer)); // move cursor for additional apply + output += eaten; + } + this.succeededEmitter.fire(prediction); this.expected.shift(); break; @@ -860,21 +907,30 @@ export class PredictionTimeline { * pty output/ */ public addBoundary(): void; - public addBoundary(buffer: IBuffer, prediction: IPrediction): void; + public addBoundary(buffer: IBuffer, prediction: IPrediction): boolean; public addBoundary(buffer?: IBuffer, prediction?: IPrediction) { + let applied = false; if (buffer && prediction) { - this.addPrediction(buffer, prediction); + applied = this.addPrediction(buffer, prediction); } this.currentGen++; + return applied; } /** * Peeks the last prediction written. */ - public peekEnd() { + public peekEnd(): IPrediction | undefined { return this.expected[this.expected.length - 1]?.p; } + /** + * Peeks the first pending prediction. + */ + public peekStart(): IPrediction | undefined { + return this.expected[0]?.p; + } + public getCursor(buffer: IBuffer) { if (!this.cursor) { if (this.showPredictions) { @@ -895,29 +951,37 @@ export class PredictionTimeline { return buffer.type === 'normal' ? buffer : undefined; } } + +/** + * Gets the escape sequence args to restore state/appearence in the cell. + */ +const attributesToArgs = (cell: XTermAttributes) => { + if (cell.isAttributeDefault()) { return [0]; } + + const args = []; + if (cell.isBold()) { args.push(1); } + if (cell.isDim()) { args.push(2); } + if (cell.isItalic()) { args.push(3); } + if (cell.isUnderline()) { args.push(4); } + if (cell.isBlink()) { args.push(5); } + if (cell.isInverse()) { args.push(7); } + if (cell.isInvisible()) { args.push(8); } + + if (cell.isFgRGB()) { args.push(38, 2, cell.getFgColor() >>> 24, (cell.getFgColor() >>> 16) & 0xFF, cell.getFgColor() & 0xFF); } + if (cell.isFgPalette()) { args.push(38, 5, cell.getFgColor()); } + if (cell.isFgDefault()) { args.push(39); } + + if (cell.isBgRGB()) { args.push(48, 2, cell.getBgColor() >>> 24, (cell.getBgColor() >>> 16) & 0xFF, cell.getBgColor() & 0xFF); } + if (cell.isBgPalette()) { args.push(48, 5, cell.getBgColor()); } + if (cell.isBgDefault()) { args.push(49); } + + return args; +}; + /** * Gets the escape sequence to restore state/appearence in the cell. */ -const attributesToSeq = (cell: XTermAttributes) => cell.isAttributeDefault() - ? `${CSI}0m` - : [ - cell.isBold() && `${CSI}1m`, - cell.isDim() && `${CSI}2m`, - cell.isItalic() && `${CSI}3m`, - cell.isUnderline() && `${CSI}4m`, - cell.isBlink() && `${CSI}5m`, - cell.isInverse() && `${CSI}7m`, - cell.isInvisible() && `${CSI}8m`, - - cell.isFgRGB() && `${CSI}38;2;${cell.getFgColor() >>> 24};${(cell.getFgColor() >>> 16) & 0xFF};${cell.getFgColor() & 0xFF}m`, - cell.isFgPalette() && `${CSI}38;5;${cell.getFgColor()}m`, - cell.isFgDefault() && `${CSI}39m`, - - cell.isBgRGB() && `${CSI}48;2;${cell.getBgColor() >>> 24};${(cell.getBgColor() >>> 16) & 0xFF};${cell.getBgColor() & 0xFF}m`, - cell.isBgPalette() && `${CSI}48;5;${cell.getBgColor()}m`, - cell.isBgDefault() && `${CSI}49m`, - ].filter(seq => !!seq).join(''); - +const attributesToSeq = (cell: XTermAttributes) => `${CSI}${attributesToArgs(cell).join(';')}m`; const arrayHasPrefixAt = (a: ReadonlyArray, ai: number, b: ReadonlyArray) => { if (a.length - ai > b.length) { @@ -968,7 +1032,7 @@ const getColorWidth = (params: (number | number[])[], pos: number) => { return advance; }; -class TypeAheadStyle { +class TypeAheadStyle implements IDisposable { private static compileArgs(args: ReadonlyArray) { return `${CSI}${args.join(';')}m`; } @@ -984,8 +1048,9 @@ class TypeAheadStyle { public apply!: string; public undo!: string; + private csiHandler?: IDisposable; - constructor(value: ITerminalConfiguration['localEchoStyle']) { + constructor(value: ITerminalConfiguration['localEchoStyle'], private readonly terminal: Terminal) { this.onUpdate(value); } @@ -998,9 +1063,38 @@ class TypeAheadStyle { } /** - * Should be called when an attribut eupdate happens in the terminal. + * Starts tracking for CSI changes in the terminal. */ - public onDidWriteSGR(args: (number | number[])[]) { + public startTracking() { + this.expectedIncomingStyles = 0; + this.onDidWriteSGR(attributesToArgs(core(this.terminal)._inputHandler._curAttrData)); + this.csiHandler = this.terminal.parser.registerCsiHandler({ final: 'm' }, args => { + this.onDidWriteSGR(args); + return false; + }); + } + + /** + * Stops tracking terminal CSI changes. + */ + @debounce(2000) + public debounceStopTracking() { + this.stopTracking(); + } + + /** + * @inheritdoc + */ + public dispose() { + this.stopTracking(); + } + + private stopTracking() { + this.csiHandler?.dispose(); + this.csiHandler = undefined; + } + + private onDidWriteSGR(args: (number | number[])[]) { const originalUndo = this.undoArgs; for (let i = 0; i < args.length;) { const px = args[i]; @@ -1089,41 +1183,70 @@ class TypeAheadStyle { } } +const compileExcludeRegexp = (programs = DEFAULT_LOCAL_ECHO_EXCLUDE) => + new RegExp(`\\b(${programs.map(escapeRegExpCharacters).join('|')})\\b`, 'i'); + +export const enum CharPredictState { + /** No characters typed on this line yet */ + Unknown, + /** Has a pending character prediction */ + HasPendingChar, + /** Character validated on this line */ + Validated, +} + export class TypeAheadAddon extends Disposable implements ITerminalAddon { - private typeaheadStyle = new TypeAheadStyle(this.config.config.localEchoStyle); + private typeaheadStyle?: TypeAheadStyle; private typeaheadThreshold = this.config.config.localEchoLatencyThreshold; - protected lastRow?: { y: number; startingX: number }; - private timeline?: PredictionTimeline; + private excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms); + protected lastRow?: { y: number; startingX: number; charState: CharPredictState }; + protected timeline?: PredictionTimeline; + private terminalTitle = ''; public stats?: PredictionStats; + /** + * Debounce that clears predictions after a timeout if the PTY doesn't apply them. + */ + private clearPredictionDebounce?: IDisposable; + constructor( private readonly processManager: ITerminalProcessManager, private readonly config: TerminalConfigHelper, @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); + this._register(toDisposable(() => this.clearPredictionDebounce?.dispose())); } public activate(terminal: Terminal): void { + const style = this.typeaheadStyle = this._register(new TypeAheadStyle(this.config.config.localEchoStyle, terminal)); const timeline = this.timeline = new PredictionTimeline(terminal, this.typeaheadStyle); const stats = this.stats = this._register(new PredictionStats(this.timeline)); timeline.setShowPredictions(this.typeaheadThreshold === 0); - this._register(terminal.parser.registerCsiHandler({ final: 'm' }, args => { - this.typeaheadStyle.onDidWriteSGR(args); - return false; - })); this._register(terminal.onData(e => this.onUserData(e))); + this._register(terminal.onTitleChange(title => { + this.terminalTitle = title; + this.reevaluatePredictorState(stats, timeline); + })); this._register(terminal.onResize(() => { timeline.setShowPredictions(false); timeline.clearCursor(); this.reevaluatePredictorState(stats, timeline); })); this._register(this.config.onConfigChanged(() => { - this.typeaheadStyle.onUpdate(this.config.config.localEchoStyle); + style.onUpdate(this.config.config.localEchoStyle); this.typeaheadThreshold = this.config.config.localEchoLatencyThreshold; + this.excludeProgramRe = compileExcludeRegexp(this.config.config.localEchoExcludePrograms); this.reevaluatePredictorState(stats, timeline); })); + this._register(this.timeline.onPredictionSucceeded(p => { + if (this.lastRow?.charState === CharPredictState.HasPendingChar && isTenativeCharacterPrediction(p) && p.inner.appliedAt) { + if (p.inner.appliedAt.pos.y + p.inner.appliedAt.pos.baseY === this.lastRow.y) { + this.lastRow.charState = CharPredictState.Validated; + } + } + })); this._register(this.processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); let nextStatsSend: any; @@ -1135,10 +1258,36 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { }, statsSendTelemetryEvery); } + if (timeline.length === 0) { + style.debounceStopTracking(); + } + this.reevaluatePredictorState(stats, timeline); })); } + private deferClearingPredictions() { + if (!this.stats || !this.timeline) { + return; + } + + this.clearPredictionDebounce?.dispose(); + if (this.timeline.length === 0 || this.timeline.peekStart()?.clearAfterTimeout === false) { + this.clearPredictionDebounce = undefined; + return; + } + + this.clearPredictionDebounce = disposableTimeout( + () => { + this.timeline?.undoAllPredictions(); + if (this.lastRow?.charState === CharPredictState.HasPendingChar) { + this.lastRow.charState = CharPredictState.Unknown; + } + }, + Math.max(500, this.stats.maxLatency * 3 / 2), + ); + } + /** * Note on debounce: * @@ -1147,8 +1296,14 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { * terminal cursor is not updated, causes issues. */ @debounce(100) - private reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { - if (this.typeaheadThreshold < 0) { + protected reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { + this.reevaluatePredictorStateNow(stats, timeline); + } + + protected reevaluatePredictorStateNow(stats: PredictionStats, timeline: PredictionTimeline) { + if (this.excludeProgramRe.test(this.terminalTitle)) { + timeline.setShowPredictions(false); + } else if (this.typeaheadThreshold < 0) { timeline.setShowPredictions(false); } else if (this.typeaheadThreshold === 0) { timeline.setShowPredictions(true); @@ -1201,7 +1356,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { // the user gave input, and mark all additions before that as tentative. const actualY = buffer.baseY + buffer.cursorY; if (actualY !== this.lastRow?.y) { - this.lastRow = { y: actualY, startingX: buffer.cursorX }; + this.lastRow = { y: actualY, startingX: buffer.cursorX, charState: CharPredictState.Unknown }; } else { this.lastRow.startingX = Math.min(this.lastRow.startingX, buffer.cursorX); } @@ -1232,7 +1387,16 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { if (reader.eatCharCode(32, 126)) { // alphanum const char = data[reader.index - 1]; - if (this.timeline.addPrediction(buffer, new CharacterPrediction(this.typeaheadStyle, char)) && this.timeline.getCursor(buffer).x === terminal.cols) { + const prediction = new CharacterPrediction(this.typeaheadStyle!, char); + let applied: boolean; + if (this.lastRow.charState === CharPredictState.Unknown) { + applied = this.timeline.addBoundary(buffer, new TentativeBoundary(prediction)); + this.lastRow.charState = CharPredictState.HasPendingChar; + } else { + applied = this.timeline.addPrediction(buffer, prediction); + } + + if (applied && this.timeline.getCursor(buffer).x >= terminal.cols) { this.timeline.addBoundary(buffer, new TentativeBoundary(new LinewrapPrediction())); } continue; @@ -1269,6 +1433,11 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { this.timeline.addBoundary(buffer, new HardBoundary()); break; } + + if (this.timeline.length === 1) { + this.deferClearingPredictions(); + this.typeaheadStyle!.startTracking(); + } } private onBeforeProcessData(event: IBeforeProcessDataEvent): void { @@ -1280,12 +1449,6 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { event.data = this.timeline.beforeServerInput(event.data); // console.log('emitted data:', JSON.stringify(event.data)); - // If there's something that looks like a password prompt, omit giving - // input. This is approximate since there's no TTY "password here" code, - // but should be enough to cover common cases like sudo - if (PASSWORD_INPUT_RE.test(event.data)) { - const terminal = this.timeline.terminal; - this.timeline.addBoundary(terminal.buffer.active, new HardBoundary()); - } + this.deferClearingPredictions(); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 74fc5f85..2d2f0a1b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -19,7 +19,7 @@ import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -67,10 +67,6 @@ export class TerminalViewPane extends ViewPane { } this._onDidChangeViewWelcomeState.fire(); }); - - this._register(this.onDidBlur(() => { - this._terminalService.getActiveTab()?.setWillFocus(false); - })); } protected renderBody(container: HTMLElement): void { @@ -133,6 +129,9 @@ export class TerminalViewPane extends ViewPane { } } else { this._terminalService.getActiveTab()?.setVisible(false); + this._terminalService.terminalInstances.forEach(instance => { + instance.notifyFindWidgetFocusChanged(false); + }); } })); @@ -217,7 +216,25 @@ export class TerminalViewPane extends ViewPane { } public focus() { - this._terminalService.getActiveTab()?.setWillFocus(true); + if (this._terminalService.connectionState === TerminalConnectionState.Connecting) { + // If the terminal is waiting to reconnect to remote terminals, then there is no TerminalInstance yet that can + // be focused. So wait for connection to finish, then focus. + const activeElement = document.activeElement; + this._register(this._terminalService.onDidChangeConnectionState(() => { + // Only focus the terminal if the activeElement has not changed since focus() was called + // TODO hack + if (document.activeElement === activeElement) { + this._focus(); + } + })); + + return; + } + this._focus(); + } + + private _focus() { + this._terminalService.getActiveInstance()?.focusWhenReady(); } public focusFindWidget() { diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts index 3bd49844..913e4de7 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget.ts @@ -12,6 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import * as dom from 'vs/base/browser/dom'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IHoverService, IHoverOptions } from 'vs/workbench/services/hover/browser/hover'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export class EnvironmentVariableInfoWidget extends Widget implements ITerminalWidget { readonly id = 'env-var-info'; @@ -34,7 +35,7 @@ export class EnvironmentVariableInfoWidget extends Widget implements ITerminalWi attach(container: HTMLElement): void { this._container = container; this._domNode = document.createElement('div'); - this._domNode.classList.add('terminal-env-var-info', 'codicon', `codicon-${this._info.getIcon()}`); + this._domNode.classList.add('terminal-env-var-info', ...ThemeIcon.asClassNameArray(this._info.getIcon())); if (this.requiresAction) { this._domNode.classList.add('requires-action'); } diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariable.ts b/src/vs/workbench/contrib/terminal/common/environmentVariable.ts index bb32eafc..196f1849 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariable.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariable.ts @@ -6,6 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export const IEnvironmentVariableService = createDecorator('environmentVariableService'); @@ -98,6 +99,6 @@ export type ISerializableEnvironmentVariableCollection = [string, IEnvironmentVa export interface IEnvironmentVariableInfo { readonly requiresAction: boolean; getInfo(): string; - getIcon(): string; + getIcon(): ThemeIcon; getActions?(): { label: string, iconClass?: string, run: () => void, commandId: string }[]; } diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts index 8373296b..e1d98440 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts @@ -6,7 +6,7 @@ import { IEnvironmentVariableService, IMergedEnvironmentVariableCollection, ISerializableEnvironmentVariableCollection, IEnvironmentVariableCollectionWithPersistence } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { Event, Emitter } from 'vs/base/common/event'; import { debounce, throttle } from 'vs/base/common/decorators'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; @@ -85,7 +85,7 @@ export class EnvironmentVariableService implements IEnvironmentVariableService { } }); const stringifiedJson = JSON.stringify(collectionsJson); - this._storageService.store(ENVIRONMENT_VARIABLE_COLLECTIONS_KEY, stringifiedJson, StorageScope.WORKSPACE); + this._storageService.store(ENVIRONMENT_VARIABLE_COLLECTIONS_KEY, stringifiedJson, StorageScope.WORKSPACE, StorageTarget.MACHINE); } @debounce(1000) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 3b32d9e9..ef7ff2e5 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -50,7 +50,7 @@ export const KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED = new RawContextKey; localEchoStyle: 'bold' | 'dim' | 'italic' | 'underlined' | 'inverted' | string; serverSpawn: boolean; enablePersistentSessions: boolean; } +export const DEFAULT_LOCAL_ECHO_EXCLUDE: ReadonlyArray = ['vim', 'vi', 'nano', 'tmux']; + export interface ITerminalConfigHelper { config: ITerminalConfiguration; @@ -486,6 +490,7 @@ export const enum TERMINAL_COMMAND_ID { TOGGLE = 'workbench.action.terminal.toggleTerminal', KILL = 'workbench.action.terminal.kill', QUICK_KILL = 'workbench.action.terminal.quickKill', + CONFIGURE_TERMINAL_SETTINGS = 'workbench.action.terminal.openSettings', COPY_SELECTION = 'workbench.action.terminal.copySelection', SELECT_ALL = 'workbench.action.terminal.selectAll', DELETE_WORD_LEFT = 'workbench.action.terminal.deleteWordLeft', diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index eba3dd72..ed21243b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -6,7 +6,7 @@ import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT } from 'vs/workbench/contrib/terminal/common/terminal'; +import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_LOCAL_ECHO_EXCLUDE } from 'vs/workbench/contrib/terminal/common/terminal'; import { isMacintosh, isWindows, Platform } from 'vs/base/common/platform'; export const terminalConfiguration: IConfigurationNode = { @@ -15,6 +15,11 @@ export const terminalConfiguration: IConfigurationNode = { title: localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), type: 'object', properties: { + 'terminal.integrated.sendKeybindingsToShell': { + markdownDescription: localize('terminal.integrated.sendKeybindingsToShell', "Dispatches most keybindings to the terminal instead of the workbench, overriding `#terminal.integrated.commandsToSkipShell#`, which can be used alternatively for fine tuning."), + type: 'boolean', + default: false + }, 'terminal.integrated.automationShell.linux': { markdownDescription: localize({ key: 'terminal.integrated.automationShell.linux', @@ -211,7 +216,7 @@ export const terminalConfiguration: IConfigurationNode = { localize('terminal.integrated.rendererType.auto', "Let VS Code guess which renderer to use."), localize('terminal.integrated.rendererType.canvas', "Use the standard GPU/canvas-based renderer."), localize('terminal.integrated.rendererType.dom', "Use the fallback DOM-based renderer."), - localize('terminal.integrated.rendererType.experimentalWebgl', "Use the experimental webgl-based renderer. Note that this has some [known issues](https://github.com/xtermjs/xterm.js/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Faddon%2Fwebgl) and this will only be enabled for new terminals (not hot swappable like the other renderers).") + localize('terminal.integrated.rendererType.experimentalWebgl', "Use the experimental webgl-based renderer. Note that this has some [known issues](https://github.com/xtermjs/xterm.js/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Faddon%2Fwebgl).") ], default: 'auto', description: localize('terminal.integrated.rendererType', "Controls how the terminal is rendered.") @@ -358,6 +363,15 @@ export const terminalConfiguration: IConfigurationNode = { minimum: -1, default: 30, }, + 'terminal.integrated.localEchoExcludePrograms': { + description: localize('terminal.integrated.localEchoExcludePrograms', "Experimental: local echo will be disabled when any of these program names are found in the terminal title."), + type: 'array', + items: { + type: 'string', + uniqueItems: true + }, + default: DEFAULT_LOCAL_ECHO_EXCLUDE, + }, 'terminal.integrated.localEchoStyle': { description: localize('terminal.integrated.localEchoStyle', "Experimental: terminal style of locally echoed text; either a font style or an RGB color."), default: 'dim', diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 069f76a4..92dcbb35 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -93,7 +93,8 @@ export function shouldSetLangEnvVariable(env: platform.IProcessEnvironment, dete return true; } if (detectLocale === 'auto') { - return !env['LANG'] || (env['LANG'].search(/\.UTF\-8$/) === -1 && env['LANG'].search(/\.utf8$/) === -1); + const lang = env['LANG']; + return !lang || (lang.search(/\.UTF\-8$/) === -1 && lang.search(/\.utf8$/) === -1 && lang.search(/\.euc.+/) === -1); } return false; // 'off' } diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 8dc3b6c1..4a006c38 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -152,7 +152,7 @@ async function validateShellPaths(label: string, potentialPaths: string[]): Prom } try { const result = await stat(normalize(current)); - if (result.isFile || result.isSymbolicLink) { + if (result.isFile() || result.isSymbolicLink()) { return { label, path: current diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts index eed4382a..d8263b55 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts @@ -8,7 +8,6 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; -import { StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; suite('Workbench - TerminalConfigHelper', () => { let fixture: HTMLElement; @@ -30,7 +29,7 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Fedora); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); @@ -40,7 +39,7 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); @@ -50,7 +49,7 @@ suite('Workbench - TerminalConfigHelper', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); - const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); }); @@ -68,7 +67,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 10 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize'); @@ -81,12 +80,12 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); @@ -99,7 +98,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 1500 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 25, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it'); @@ -112,12 +111,12 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: null } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.setLinuxDistro(LinuxDistro.Ubuntu); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); }); @@ -135,7 +134,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 2 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight'); @@ -149,7 +148,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set'); }); @@ -162,7 +161,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -174,7 +173,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'sans-serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -186,7 +185,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); @@ -202,7 +201,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -218,7 +217,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -234,7 +233,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService()); + let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts index f50415e5..0814572f 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts @@ -7,8 +7,8 @@ import * as assert from 'assert'; import { Terminal } from 'xterm'; import { SinonStub, stub, useFakeTimers } from 'sinon'; import { Emitter } from 'vs/base/common/event'; -import { IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; -import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { CharPredictState, IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -92,7 +92,8 @@ suite('Workbench - Terminal Typeahead', () => { setup(() => { config = upcastPartial({ localEchoStyle: 'italic', - localEchoLatencyThreshold: 0 + localEchoLatencyThreshold: 0, + localEchoExcludePrograms: DEFAULT_LOCAL_ECHO_EXCLUDE, }); publicLog = stub(); addon = new TestTypeAheadAddon( @@ -100,6 +101,7 @@ suite('Workbench - Terminal Typeahead', () => { upcastPartial({ config, onConfigChanged: onConfigChanged.event }), upcastPartial({ publicLog }) ); + addon.unlockMakingPredictions(); }); teardown(() => { @@ -149,8 +151,7 @@ suite('Workbench - Terminal Typeahead', () => { `${CSI}?25l`, // hide cursor `${CSI}2;7H`, // move cursor cursor `${CSI}X`, // delete character - `${CSI}1m`, // reset style - `${CSI}38;5;1m`, // reset style + `${CSI}1;38;5;1m`, // reset style 'q', // new character `${CSI}?25h`, // show cursor ].join('')); @@ -244,29 +245,56 @@ suite('Workbench - Terminal Typeahead', () => { t.expectWritten(`${CSI}2;6H${CSI}X`); }); - test('avoids predicting password input', () => { + test('waits for first valid prediction on a line', () => { + const t = createMockTerminal({ lines: ['hello|'] }); + addon.lockMakingPredictions(); + addon.activate(t.terminal); + + t.onData('o'); + t.expectWritten(''); + expectProcessed('o', 'o'); + + t.onData('o'); + t.expectWritten(`${CSI}3mo${CSI}23m`); + }); + + test('disables on title change', () => { const t = createMockTerminal({ lines: ['hello|'] }); addon.activate(t.terminal); - const tcases = ['Your password:', 'Password here:', 'PAT:', 'Access token:']; - for (const tcase of tcases) { - expectProcessed(tcase, tcase); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, true, 'expected to show initially'); - t.onData('mellon\r\n'); - t.expectWritten(''); - expectProcessed('\r\n', '\r\n'); + t.onTitleChange.fire('foo - VIM.exe'); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, false, 'expected to hide when vim is open'); - t.onData('o'); // back to normal mode - t.expectWritten(`${CSI}3mo${CSI}23m`); - onBeforeProcessData.fire({ data: 'o' }); - } + t.onTitleChange.fire('foo - git.exe'); + addon.reevaluateNow(); + assert.strictEqual(addon.isShowing, true, 'expected to show again after vim closed'); }); }); }); class TestTypeAheadAddon extends TypeAheadAddon { + public unlockMakingPredictions() { + this.lastRow = { y: 1, startingX: 100, charState: CharPredictState.Validated }; + } + + public lockMakingPredictions() { + this.lastRow = undefined; + } + public unlockLeftNavigating() { - this.lastRow = { y: 1, startingX: 1 }; + this.lastRow = { y: 1, startingX: 1, charState: CharPredictState.Validated }; + } + + public reevaluateNow() { + this.reevaluatePredictorStateNow(this.stats!, this.timeline!); + } + + public get isShowing() { + return !!this.timeline?.isShowingPredictions; } } @@ -293,6 +321,7 @@ function createMockTerminal({ lines, cursorAttrs }: { }) { const written: string[] = []; const cursor = { y: 1, x: 1 }; + const onTitleChange = new Emitter(); const onData = new Emitter(); const csiEmitter = new Emitter(); @@ -316,11 +345,13 @@ function createMockTerminal({ lines, cursorAttrs }: { clearWritten: () => written.splice(0, written.length), onData: (s: string) => onData.fire(s), csiEmitter, + onTitleChange, terminal: { cols: 80, rows: 5, onResize: new Emitter().event, onData: onData.event, + onTitleChange: onTitleChange.event, parser: { registerCsiHandler(_: unknown, callback: () => void) { csiEmitter.event(callback); diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts new file mode 100644 index 00000000..4e541bc8 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { TestService } from 'vs/workbench/contrib/testing/common/testServiceImpl'; + +registerSingleton(ITestService, TestService); diff --git a/src/vs/workbench/contrib/testing/common/testCollection.ts b/src/vs/workbench/contrib/testing/common/testCollection.ts new file mode 100644 index 00000000..ab76dab7 --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/testCollection.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { URI } from 'vs/base/common/uri'; +import { Location as ModeLocation } from 'vs/editor/common/modes'; +import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; +import { TestMessageSeverity, TestRunState } from 'vs/workbench/api/common/extHostTypes'; + +/** + * Request to them main thread to run a set of tests. + */ +export interface RunTestsRequest { + tests: { testId: string; providerId: string }[]; + debug: boolean; +} + +/** + * Request from the main thread to run tests for a single provider. + */ +export interface RunTestForProviderRequest { + providerId: string; + ids: string[]; + debug: boolean; +} + +/** + * Response to a {@link RunTestsRequest} + */ +export interface RunTestsResult { + // todo +} + +export const EMPTY_TEST_RESULT: RunTestsResult = {}; + +export const collectTestResults = (results: ReadonlyArray) => { + return results[0] || {}; // todo +}; + +export interface ITestMessage { + message: string | IMarkdownString; + severity: TestMessageSeverity | undefined; + expectedOutput: string | undefined; + actualOutput: string | undefined; + location: ModeLocation | undefined; +} + +export interface ITestState { + runState: TestRunState; + duration: number | undefined; + messages: ITestMessage[]; +} + +/** + * The TestItem from .d.ts, as a plain object without children. + */ +export interface ITestItem { + label: string; + children?: never; + location: ModeLocation | undefined; + description: string | undefined; + runnable: boolean | undefined; + debuggable: boolean | undefined; + state: ITestState; +} + +/** + * TestItem-like shape, butm with an ID and children as strings. + */ +export interface InternalTestItem { + id: string; + providerId: string; + parent: string | null; + item: ITestItem; +} + +export const enum TestDiffOpType { + Add, + Update, + Remove, +} + +export type TestsDiffOp = + | [op: TestDiffOpType.Add, item: InternalTestItem] + | [op: TestDiffOpType.Update, item: InternalTestItem] + | [op: TestDiffOpType.Remove, itemId: string]; + +/** + * Utility function to get a unique string for a subscription to a resource, + * useful to keep maps of document or workspace folder subscription info. + */ +export const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`; + +/** + * Request from the ext host or main thread to indicate that tests have + * changed. It's assumed that any item upserted *must* have its children + * previously also upserted, or upserted as part of the same operation. + * Children that no longer exist in an upserted item will be removed. + */ +export type TestsDiff = TestsDiffOp[]; + +/** + * @private + */ +export interface IncrementalTestCollectionItem extends InternalTestItem { + children: Set; +} + +/** + * The IncrementalChangeCollector is used in the IncrementalTestCollection + * and called with diff changes as they're applied. This is used in the + * ext host to create a cohesive change event from a diff. + */ +export class IncrementalChangeCollector { + /** + * A node was added. + */ + public add(node: T): void { } + + /** + * A node in the collection was updated. + */ + public update(node: T): void { } + + /** + * A node was removed. + */ + public remove(node: T): void { } + + /** + * Called when the diff has been applied. + */ + public complete(): void { } +} + +/** + * Maintains tests in this extension host sent from the main thread. + */ +export abstract class AbstractIncrementalTestCollection { + /** + * Map of item IDs to test item objects. + */ + protected readonly items = new Map(); + + /** + * ID of test root items. + */ + protected readonly roots = new Set(); + + /** + * Applies the diff to the collection. + */ + public apply(diff: TestsDiff) { + const changes = this.createChangeCollector(); + + for (const op of diff) { + switch (op[0]) { + case TestDiffOpType.Add: { + const item = op[1]; + if (!item.parent) { + this.roots.add(item.id); + const created = this.createItem(item); + this.items.set(item.id, created); + changes.add(created); + } else if (this.items.has(item.parent)) { + const parent = this.items.get(item.parent)!; + parent.children.add(item.id); + const created = this.createItem(item, parent); + this.items.set(item.id, created); + changes.add(created); + } + break; + } + + case TestDiffOpType.Update: { + const item = op[1]; + const existing = this.items.get(item.id); + if (existing) { + Object.assign(existing.item, item.item); + changes.update(existing); + } + break; + } + + case TestDiffOpType.Remove: { + const toRemove = this.items.get(op[1]); + if (!toRemove) { + break; + } + + if (toRemove.parent) { + const parent = this.items.get(toRemove.parent)!; + parent.children.delete(toRemove.id); + } else { + this.roots.delete(toRemove.id); + } + + const queue: Iterable[] = [[op[1]]]; + while (queue.length) { + for (const itemId of queue.pop()!) { + const existing = this.items.get(itemId); + if (existing) { + queue.push(existing.children); + this.items.delete(itemId); + changes.remove(existing); + } + } + } + } + } + } + + changes.complete(); + } + + /** + * Called before a diff is applied to create a new change collector. + */ + protected createChangeCollector() { + return new IncrementalChangeCollector(); + } + + /** + * Creates a new item for the collection from the internal test item. + */ + protected abstract createItem(internal: InternalTestItem, parent?: T): T; +} diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts new file mode 100644 index 00000000..f4d84d58 --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; +import { RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; + +export const ITestService = createDecorator('testService'); + +export interface MainTestController { + runTests(request: RunTestForProviderRequest): Promise; +} + +export type TestDiffListener = (diff: TestsDiff) => void; + +export interface ITestService { + readonly _serviceBrand: undefined; + readonly onShouldSubscribe: Event<{ resource: ExtHostTestingResource, uri: URI }>; + readonly onShouldUnsubscribe: Event<{ resource: ExtHostTestingResource, uri: URI }>; + registerTestController(id: string, controller: MainTestController): void; + unregisterTestController(id: string): void; + runTests(req: RunTestsRequest): Promise; + publishDiff(resource: ExtHostTestingResource, uri: URI, diff: TestsDiff): void; + subscribeToDiffs(resource: ExtHostTestingResource, uri: URI, acceptDiff: TestDiffListener): IDisposable; +} diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts new file mode 100644 index 00000000..845534dd --- /dev/null +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { groupBy } from 'vs/base/common/arrays'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; +import { AbstractIncrementalTestCollection, collectTestResults, getTestSubscriptionKey, IncrementalTestCollectionItem, InternalTestItem, RunTestsRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { ITestService, MainTestController, TestDiffListener } from 'vs/workbench/contrib/testing/common/testService'; + +export class TestService extends Disposable implements ITestService { + declare readonly _serviceBrand: undefined; + private testControllers = new Map(); + private readonly testSubscriptions = new Map; + listeners: number; + }>(); + private readonly subscribeEmitter = new Emitter<{ resource: ExtHostTestingResource, uri: URI }>(); + private readonly unsubscribeEmitter = new Emitter<{ resource: ExtHostTestingResource, uri: URI }>(); + + /** + * Fired when extension hosts should pull events from their test factories. + */ + public readonly onShouldSubscribe = this.subscribeEmitter.event; + + /** + * Fired when extension hosts should stop pulling events from their test factories. + */ + public readonly onShouldUnsubscribe = this.unsubscribeEmitter.event; + + /** + * @inheritdoc + */ + async runTests(req: RunTestsRequest): Promise { + const tests = groupBy(req.tests, (a, b) => a.providerId === b.providerId ? 0 : 1); + const requests = tests.map(group => { + const providerId = group[0].providerId; + const controller = this.testControllers.get(providerId); + return controller?.runTests({ providerId, debug: req.debug, ids: group.map(t => t.testId) }); + }).filter(isDefined); + + return collectTestResults(await Promise.all(requests)); + } + + /** + * @inheritdoc + */ + public subscribeToDiffs(resource: ExtHostTestingResource, uri: URI, acceptDiff: TestDiffListener) { + const subscriptionKey = getTestSubscriptionKey(resource, uri); + let subscription = this.testSubscriptions.get(subscriptionKey); + if (!subscription) { + subscription = { collection: new MainThreadTestCollection(), listeners: 0, onDiff: new Emitter() }; + this.subscribeEmitter.fire({ resource, uri }); + this.testSubscriptions.set(subscriptionKey, subscription); + } + + subscription.listeners++; + + const revive = subscription.collection.getReviverDiff(); + if (revive.length) { + acceptDiff(revive); + } + + const listener = subscription.onDiff.event(acceptDiff); + return toDisposable(() => { + listener.dispose(); + + if (!--subscription!.listeners) { + this.unsubscribeEmitter.fire({ resource, uri }); + this.testSubscriptions.delete(subscriptionKey); + } + }); + } + + /** + * @inheritdoc + */ + public publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff) { + const sub = this.testSubscriptions.get(getTestSubscriptionKey(resource, URI.revive(uri))); + if (sub) { + sub.collection.apply(diff); + sub.onDiff.fire(diff); + } + } + + /** + * @inheritdoc + */ + public registerTestController(id: string, controller: MainTestController): void { + this.testControllers.set(id, controller); + } + + /** + * @inheritdoc + */ + public unregisterTestController(id: string): void { + this.testControllers.delete(id); + } +} + +class MainThreadTestCollection extends AbstractIncrementalTestCollection { + /** + * Gets a diff that adds all items currently in the tree to a new collection, + * allowing it to fully hydrate. + */ + public getReviverDiff() { + const ops: TestsDiff = []; + const queue = [this.roots]; + while (queue.length) { + for (const child of queue.pop()!) { + const item = this.items.get(child)!; + ops.push([TestDiffOpType.Add, { id: item.id, providerId: item.providerId, item: item.item, parent: item.parent }]); + queue.push(item.children); + } + } + + return ops; + } + + protected createItem(internal: InternalTestItem): IncrementalTestCollectionItem { + return { ...internal, children: new Set() }; + } +} diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 769f8708..ca1165af 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -22,6 +22,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { DEFAULT_PRODUCT_ICON_THEME_ID } from 'vs/workbench/services/themes/browser/productIconThemeData'; +import { IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService'; export class SelectColorThemeAction extends Action { @@ -34,6 +35,7 @@ export class SelectColorThemeAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IGettingStartedService private readonly gettingStartedService: IGettingStartedService, @IViewletService private readonly viewletService: IViewletService ) { super(id, label); @@ -84,6 +86,7 @@ export class SelectColorThemeAction extends Action { openExtensionViewlet(this.viewletService, `category:themes ${quickpick.value}`); } else { selectTheme(theme, true); + this.gettingStartedService.progressByEvent('themeSelected'); } isCompleted = true; quickpick.hide(); @@ -302,7 +305,7 @@ class GenerateColorThemeAction extends Action { let theme = this.themeService.getColorTheme(); let colors = Registry.as(ColorRegistryExtensions.ColorContribution).getColors(); let colorIds = colors.map(c => c.id).sort(); - let resultingColors: { [key: string]: string } = {}; + let resultingColors: { [key: string]: string | null } = {}; let inherited: string[] = []; for (let colorId of colorIds) { const color = theme.getColor(colorId, false); @@ -312,12 +315,18 @@ class GenerateColorThemeAction extends Action { inherited.push(colorId); } } + const nullDefaults = []; for (let id of inherited) { const color = theme.getColor(id); if (color) { resultingColors['__' + id] = Color.Format.CSS.formatHexA(color, true); + } else { + nullDefaults.push(id); } } + for (let id of nullDefaults) { + resultingColors['__' + id] = null; + } let contents = JSON.stringify({ '$schema': colorThemeSchemaId, type: theme.type, @@ -326,7 +335,7 @@ class GenerateColorThemeAction extends Action { }, null, '\t'); contents = contents.replace(/\"__/g, '//"'); - return this.editorService.openEditor({ contents, mode: 'jsonc' }); + return this.editorService.openEditor({ contents, mode: 'jsonc', options: { pinned: true } }); } } diff --git a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts index 0eb622bb..d97e3c08 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts @@ -18,6 +18,7 @@ import { Color } from 'vs/base/common/color'; import { IFileService } from 'vs/platform/files/common/files'; import { basename } from 'vs/base/common/resources'; import { Schemas } from 'vs/base/common/network'; +import { splitLines } from 'vs/base/common/strings'; interface IToken { c: string; @@ -220,7 +221,7 @@ class Snapper { if (!grammar) { return []; } - let lines = content.split(/\r\n|\r|\n/); + let lines = splitLines(content); let result = this._tokenize(grammar, lines); return this._getThemesResult(grammar, lines).then((themesResult) => { diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index b7ca9dbb..212f827b 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -18,11 +18,17 @@ import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files'; import { ResourceContextKey } from 'vs/workbench/common/resources'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + + +const timelineViewIcon = registerIcon('timeline-view-icon', Codicon.history, localize('timelineViewIcon', 'View icon of the timeline view.')); +const timelineOpenIcon = registerIcon('timeline-open', Codicon.history, localize('timelineOpenIcon', 'Icon for the open timeline action.')); export class TimelinePaneDescriptor implements IViewDescriptor { readonly id = TimelinePaneId; readonly name = TimelinePane.TITLE; - readonly containerIcon = 'codicon-history'; + readonly containerIcon = timelineViewIcon; readonly ctorDescriptor = new SyncDescriptor(TimelinePane); readonly order = 2; readonly weight = 30; @@ -87,7 +93,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, ({ command: { id: OpenTimelineAction.ID, title: OpenTimelineAction.LABEL, - icon: { id: 'codicon/history' } + icon: timelineOpenIcon }, when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource) })); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 78ae8893..64f30027 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -42,6 +42,8 @@ import { MenuItemAction, IMenuService, MenuId, registerAction2, Action2, MenuReg import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { Codicon } from 'vs/base/common/codicons'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; const ItemHeight = 22; @@ -208,7 +210,7 @@ class LoadMoreCommand { return this.loading ? localize('timeline.loadingMore', "Loading...") : localize('timeline.loadMore', "Load more"); } - get themeIcon(): { id: string; } | undefined { + get themeIcon(): ThemeIcon | undefined { return undefined; //this.loading ? { id: 'sync~spin' } : undefined; } } @@ -1150,6 +1152,11 @@ class TimelineTreeRenderer implements ITreeRenderer('updateState', StateType.Idle); @@ -117,6 +115,30 @@ export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesActio } } +interface IVersion { + major: number; + minor: number; + patch: number; +} + +function parseVersion(version: string): IVersion | undefined { + const match = /([0-9]+)\.([0-9]+)\.([0-9]+)/.exec(version); + + if (!match) { + return undefined; + } + + return { + major: parseInt(match[1]), + minor: parseInt(match[2]), + patch: parseInt(match[3]) + }; +} + +function isMajorMinorUpdate(before: IVersion, after: IVersion): boolean { + return before.major < after.major || before.minor < after.minor; +} + export class ProductContribution implements IWorkbenchContribution { private static readonly KEY = 'releaseNotes/lastVersion'; @@ -136,12 +158,13 @@ export class ProductContribution implements IWorkbenchContribution { return; } - const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, ''); + const lastVersion = parseVersion(storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, '')); + const currentVersion = parseVersion(productService.version); const shouldShowReleaseNotes = configurationService.getValue('update.showReleaseNotes'); - - // was there an update? if so, open release notes const releaseNotesUrl = productService.releaseNotesUrl; - if (shouldShowReleaseNotes && !environmentService.skipReleaseNotes && releaseNotesUrl && lastVersion && productService.version !== lastVersion) { + + // was there a major/minor update? if so, open release notes + if (shouldShowReleaseNotes && !environmentService.skipReleaseNotes && releaseNotesUrl && lastVersion && currentVersion && isMajorMinorUpdate(lastVersion, currentVersion)) { showReleaseNotes(instantiationService, productService.version) .then(undefined, () => { notificationService.prompt( @@ -160,11 +183,11 @@ export class ProductContribution implements IWorkbenchContribution { } // should we show the new license? - if (productService.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(productService.version, '>=1.0.0')) { + if (productService.licenseUrl && lastVersion && lastVersion.major < 1 && currentVersion && currentVersion.major >= 1) { notificationService.info(nls.localize('licenseChanged', "Our license terms have changed, please click [here]({0}) to go through them.", productService.licenseUrl)); } - storageService.store(ProductContribution.KEY, productService.version, StorageScope.GLOBAL); + storageService.store(ProductContribution.KEY, productService.version, StorageScope.GLOBAL, StorageTarget.MACHINE); }); } } @@ -185,15 +208,12 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu @IContextKeyService private readonly contextKeyService: IContextKeyService, @IProductService private readonly productService: IProductService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + @IOpenerService private readonly openerService: IOpenerService ) { super(); this.state = updateService.state; this.updateStateContextKey = CONTEXT_UPDATE_STATE.bindTo(this.contextKeyService); - // opt-in to syncing - storageKeysSyncRegistryService.registerStorageKey({ key: 'neverShowAgain:update/win32-fast-updates', version: 1 }); - this._register(updateService.onStateChange(this.onUpdateStateChange, this)); this.onUpdateStateChange(this.updateService.state); @@ -268,6 +288,10 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu } private onError(error: string): void { + if (/The request timed out|The network connection was lost/i.test(error)) { + return; + } + error = error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information'); this.notificationService.notify({ @@ -398,8 +422,8 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu // if version != stored version, save version and date if (currentVersion !== lastKnownVersion) { - this.storageService.store('update/lastKnownVersion', currentVersion!, StorageScope.GLOBAL); - this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL); + this.storageService.store('update/lastKnownVersion', currentVersion!, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL, StorageTarget.MACHINE); } const updateNotificationMillis = this.storageService.getNumber('update/updateNotificationTime', StorageScope.GLOBAL, currentMillis); @@ -472,7 +496,35 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Updating) }); - CommandsRegistry.registerCommand('update.restart', () => this.updateService.quitAndInstall()); + CommandsRegistry.registerCommand('update.restart', async () => { + if (isNative && isMacintosh && typeof require !== 'undefined' && typeof require.__$__nodeRequire === 'function') { + const os = require.__$__nodeRequire('os') as { release(): string; }; + const release = os.release(); + + if (parseInt(release) >= 20) { // Big Sur + const answer = await this.dialogService.show( + Severity.Warning, + nls.localize('good luck', "'Restart to Update' is not working properly on macOS Big Sur. Click 'Quit to Update' to quit {0} and update it. Then, relaunch it from Finder.", this.productService.nameShort), + [ + nls.localize('quit', "Quit to Update"), + nls.localize('learn more', "Learn More"), + nls.localize('cancel', "Cancel") + ], + { cancelId: 2 } + ); + + if (answer.choice === 2) { + return; + } else if (answer.choice === 1) { + await this.openerService.open(URI.parse('https://github.com/microsoft/vscode/issues/109728')); + return; + } + } + } + + this.updateService.quitAndInstall(); + }); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '6_update', command: { diff --git a/src/vs/workbench/contrib/url/browser/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts index 81ec10ae..145afdf0 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; @@ -31,7 +31,7 @@ export const manageTrustedDomainSettingsCommand = { }, handler: async (accessor: ServicesAccessor) => { const editorService = accessor.get(IEditorService); - editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, mode: 'jsonc' }); + editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, mode: 'jsonc', options: { pinned: true } }); return; } }; @@ -117,7 +117,8 @@ export async function configureOpenerTrustedDomainsHandler( case 'manage': await editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, - mode: 'jsonc' + mode: 'jsonc', + options: { pinned: true } }); notificationService.prompt(Severity.Info, localize('configuringURL', "Configuring trust for: {0}", resource.toString()), [{ label: 'Copy', run: () => clipboardService.writeText(resource.toString()) }]); @@ -129,7 +130,8 @@ export async function configureOpenerTrustedDomainsHandler( storageService.store( TRUSTED_DOMAINS_STORAGE_KEY, JSON.stringify([...trustedDomains, itemToTrust]), - StorageScope.GLOBAL + StorageScope.GLOBAL, + StorageTarget.USER ); return [...trustedDomains, itemToTrust]; diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts index a04a706c..988007b3 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsFileSystemProvider.ts @@ -8,11 +8,10 @@ import { parse } from 'vs/base/common/json'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileService, IStat, IWatchOptions, IFileSystemProviderWithFileReadWriteCapability } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { VSBuffer } from 'vs/base/common/buffer'; import { readTrustedDomains, TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, TRUSTED_DOMAINS_STORAGE_KEY } from 'vs/workbench/contrib/url/browser/trustedDomains'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { assertIsDefined } from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -97,12 +96,8 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith @IFileService private readonly fileService: IFileService, @IStorageService private readonly storageService: IStorageService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { this.fileService.registerProvider(TRUSTED_DOMAINS_SCHEMA, this); - - this.storageKeysSyncRegistryService.registerStorageKey({ key: TRUSTED_DOMAINS_STORAGE_KEY, version: 1 }); - this.storageKeysSyncRegistryService.registerStorageKey({ key: TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, version: 1 }); } stat(resource: URI): Promise { @@ -134,11 +129,12 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith const trustedDomainsContent = VSBuffer.wrap(content).toString(); const trustedDomains = parse(trustedDomainsContent); - this.storageService.store(TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, trustedDomainsContent, StorageScope.GLOBAL); + this.storageService.store(TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, trustedDomainsContent, StorageScope.GLOBAL, StorageTarget.USER); this.storageService.store( TRUSTED_DOMAINS_STORAGE_KEY, JSON.stringify(trustedDomains) || '', - StorageScope.GLOBAL + StorageScope.GLOBAL, + StorageTarget.USER ); } catch (err) { } diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts index 2c8428a6..c8f144bd 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts @@ -283,13 +283,15 @@ const doURLMatch = ( options.push(doURLMatch(memo, url, trustedURL, urlOffset, trustedURLOffset + 2)); } - if (trustedURL[trustedURLOffset] + trustedURL[trustedURLOffset + 1] === '.*' && url[urlOffset] === '.') { - // IP mode. Consume one segment of numbers or nothing. - let endBlockIndex = urlOffset + 1; - do { endBlockIndex++; } while (/[0-9]/.test(url[endBlockIndex])); - if (['.', ':', '/', undefined].includes(url[endBlockIndex])) { - options.push(doURLMatch(memo, url, trustedURL, endBlockIndex, trustedURLOffset + 2)); + if (trustedURL[trustedURLOffset] === '*') { + // Any match. Either consume one thing and don't advance base or consume nothing and do. + if (urlOffset + 1 === url.length) { + // If we're at the end of the input url consume one from both. + options.push(doURLMatch(memo, url, trustedURL, urlOffset + 1, trustedURLOffset + 1)); + } else { + options.push(doURLMatch(memo, url, trustedURL, urlOffset + 1, trustedURLOffset)); } + options.push(doURLMatch(memo, url, trustedURL, urlOffset, trustedURLOffset + 1)); } if (trustedURL[trustedURLOffset] + trustedURL[trustedURLOffset + 1] === ':*') { diff --git a/src/vs/workbench/contrib/url/browser/url.contribution.ts b/src/vs/workbench/contrib/url/browser/url.contribution.ts index 949c9980..5d3a0148 100644 --- a/src/vs/workbench/contrib/url/browser/url.contribution.ts +++ b/src/vs/workbench/contrib/url/browser/url.contribution.ts @@ -37,7 +37,7 @@ class OpenUrlAction extends Action2 { return quickInputService.input({ prompt: localize('urlToOpen', "URL to open") }).then(input => { if (input) { const uri = URI.parse(input); - urlService.open(uri, { trusted: true }); + urlService.open(uri, { originalUrl: input }); } }); } diff --git a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts index 6f8ec1ad..77144c98 100644 --- a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts +++ b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts @@ -116,6 +116,14 @@ suite('Link protection domain matching', () => { linkNotAllowedByRules('http://192.168.1.7:3000/', ['http://192.168.*.6:*']); }); + test('scheme match', () => { + linkAllowedByRules('http://192.168.1.7/', ['http://*']); + linkAllowedByRules('http://twitter.com', ['http://*']); + linkAllowedByRules('http://twitter.com/hello', ['http://*']); + linkNotAllowedByRules('https://192.168.1.7/', ['http://*']); + linkNotAllowedByRules('https://twitter.com/', ['http://*']); + }); + test('case normalization', () => { // https://github.com/microsoft/vscode/issues/99294 linkAllowedByRules('https://github.com/microsoft/vscode/issues/new', ['https://github.com/microsoft']); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index f41dbb1c..8a1aa13b 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { toErrorMessage } from 'vs/base/common/errorMessage'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, MutableDisposable, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; @@ -45,15 +44,14 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { fromNow } from 'vs/base/common/date'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { Codicon } from 'vs/base/common/codicons'; import { ViewContainerLocation, IViewContainersRegistry, Extensions, ViewContainer } from 'vs/workbench/common/views'; import { UserDataSyncViewPaneContainer, UserDataSyncDataViews } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncViews'; -import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_VIEW_ICON } from 'vs/workbench/services/userDataSync/common/userDataSync'; const CONTEXT_CONFLICTS_SOURCES = new RawContextKey('conflictsSources', ''); @@ -259,7 +257,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.userDataSyncService.accept(syncResource, conflict.remoteResource, undefined, this.userDataAutoSyncEnablementService.isEnabled()); } } catch (e) { - this.notificationService.error(e); + this.notificationService.error(localize('accept failed', "Error while accepting changes. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } @@ -269,7 +267,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.userDataSyncService.accept(syncResource, conflict.localResource, undefined, this.userDataAutoSyncEnablementService.isEnabled()); } } catch (e) { - this.notificationService.error(e); + this.notificationService.error(localize('accept failed', "Error while accepting changes. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } @@ -447,7 +445,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.selectSettingsSyncService(this.userDataSyncStoreManagementService.userDataSyncStore); } await this.userDataSyncWorkbenchService.turnOn(); - this.storageService.store('sync.donotAskPreviewConfirmation', true, StorageScope.GLOBAL); + this.storageService.store('sync.donotAskPreviewConfirmation', true, StorageScope.GLOBAL, StorageTarget.MACHINE); } catch (e) { if (isPromiseCanceledError(e)) { return; @@ -482,9 +480,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } }); return; + case UserDataSyncErrorCode.Unauthorized: + this.notificationService.error(localize('auth failed', "Error while turning on Settings Sync: Authentication failed.")); + return; } } - this.notificationService.error(localize('turn on failed', "Error while starting Settings Sync: {0}", toErrorMessage(e))); + this.notificationService.error(localize('turn on failed', "Error while turning on Settings Sync. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } @@ -658,6 +659,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo leftResource: conflict.remoteResource, rightResource: conflict.previewResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: false, pinned: true, @@ -1047,7 +1049,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await that.turnOff(); } catch (e) { if (!isPromiseCanceledError(e)) { - that.notificationService.error(localize('turn off failed', "Error while turning off sync: {0}", toErrorMessage(e))); + that.notificationService.error(localize('turn off failed', "Error while turning off Settings Sync. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } } @@ -1121,7 +1123,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo UserDataSyncViewPaneContainer, [SYNC_VIEW_CONTAINER_ID] ), - icon: Codicon.sync.classNames, + icon: SYNC_VIEW_ICON, hideIfEmpty: true, }, ViewContainerLocation.Sidebar); } @@ -1251,7 +1253,7 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio this.notificationService.warn(localize('update conflicts', "Could not resolve conflicts as there is new local version available. Please try again.")); } } else { - this.notificationService.error(e); + this.notificationService.error(localize('accept failed', "Error while accepting changes. Please check [logs]({0}) for more details.", `command:${SHOW_SYNC_LOG_COMMAND_ID}`)); } } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts index bf0675cd..3610b1ca 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts @@ -302,7 +302,11 @@ export class UserDataSyncMergesViewPane extends TreeViewPane { if (previewResource.mergeState === MergeState.Accepted) { if (previewResource.localChange !== Change.Deleted && previewResource.remoteChange !== Change.Deleted) { // Do not open deleted preview - await this.editorService.openEditor({ resource: previewResource.accepted, label: localize('preview', "{0} (Preview)", basename(previewResource.accepted)) }); + await this.editorService.openEditor({ + resource: previewResource.accepted, + label: localize('preview', "{0} (Preview)", basename(previewResource.accepted)), + options: { pinned: true } + }); } } else { const leftResource = previewResource.remote; @@ -314,9 +318,11 @@ export class UserDataSyncMergesViewPane extends TreeViewPane { leftResource, rightResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: true, revealIfVisible: true, + pinned: true }, }); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 1bf03ec3..d26d9dd1 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IViewsRegistry, Extensions, ITreeViewDescriptor, ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState, TreeViewItemHandleArg, ViewContainer, IViewDescriptorService } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; +import { TreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncResourceEnablementService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; @@ -34,7 +34,6 @@ import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CO import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { TreeView } from 'vs/workbench/contrib/views/browser/treeView'; import { flatten } from 'vs/base/common/arrays'; import { UserDataSyncMergesViewPane } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView'; import { basename } from 'vs/base/common/resources'; @@ -229,7 +228,7 @@ export class UserDataSyncDataViews extends Disposable { async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { const { resource } = <{ resource: string }>JSON.parse(handle.$treeItemHandle); const editorService = accessor.get(IEditorService); - await editorService.openEditor({ resource: URI.parse(resource) }); + await editorService.openEditor({ resource: URI.parse(resource), options: { pinned: true } }); } }); @@ -238,7 +237,7 @@ export class UserDataSyncDataViews extends Disposable { super({ id: `workbench.actions.sync.replaceCurrent`, title: localize('workbench.actions.sync.replaceCurrent', "Restore"), - icon: { id: 'codicon/discard' }, + icon: Codicon.discard, menu: { id: MenuId.ViewItemContext, when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i)), @@ -279,9 +278,11 @@ export class UserDataSyncDataViews extends Disposable { leftResource, rightResource, label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + description: localize('sideBySideDescription', "Settings Sync"), options: { preserveFocus: true, revealIfVisible: true, + pinned: true }, }); } diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts index bad65d90..098549a7 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts @@ -17,7 +17,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; -import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-sandbox/issue'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { Disposable } from 'vs/base/common/lifecycle'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CONTEXT_SYNC_STATE, SHOW_SYNC_LOG_COMMAND_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts deleted file mode 100644 index b035d4ca..00000000 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ /dev/null @@ -1,1059 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/views'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IAction, ActionRunner, IActionViewItemProvider } from 'vs/base/common/actions'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IMenuService, MenuId, MenuItemAction, registerAction2, Action2, SubmenuItemAction } from 'vs/platform/actions/common/actions'; -import { MenuEntryActionViewItem, createAndFillInContextMenuActions, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, IViewDescriptorService, ViewContainer, ViewContainerLocation, ResolvableTreeItem } from 'vs/workbench/common/views'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import * as DOM from 'vs/base/browser/dom'; -import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { URI } from 'vs/base/common/uri'; -import { dirname, basename } from 'vs/base/common/resources'; -import { FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon, IThemeService } from 'vs/platform/theme/common/themeService'; -import { FileKind } from 'vs/platform/files/common/files'; -import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; -import { localize } from 'vs/nls'; -import { timeout } from 'vs/base/common/async'; -import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; -import { isString } from 'vs/base/common/types'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; -import { FuzzyScore, createMatches } from 'vs/base/common/filters'; -import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; -import { isFalsyOrWhitespace } from 'vs/base/common/strings'; -import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; -import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; -import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; - -class Root implements ITreeItem { - label = { label: 'root' }; - handle = '0'; - parentHandle: string | undefined = undefined; - collapsibleState = TreeItemCollapsibleState.Expanded; - children: ITreeItem[] | undefined = undefined; -} - -const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data."); - -class Tree extends WorkbenchAsyncDataTree { } - -export class TreeView extends Disposable implements ITreeView { - - private isVisible: boolean = false; - private _hasIconForParentNode = false; - private _hasIconForLeafNode = false; - - private readonly collapseAllContextKey: RawContextKey; - private readonly collapseAllContext: IContextKey; - private readonly collapseAllToggleContextKey: RawContextKey; - private readonly collapseAllToggleContext: IContextKey; - private readonly refreshContextKey: RawContextKey; - private readonly refreshContext: IContextKey; - - private focused: boolean = false; - private domNode!: HTMLElement; - private treeContainer!: HTMLElement; - private _messageValue: string | undefined; - private _canSelectMany: boolean = false; - private messageElement!: HTMLDivElement; - private tree: Tree | undefined; - private treeLabels: ResourceLabels | undefined; - - private root: ITreeItem; - private elementsToRefresh: ITreeItem[] = []; - - private readonly _onDidExpandItem: Emitter = this._register(new Emitter()); - readonly onDidExpandItem: Event = this._onDidExpandItem.event; - - private readonly _onDidCollapseItem: Emitter = this._register(new Emitter()); - readonly onDidCollapseItem: Event = this._onDidCollapseItem.event; - - private _onDidChangeSelection: Emitter = this._register(new Emitter()); - readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; - - private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); - readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; - - private readonly _onDidChangeActions: Emitter = this._register(new Emitter()); - readonly onDidChangeActions: Event = this._onDidChangeActions.event; - - private readonly _onDidChangeWelcomeState: Emitter = this._register(new Emitter()); - readonly onDidChangeWelcomeState: Event = this._onDidChangeWelcomeState.event; - - private readonly _onDidChangeTitle: Emitter = this._register(new Emitter()); - readonly onDidChangeTitle: Event = this._onDidChangeTitle.event; - - private readonly _onDidChangeDescription: Emitter = this._register(new Emitter()); - readonly onDidChangeDescription: Event = this._onDidChangeDescription.event; - - private readonly _onDidCompleteRefresh: Emitter = this._register(new Emitter()); - - constructor( - readonly id: string, - private _title: string, - @IThemeService private readonly themeService: IThemeService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ICommandService private readonly commandService: ICommandService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IProgressService protected readonly progressService: IProgressService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @INotificationService private readonly notificationService: INotificationService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IHoverService private readonly hoverService: IHoverService, - @IContextKeyService contextKeyService: IContextKeyService - ) { - super(); - this.root = new Root(); - this.collapseAllContextKey = new RawContextKey(`treeView.${this.id}.enableCollapseAll`, false); - this.collapseAllContext = this.collapseAllContextKey.bindTo(contextKeyService); - this.collapseAllToggleContextKey = new RawContextKey(`treeView.${this.id}.toggleCollapseAll`, false); - this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(contextKeyService); - this.refreshContextKey = new RawContextKey(`treeView.${this.id}.enableRefresh`, false); - this.refreshContext = this.refreshContextKey.bindTo(contextKeyService); - - this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); - this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('explorer.decorations')) { - this.doRefresh([this.root]); /** soft refresh **/ - } - })); - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } }); - } - })); - this.registerActions(); - - this.create(); - } - - get viewContainer(): ViewContainer { - return this.viewDescriptorService.getViewContainerByViewId(this.id)!; - } - - get viewLocation(): ViewContainerLocation { - return this.viewDescriptorService.getViewLocationById(this.id)!; - } - - private _dataProvider: ITreeViewDataProvider | undefined; - get dataProvider(): ITreeViewDataProvider | undefined { - return this._dataProvider; - } - - set dataProvider(dataProvider: ITreeViewDataProvider | undefined) { - if (this.tree === undefined) { - this.createTree(); - } - - if (dataProvider) { - const self = this; - this._dataProvider = new class implements ITreeViewDataProvider { - private _isEmpty: boolean = true; - private _onDidChangeEmpty: Emitter = new Emitter(); - public onDidChangeEmpty: Event = this._onDidChangeEmpty.event; - - get isTreeEmpty(): boolean { - return this._isEmpty; - } - - async getChildren(node?: ITreeItem): Promise { - let children: ITreeItem[]; - if (node && node.children) { - children = node.children; - } else { - node = node ?? self.root; - children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); - node.children = children; - } - if (node instanceof Root) { - const oldEmpty = this._isEmpty; - this._isEmpty = children.length === 0; - if (oldEmpty !== this._isEmpty) { - this._onDidChangeEmpty.fire(); - } - } - return children; - } - }; - if (this._dataProvider.onDidChangeEmpty) { - this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire())); - } - this.updateMessage(); - this.refresh(); - } else { - this._dataProvider = undefined; - this.updateMessage(); - } - - this._onDidChangeWelcomeState.fire(); - } - - private _message: string | undefined; - get message(): string | undefined { - return this._message; - } - - set message(message: string | undefined) { - this._message = message; - this.updateMessage(); - this._onDidChangeWelcomeState.fire(); - } - - get title(): string { - return this._title; - } - - set title(name: string) { - this._title = name; - this._onDidChangeTitle.fire(this._title); - } - - private _description: string | undefined; - get description(): string | undefined { - return this._description; - } - - set description(description: string | undefined) { - this._description = description; - this._onDidChangeDescription.fire(this._description); - } - - get canSelectMany(): boolean { - return this._canSelectMany; - } - - set canSelectMany(canSelectMany: boolean) { - this._canSelectMany = canSelectMany; - } - - get hasIconForParentNode(): boolean { - return this._hasIconForParentNode; - } - - get hasIconForLeafNode(): boolean { - return this._hasIconForLeafNode; - } - - get visible(): boolean { - return this.isVisible; - } - - get showCollapseAllAction(): boolean { - return !!this.collapseAllContext.get(); - } - - set showCollapseAllAction(showCollapseAllAction: boolean) { - this.collapseAllContext.set(showCollapseAllAction); - } - - get showRefreshAction(): boolean { - return !!this.refreshContext.get(); - } - - set showRefreshAction(showRefreshAction: boolean) { - this.refreshContext.set(showRefreshAction); - } - - private registerActions() { - const that = this; - this._register(registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${that.id}.refresh`, - title: localize('refresh', "Refresh"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.refreshContextKey), - group: 'navigation', - order: Number.MAX_SAFE_INTEGER - 1, - }, - icon: { id: 'codicon/refresh' } - }); - } - async run(): Promise { - return that.refresh(); - } - })); - this._register(registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.treeView.${that.id}.collapseAll`, - title: localize('collapseAll', "Collapse All"), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', that.id), that.collapseAllContextKey), - group: 'navigation', - order: Number.MAX_SAFE_INTEGER, - }, - precondition: that.collapseAllToggleContextKey, - icon: { id: 'codicon/collapse-all' } - }); - } - async run(): Promise { - if (that.tree) { - return new CollapseAllAction(that.tree, true).run(); - } - } - })); - } - - setVisibility(isVisible: boolean): void { - isVisible = !!isVisible; - if (this.isVisible === isVisible) { - return; - } - - this.isVisible = isVisible; - - if (this.tree) { - if (this.isVisible) { - DOM.show(this.tree.getHTMLElement()); - } else { - DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it - } - - if (this.isVisible && this.elementsToRefresh.length) { - this.doRefresh(this.elementsToRefresh); - this.elementsToRefresh = []; - } - } - - this._onDidChangeVisibility.fire(this.isVisible); - } - - focus(reveal: boolean = true): void { - if (this.tree && this.root.children && this.root.children.length > 0) { - // Make sure the current selected element is revealed - const selectedElement = this.tree.getSelection()[0]; - if (selectedElement && reveal) { - this.tree.reveal(selectedElement, 0.5); - } - - // Pass Focus to Viewer - this.tree.domFocus(); - } else if (this.tree) { - this.tree.domFocus(); - } else { - this.domNode.focus(); - } - } - - show(container: HTMLElement): void { - DOM.append(container, this.domNode); - } - - private create() { - this.domNode = DOM.$('.tree-explorer-viewlet-tree-view'); - this.messageElement = DOM.append(this.domNode, DOM.$('.message')); - this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree')); - this.treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); - const focusTracker = this._register(DOM.trackFocus(this.domNode)); - this._register(focusTracker.onDidFocus(() => this.focused = true)); - this._register(focusTracker.onDidBlur(() => this.focused = false)); - } - - private createTree() { - const actionViewItemProvider = (action: IAction) => { - if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action); - } else if (action instanceof SubmenuItemAction) { - return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action); - } - - return undefined; - }; - const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); - this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); - const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); - const aligner = new Aligner(this.themeService); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); - const widgetAriaLabel = this._title; - - this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer, new TreeViewDelegate(), [renderer], - dataSource, { - identityProvider: new TreeViewIdentityProvider(), - accessibilityProvider: { - getAriaLabel(element: ITreeItem): string { - if (element.accessibilityInformation) { - return element.accessibilityInformation.label; - } - - return isString(element.tooltip) ? element.tooltip : element.label ? element.label.label : ''; - }, - getRole(element: ITreeItem): string | undefined { - return element.accessibilityInformation?.role ?? 'treeitem'; - }, - getWidgetAriaLabel(): string { - return widgetAriaLabel; - } - }, - keyboardNavigationLabelProvider: { - getKeyboardNavigationLabel: (item: ITreeItem) => { - return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); - } - }, - expandOnlyOnTwistieClick: (e: ITreeItem) => !!e.command, - collapseByDefault: (e: ITreeItem): boolean => { - return e.collapsibleState !== TreeItemCollapsibleState.Expanded; - }, - multipleSelectionSupport: this.canSelectMany, - overrideStyles: { - listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND - } - }) as WorkbenchAsyncDataTree); - aligner.tree = this.tree; - const actionRunner = new MultipleSelectionActionRunner(this.notificationService, () => this.tree!.getSelection()); - renderer.actionRunner = actionRunner; - - this.tree.contextKeyService.createKey(this.id, true); - this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); - this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); - this._register(this.tree.onDidChangeCollapseState(e => { - if (!e.node.element) { - return; - } - - const element: ITreeItem = Array.isArray(e.node.element.element) ? e.node.element.element[0] : e.node.element.element; - if (e.node.collapsed) { - this._onDidCollapseItem.fire(element); - } else { - this._onDidExpandItem.fire(element); - } - })); - this.tree.setInput(this.root).then(() => this.updateContentAreas()); - - this._register(this.tree.onDidOpen(e => { - if (!e.browserEvent) { - return; - } - const selection = this.tree!.getSelection(); - if ((selection.length === 1) && selection[0].command) { - this.commandService.executeCommand(selection[0].command.id, ...(selection[0].command.arguments || [])); - } - })); - - } - - private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { - this.hoverService.hideHover(); - const node: ITreeItem | null = treeEvent.element; - if (node === null) { - return; - } - const event: UIEvent = treeEvent.browserEvent; - - event.preventDefault(); - event.stopPropagation(); - - this.tree!.setFocus([node]); - const actions = treeMenus.getResourceContextActions(node); - if (!actions.length) { - return; - } - this.contextMenuService.showContextMenu({ - getAnchor: () => treeEvent.anchor, - - getActions: () => actions, - - getActionViewItem: (action) => { - const keybinding = this.keybindingService.lookupKeybinding(action.id); - if (keybinding) { - return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); - } - return undefined; - }, - - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - this.tree!.domFocus(); - } - }, - - getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), - - actionRunner - }); - } - - protected updateMessage(): void { - if (this._message) { - this.showMessage(this._message); - } else if (!this.dataProvider) { - this.showMessage(noDataProviderMessage); - } else { - this.hideMessage(); - } - this.updateContentAreas(); - } - - private showMessage(message: string): void { - this.messageElement.classList.remove('hide'); - this.resetMessageElement(); - this._messageValue = message; - if (!isFalsyOrWhitespace(this._message)) { - this.messageElement.textContent = this._messageValue; - } - this.layout(this._height, this._width); - } - - private hideMessage(): void { - this.resetMessageElement(); - this.messageElement.classList.add('hide'); - this.layout(this._height, this._width); - } - - private resetMessageElement(): void { - DOM.clearNode(this.messageElement); - } - - private _height: number = 0; - private _width: number = 0; - layout(height: number, width: number) { - if (height && width) { - this._height = height; - this._width = width; - const treeHeight = height - DOM.getTotalHeight(this.messageElement); - this.treeContainer.style.height = treeHeight + 'px'; - if (this.tree) { - this.tree.layout(treeHeight, width); - } - } - } - - getOptimalWidth(): number { - if (this.tree) { - const parentNode = this.tree.getHTMLElement(); - const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.outline-item-label > a')); - return DOM.getLargestChildWidth(parentNode, childNodes); - } - return 0; - } - - async refresh(elements?: ITreeItem[]): Promise { - if (this.dataProvider && this.tree) { - if (this.refreshing) { - await Event.toPromise(this._onDidCompleteRefresh.event); - } - if (!elements) { - elements = [this.root]; - // remove all waiting elements to refresh if root is asked to refresh - this.elementsToRefresh = []; - } - for (const element of elements) { - element.children = undefined; // reset children - } - if (this.isVisible) { - return this.doRefresh(elements); - } else { - if (this.elementsToRefresh.length) { - const seen: Set = new Set(); - this.elementsToRefresh.forEach(element => seen.add(element.handle)); - for (const element of elements) { - if (!seen.has(element.handle)) { - this.elementsToRefresh.push(element); - } - } - } else { - this.elementsToRefresh.push(...elements); - } - } - } - return undefined; - } - - async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { - const tree = this.tree; - if (tree) { - itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; - await Promise.all(itemOrItems.map(element => { - return tree.expand(element, false); - })); - } - } - - setSelection(items: ITreeItem[]): void { - if (this.tree) { - this.tree.setSelection(items); - } - } - - setFocus(item: ITreeItem): void { - if (this.tree) { - this.focus(); - this.tree.setFocus([item]); - } - } - - async reveal(item: ITreeItem): Promise { - if (this.tree) { - return this.tree.reveal(item); - } - } - - private refreshing: boolean = false; - private async doRefresh(elements: ITreeItem[]): Promise { - const tree = this.tree; - if (tree && this.visible) { - this.refreshing = true; - await Promise.all(elements.map(element => tree.updateChildren(element, true, true))); - this.refreshing = false; - this._onDidCompleteRefresh.fire(); - this.updateContentAreas(); - if (this.focused) { - this.focus(false); - } - this.updateCollapseAllToggle(); - } - } - - private updateCollapseAllToggle() { - if (this.showCollapseAllAction) { - this.collapseAllToggleContext.set(!!this.root.children && (this.root.children.length > 0) && - this.root.children.some(value => value.collapsibleState !== TreeItemCollapsibleState.None)); - } - } - - private updateContentAreas(): void { - const isTreeEmpty = !this.root.children || this.root.children.length === 0; - // Hide tree container only when there is a message and tree is empty and not refreshing - if (this._messageValue && isTreeEmpty && !this.refreshing) { - this.treeContainer.classList.add('hide'); - this.domNode.setAttribute('tabindex', '0'); - } else { - this.treeContainer.classList.remove('hide'); - this.domNode.removeAttribute('tabindex'); - } - } -} - -class TreeViewIdentityProvider implements IIdentityProvider { - getId(element: ITreeItem): { toString(): string; } { - return element.handle; - } -} - -class TreeViewDelegate implements IListVirtualDelegate { - - getHeight(element: ITreeItem): number { - return TreeRenderer.ITEM_HEIGHT; - } - - getTemplateId(element: ITreeItem): string { - return TreeRenderer.TREE_TEMPLATE_ID; - } -} - -class TreeDataSource implements IAsyncDataSource { - - constructor( - private treeView: ITreeView, - private withProgress: (task: Promise) => Promise - ) { - } - - hasChildren(element: ITreeItem): boolean { - return !!this.treeView.dataProvider && (element.collapsibleState !== TreeItemCollapsibleState.None); - } - - async getChildren(element: ITreeItem): Promise { - if (this.treeView.dataProvider) { - return this.withProgress(this.treeView.dataProvider.getChildren(element)); - } - return []; - } -} - -// todo@joh,sandy make this proper and contributable from extensions -registerThemingParticipant((theme, collector) => { - - const matchBackgroundColor = theme.getColor(listFilterMatchHighlight); - if (matchBackgroundColor) { - collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); - collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`); - } - const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder); - if (matchBorderColor) { - collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); - collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`); - } - const link = theme.getColor(textLinkForeground); - if (link) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message a { color: ${link}; }`); - } - const focusBorderColor = theme.getColor(focusBorder); - if (focusBorderColor) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message a:focus { outline: 1px solid ${focusBorderColor}; outline-offset: -1px; }`); - } - const codeBackground = theme.getColor(textCodeBlockBackground); - if (codeBackground) { - collector.addRule(`.tree-explorer-viewlet-tree-view > .message code { background-color: ${codeBackground}; }`); - } -}); - -interface ITreeExplorerTemplateData { - elementDisposable: IDisposable; - container: HTMLElement; - resourceLabel: IResourceLabel; - icon: HTMLElement; - actionBar: ActionBar; -} - -class TreeRenderer extends Disposable implements ITreeRenderer { - static readonly ITEM_HEIGHT = 22; - static readonly TREE_TEMPLATE_ID = 'treeExplorer'; - - private _actionRunner: MultipleSelectionActionRunner | undefined; - private _hoverDelegate: IHoverDelegate; - - constructor( - private treeViewId: string, - private menus: TreeMenus, - private labels: ResourceLabels, - private actionViewItemProvider: IActionViewItemProvider, - private aligner: Aligner, - @IThemeService private readonly themeService: IThemeService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ILabelService private readonly labelService: ILabelService, - @IHoverService private readonly hoverService: IHoverService - ) { - super(); - this._hoverDelegate = { - showHover: (options: IHoverDelegateOptions): IDisposable | undefined => { - return this.hoverService.showHover(options); - } - }; - } - - get templateId(): string { - return TreeRenderer.TREE_TEMPLATE_ID; - } - - set actionRunner(actionRunner: MultipleSelectionActionRunner) { - this._actionRunner = actionRunner; - } - - renderTemplate(container: HTMLElement): ITreeExplorerTemplateData { - container.classList.add('custom-view-tree-node-item'); - - const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - - const resourceLabel = this.labels.create(container, { supportHighlights: true, hoverDelegate: this._hoverDelegate }); - const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); - const actionBar = new ActionBar(actionsContainer, { - actionViewItemProvider: this.actionViewItemProvider - }); - - return { resourceLabel, icon, actionBar, container, elementDisposable: Disposable.None }; - } - - private getHover(label: string | undefined, resource: URI | null, node: ITreeItem): string | Promise | undefined { - if (!(node instanceof ResolvableTreeItem) || !node.hasResolve) { - if (resource) { - return undefined; - } else if (!node.tooltip) { - return label; - } else if (!isString(node.tooltip)) { - return Promise.resolve(node.tooltip); - } else { - return node.tooltip; - } - } - - return new Promise(async (resolve) => { - await node.resolve(); - resolve(node.tooltip); - }); - } - - renderElement(element: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); - const node = element.element; - const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; - const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : (resource ? { label: basename(resource) } : undefined); - const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined; - const label = treeItemLabel ? treeItemLabel.label : undefined; - const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => { - if (start < 0) { - start = label.length + start; - } - if (end < 0) { - end = label.length + end; - } - if ((start >= label.length) || (end > label.length)) { - return ({ start: 0, end: 0 }); - } - if (start > end) { - const swap = start; - start = end; - end = swap; - } - return ({ start, end }); - }) : undefined; - const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; - const iconUrl = icon ? URI.revive(icon) : null; - const title = this.getHover(label, resource, node); - - // reset - templateData.actionBar.clear(); - templateData.icon.style.color = ''; - - if (resource || this.isFileKindThemeIcon(node.themeIcon)) { - const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - const labelResource = resource ? resource : URI.parse('missing:_icon_resource'); - templateData.resourceLabel.setResource({ name: label, description, resource: labelResource }, { - fileKind: this.getFileKind(node), - title, - hideIcon: !!iconUrl, - fileDecorations, - extraClasses: ['custom-view-tree-node-item-resourceLabel'], - matches: matches ? matches : createMatches(element.filterData), - strikethrough: treeItemLabel?.strikethrough - }); - } else { - templateData.resourceLabel.setResource({ name: label, description }, { - title, - hideIcon: true, - extraClasses: ['custom-view-tree-node-item-resourceLabel'], - matches: matches ? matches : createMatches(element.filterData), - strikethrough: treeItemLabel?.strikethrough - }); - } - - if (iconUrl) { - templateData.icon.className = 'custom-view-tree-node-item-icon'; - templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl); - } else { - let iconClass: string | undefined; - if (node.themeIcon && !this.isFileKindThemeIcon(node.themeIcon)) { - iconClass = ThemeIcon.asClassName(node.themeIcon); - if (node.themeIcon.color) { - templateData.icon.style.color = this.themeService.getColorTheme().getColor(node.themeIcon.color.id)?.toString() ?? ''; - } - } - templateData.icon.className = iconClass ? `custom-view-tree-node-item-icon ${iconClass}` : ''; - templateData.icon.style.backgroundImage = ''; - } - - templateData.actionBar.context = { $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; - templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); - if (this._actionRunner) { - templateData.actionBar.actionRunner = this._actionRunner; - } - this.setAlignment(templateData.container, node); - const disposableStore = new DisposableStore(); - templateData.elementDisposable = disposableStore; - disposableStore.add(this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); - } - - private setAlignment(container: HTMLElement, treeItem: ITreeItem) { - container.parentElement!.classList.toggle('align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); - } - - private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean { - if (icon) { - return icon.id === FileThemeIcon.id || icon.id === FolderThemeIcon.id; - } else { - return false; - } - } - - private getFileKind(node: ITreeItem): FileKind { - if (node.themeIcon) { - switch (node.themeIcon.id) { - case FileThemeIcon.id: - return FileKind.FILE; - case FolderThemeIcon.id: - return FileKind.FOLDER; - } - } - return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE; - } - - disposeElement(resource: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.dispose(); - } - - disposeTemplate(templateData: ITreeExplorerTemplateData): void { - templateData.resourceLabel.dispose(); - templateData.actionBar.dispose(); - templateData.elementDisposable.dispose(); - } -} - -class Aligner extends Disposable { - private _tree: WorkbenchAsyncDataTree | undefined; - - constructor(private themeService: IThemeService) { - super(); - } - - set tree(tree: WorkbenchAsyncDataTree) { - this._tree = tree; - } - - public alignIconWithTwisty(treeItem: ITreeItem): boolean { - if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { - return false; - } - if (!this.hasIcon(treeItem)) { - return false; - } - - if (this._tree) { - const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); - if (this.hasIcon(parent)) { - return !!parent.children && parent.children.some(c => c.collapsibleState !== TreeItemCollapsibleState.None && !this.hasIcon(c)); - } - return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); - } else { - return false; - } - } - - private hasIcon(node: ITreeItem): boolean { - const icon = this.themeService.getColorTheme().type === ColorScheme.LIGHT ? node.icon : node.iconDark; - if (icon) { - return true; - } - if (node.resourceUri || node.themeIcon) { - const fileIconTheme = this.themeService.getFileIconTheme(); - const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None; - if (isFolder) { - return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons; - } - return fileIconTheme.hasFileIcons; - } - return false; - } -} - -class MultipleSelectionActionRunner extends ActionRunner { - - constructor(notificationService: INotificationService, private getSelectedResources: (() => ITreeItem[])) { - super(); - this._register(this.onDidRun(e => { - if (e.error) { - notificationService.error(localize('command-error', 'Error running command {1}: {0}. This is likely caused by the extension that contributes {1}.', e.error.message, e.action.id)); - } - })); - } - - runAction(action: IAction, context: TreeViewItemHandleArg): Promise { - const selection = this.getSelectedResources(); - let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; - let actionInSelected: boolean = false; - if (selection.length > 1) { - selectionHandleArgs = selection.map(selected => { - if (selected.handle === context.$treeItemHandle) { - actionInSelected = true; - } - return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }; - }); - } - - if (!actionInSelected) { - selectionHandleArgs = undefined; - } - - return action.run(...[context, selectionHandleArgs]); - } -} - -class TreeMenus extends Disposable implements IDisposable { - - constructor( - private id: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService - ) { - super(); - } - - getResourceActions(element: ITreeItem): IAction[] { - return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).primary; - } - - getResourceContextActions(element: ITreeItem): IAction[] { - return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary; - } - - private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } { - const contextKeyService = this.contextKeyService.createScoped(); - contextKeyService.createKey('view', this.id); - contextKeyService.createKey(context.key, context.value); - - const menu = this.menuService.createMenu(menuId, contextKeyService); - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - menu.dispose(); - contextKeyService.dispose(); - - return result; - } -} - -export class CustomTreeView extends TreeView { - - private activated: boolean = false; - - constructor( - id: string, - title: string, - @IThemeService themeService: IThemeService, - @IInstantiationService instantiationService: IInstantiationService, - @ICommandService commandService: ICommandService, - @IConfigurationService configurationService: IConfigurationService, - @IProgressService progressService: IProgressService, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, - @INotificationService notificationService: INotificationService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IContextKeyService contextKeyService: IContextKeyService, - @IHoverService hoverService: IHoverService, - @IExtensionService private readonly extensionService: IExtensionService, - ) { - super(id, title, themeService, instantiationService, commandService, configurationService, progressService, contextMenuService, keybindingService, notificationService, viewDescriptorService, hoverService, contextKeyService); - } - - setVisibility(isVisible: boolean): void { - super.setVisibility(isVisible); - if (this.visible) { - this.activate(); - } - } - - private activate() { - if (!this.activated) { - this.progressService.withProgress({ location: this.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) - .then(() => timeout(2000)) - .then(() => { - this.updateMessage(); - }); - this.activated = true; - } - } -} diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 31686124..142650d3 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -37,7 +37,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private _owner: any = undefined; private readonly _scopedContextKeyService = this._register(new MutableDisposable()); - private _findWidgetVisible: IContextKey; + private _findWidgetVisible: IContextKey | undefined; public constructor( public readonly id: string, @@ -46,15 +46,13 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv extension: WebviewExtensionDescription | undefined, @ILayoutService private readonly _layoutService: ILayoutService, @IWebviewService private readonly _webviewService: IWebviewService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService + @IContextKeyService private readonly _baseContextKeyService: IContextKeyService ) { super(); this._extension = extension; this._options = initialOptions; this._contentOptions = initialContentOptions; - - this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(_contextKeyService); } public get isFocused() { @@ -83,15 +81,32 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv return container; } - public claim(owner: any) { + public claim(owner: any, scopedContextKeyService: IContextKeyService | undefined) { + const oldOwner = this._owner; + this._owner = owner; this.show(); + + if (oldOwner !== owner) { + const contextKeyService = (scopedContextKeyService || this._baseContextKeyService); + + // Explicitly clear before creating the new context. + // Otherwise we create the new context while the old one is still around + this._scopedContextKeyService.clear(); + this._scopedContextKeyService.value = contextKeyService.createScoped(this.container); + + this._findWidgetVisible?.reset(); + this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(contextKeyService); + } } public release(owner: any) { if (this._owner !== owner) { return; } + + this._scopedContextKeyService.clear(); + this._owner = undefined; this.container.style.visibility = 'hidden'; if (!this._options.retainContextWhenHidden) { @@ -130,8 +145,6 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv } webview.mountTo(this.container); - this._scopedContextKeyService.value = this._contextKeyService.createScoped(this.container); - this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(this._scopedContextKeyService.value); // Forward events from inner webview to outer listeners this._webviewEvents.clear(); @@ -156,6 +169,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv this._pendingMessages.forEach(msg => webview.postMessage(msg)); this._pendingMessages.clear(); } + this.container.style.visibility = 'visible'; } @@ -240,12 +254,12 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv showFind() { if (this._webview.value) { this._webview.value.showFind(); - this._findWidgetVisible.set(true); + this._findWidgetVisible?.set(true); } } hideFind() { - this._findWidgetVisible.reset(); + this._findWidgetVisible?.reset(); this._webview.value?.hideFind(); } diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 0f0773fc..726eff39 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -58,6 +58,10 @@ }; const defaultCssRules = ` + html { + scrollbar-color: var(--vscode-scrollbarSlider-background) var(--vscode-editor-background); + } + body { background-color: transparent; color: var(--vscode-editor-foreground); @@ -439,10 +443,18 @@ // propagate focus host.onMessage('focus', () => { - const target = getActiveFrame(); - if (target) { - target.contentWindow.focus(); + const activeFrame = getActiveFrame(); + if (!activeFrame || !activeFrame.contentWindow) { + return; } + + if (document.activeElement === activeFrame) { + // We are already focused on the iframe (or one of its children) so no need + // to refocus. + return; + } + + activeFrame.contentWindow.focus(); }); // update iframe-contents @@ -573,6 +585,8 @@ newFrame.contentWindow.focus(); } + + contentWindow.addEventListener('scroll', handleInnerScroll); contentWindow.addEventListener('wheel', handleWheel); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 0da00297..945817c5 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Dimension } from 'vs/base/browser/dom'; +import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; /** * Set when the find widget in a webview is visible. @@ -145,7 +145,7 @@ export interface WebviewOverlay extends Webview { readonly container: HTMLElement; options: WebviewOptions; - claim(owner: any): void; + claim(owner: any, scopedContextKeyService: IContextKeyService | undefined): void; release(owner: any): void; getInnerWebview(): Webview | undefined; diff --git a/src/vs/workbench/contrib/webview/browser/webviewWindowDragMonitor.ts b/src/vs/workbench/contrib/webview/browser/webviewWindowDragMonitor.ts new file mode 100644 index 00000000..3bbc9e85 --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/webviewWindowDragMonitor.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; + +/** + * Allows webviews to monitor when an element in the VS Code editor is being dragged/dropped. + * + * This is required since webview end up eating the drag event. VS Code needs to see this + * event so it can handle editor element drag drop. + */ +export class WebviewWindowDragMonitor extends Disposable { + constructor(getWebview: () => Webview | undefined) { + super(); + + this._register(DOM.addDisposableListener(window, DOM.EventType.DRAG_START, () => { + getWebview()?.windowDidDragStart(); + })); + + const onDragEnd = () => { + getWebview()?.windowDidDragEnd(); + }; + + this._register(DOM.addDisposableListener(window, DOM.EventType.DRAG_END, onDragEnd)); + this._register(DOM.addDisposableListener(window, DOM.EventType.MOUSE_MOVE, currentEvent => { + if (currentEvent.buttons === 0) { + onDragEnd(); + } + })); + } +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 6ac6a86d..d11fa227 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -8,96 +8,35 @@ import { addDisposableListener } from 'vs/base/browser/dom'; import { ThrottledDelayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { FileAccess, Schemas } from 'vs/base/common/network'; -import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader'; -import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WebviewFindDelegate, WebviewFindWidget } from 'vs/workbench/contrib/webview/browser/webviewFindWidget'; -import { WebviewResourceRequestManager, rewriteVsCodeResourceUrls } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; - -class WebviewKeyboardHandler { - - private readonly _webviews = new Set(); - private readonly _isUsingNativeTitleBars: boolean; - - private readonly webviewMainService: IWebviewManagerService; - - constructor( - configurationService: IConfigurationService, - mainProcessService: IMainProcessService, - ) { - this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; - - this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); - } - - public add(webview: WebviewTag): IDisposable { - this._webviews.add(webview); - - const disposables = new DisposableStore(); - - if (this.shouldToggleMenuShortcutsEnablement) { - this.setIgnoreMenuShortcutsForWebview(webview, true); - } - - disposables.add(addDisposableListener(webview, 'ipc-message', (event) => { - switch (event.channel) { - case 'did-focus': - this.setIgnoreMenuShortcuts(true); - break; - - case 'did-blur': - this.setIgnoreMenuShortcuts(false); - return; - } - })); - - return toDisposable(() => { - disposables.dispose(); - this._webviews.delete(webview); - }); - } - - private get shouldToggleMenuShortcutsEnablement() { - return isMacintosh || this._isUsingNativeTitleBars; - } - - private setIgnoreMenuShortcuts(value: boolean) { - for (const webview of this._webviews) { - this.setIgnoreMenuShortcutsForWebview(webview, value); - } - } - - private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) { - if (this.shouldToggleMenuShortcutsEnablement) { - this.webviewMainService.setIgnoreMenuShortcuts(webview.getWebContentsId(), value); - } - } -} +import { WebviewIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager'; +import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class ElectronWebviewBasedWebview extends BaseWebview implements Webview, WebviewFindDelegate { - private static _webviewKeyboardHandler: WebviewKeyboardHandler | undefined; + private static _webviewKeyboardHandler: WebviewIgnoreMenuShortcutsManager | undefined; private static getWebviewKeyboardHandler( configService: IConfigurationService, mainProcessService: IMainProcessService, ) { if (!this._webviewKeyboardHandler) { - this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService, mainProcessService); + this._webviewKeyboardHandler = new WebviewIgnoreMenuShortcutsManager(configService, mainProcessService); } return this._webviewKeyboardHandler; } @@ -124,6 +63,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme @IConfigurationService configurationService: IConfigurationService, @IMainProcessService mainProcessService: IMainProcessService, @INotificationService noficationService: INotificationService, + @INativeHostService nativeHostService: INativeHostService, ) { super(id, options, contentOptions, extension, _webviewThemeDataProvider, noficationService, _myLogService, telemetryService, environmentService); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts new file mode 100644 index 00000000..5b5e4dd7 --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WebviewTag } from 'electron'; +import { addDisposableListener } from 'vs/base/browser/dom'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; + +export class WebviewIgnoreMenuShortcutsManager { + + private readonly _webviews = new Set(); + private readonly _isUsingNativeTitleBars: boolean; + + private readonly webviewMainService: IWebviewManagerService; + + constructor( + configurationService: IConfigurationService, + mainProcessService: IMainProcessService, + ) { + this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + + this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); + } + + public add(webview: WebviewTag): IDisposable { + this._webviews.add(webview); + + const disposables = new DisposableStore(); + + if (this.shouldToggleMenuShortcutsEnablement) { + this.setIgnoreMenuShortcutsForWebview(webview, true); + } + + disposables.add(addDisposableListener(webview, 'ipc-message', (event) => { + switch (event.channel) { + case WebviewMessageChannels.didFocus: + this.setIgnoreMenuShortcuts(true); + break; + + case WebviewMessageChannels.didBlur: + this.setIgnoreMenuShortcuts(false); + return; + } + })); + + return toDisposable(() => { + disposables.dispose(); + this._webviews.delete(webview); + }); + } + + private get shouldToggleMenuShortcutsEnablement() { + return isMacintosh || this._isUsingNativeTitleBars; + } + + private setIgnoreMenuShortcuts(value: boolean) { + for (const webview of this._webviews) { + this.setIgnoreMenuShortcutsForWebview(webview, value); + } + } + + private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) { + if (this.shouldToggleMenuShortcutsEnablement) { + this.webviewMainService.setIgnoreMenuShortcuts({ webContentsId: webview.getWebContentsId() }, value); + } + } +} diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts index 3b9c5e75..8c010272 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts @@ -6,18 +6,23 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { IRequestService } from 'vs/platform/request/common/request'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading'; +import { WindowIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; /** @@ -31,6 +36,8 @@ export class ElectronIframeWebview extends IFrameWebview { private readonly _focusDelayer = this._register(new ThrottledDelayer(10)); private _elementFocusImpl!: (options?: FocusOptions | undefined) => void; + private readonly _webviewKeyboardHandler: WindowIgnoreMenuShortcutsManager; + constructor( id: string, options: WebviewOptions, @@ -45,17 +52,30 @@ export class ElectronIframeWebview extends IFrameWebview { @IRemoteAuthorityResolverService _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ILogService logService: ILogService, @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @IMainProcessService mainProcessService: IMainProcessService, @INotificationService noficationService: INotificationService, + @INativeHostService nativeHostService: INativeHostService, ) { super(id, options, contentOptions, extension, webviewThemeDataProvider, noficationService, tunnelService, fileService, requestService, telemetryService, environmentService, _remoteAuthorityResolverService, logService); this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options)); + + this._webviewKeyboardHandler = new WindowIgnoreMenuShortcutsManager(configurationService, mainProcessService, nativeHostService); + + this._register(this.on(WebviewMessageChannels.didFocus, () => { + this._webviewKeyboardHandler.didFocus(); + })); + + this._register(this.on(WebviewMessageChannels.didBlur, () => { + this._webviewKeyboardHandler.didBlur(); + })); } protected createElement(options: WebviewOptions, contentOptions: WebviewContentOptions) { const element = super.createElement(options, contentOptions); - this._elementFocusImpl = element.focus.bind(element); + this._elementFocusImpl = () => element.contentWindow?.focus(); element.focus = () => { this.doFocus(); }; diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts new file mode 100644 index 00000000..00da271c --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isMacintosh } from 'vs/base/common/platform'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; + +export class WindowIgnoreMenuShortcutsManager { + + private readonly _isUsingNativeTitleBars: boolean; + + private readonly webviewMainService: IWebviewManagerService; + + constructor( + configurationService: IConfigurationService, + mainProcessService: IMainProcessService, + private readonly nativeHostService: INativeHostService + ) { + this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + + this.webviewMainService = createChannelSender(mainProcessService.getChannel('webview')); + } + + public didFocus(): void { + this.setIgnoreMenuShortcuts(true); + } + + public didBlur(): void { + this.setIgnoreMenuShortcuts(false); + } + + private get shouldToggleMenuShortcutsEnablement() { + return isMacintosh || this._isUsingNativeTitleBars; + } + + protected setIgnoreMenuShortcuts(value: boolean) { + if (this.shouldToggleMenuShortcutsEnablement) { + this.webviewMainService.setIgnoreMenuShortcuts({ windowId: this.nativeHostService.windowId }, value); + } + } +} diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts index 1ec3edc7..1eebb76e 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts @@ -8,18 +8,21 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; -import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor'; +import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; +import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; -import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; export class WebviewEditor extends EditorPane { @@ -35,6 +38,8 @@ export class WebviewEditor extends EditorPane { private readonly _onDidFocusWebview = this._register(new Emitter()); public get onDidFocus(): Event { return this._onDidFocusWebview.event; } + private readonly _scopedContextKeyService = this._register(new MutableDisposable()); + constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @@ -43,6 +48,7 @@ export class WebviewEditor extends EditorPane { @IWorkbenchLayoutService private readonly _workbenchLayoutService: IWorkbenchLayoutService, @IEditorDropService private readonly _editorDropService: IEditorDropService, @IHostService private readonly _hostService: IHostService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, ) { super(WebviewEditor.ID, telemetryService, themeService, storageService); } @@ -51,10 +57,17 @@ export class WebviewEditor extends EditorPane { return this.input instanceof WebviewInput ? this.input.webview : undefined; } + get scopedContextKeyService(): IContextKeyService | undefined { + return this._scopedContextKeyService.value; + } + protected createEditor(parent: HTMLElement): void { const element = document.createElement('div'); this._element = element; + this._element.id = `webview-editor-element-${generateUuid()}`; parent.appendChild(element); + + this._scopedContextKeyService.value = this._contextKeyService.createScoped(element); } public dispose(): void { @@ -139,10 +152,11 @@ export class WebviewEditor extends EditorPane { } private claimWebview(input: WebviewInput): void { - input.webview.claim(this); + input.webview.claim(this, this.scopedContextKeyService); if (this._element) { this._element.setAttribute('aria-flowto', input.webview.container.id); + DOM.setParentFlowTo(input.webview.container, this._element); } this._webviewVisibleDisposables.clear(); @@ -152,19 +166,7 @@ export class WebviewEditor extends EditorPane { containsGroup: (group) => this.group?.id === group.group.id })); - this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_START, () => { - this.webview?.windowDidDragStart(); - })); - - const onDragEnd = () => { - this.webview?.windowDidDragEnd(); - }; - this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_END, onDragEnd)); - this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.MOUSE_MOVE, currentEvent => { - if (currentEvent.buttons === 0) { - onDragEnd(); - } - })); + this._webviewVisibleDisposables.add(new WebviewWindowDragMonitor(() => this.webview)); this.synchronizeWebviewContainerDimensions(input.webview); this._webviewVisibleDisposables.add(this.trackFocus(input.webview)); diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index 41e915c4..d9beb099 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Dimension } from 'vs/base/browser/dom'; +import * as DOM from 'vs/base/browser/dom'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -24,6 +24,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; import { IWebviewService, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor'; import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -70,7 +71,7 @@ export class WebviewViewPane extends ViewPane { this.defaultTitle = this.title; this.memento = new Memento(`webviewView.${this.id}`, storageService); - this.viewState = this.memento.getMemento(StorageScope.WORKSPACE); + this.viewState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); @@ -139,14 +140,14 @@ export class WebviewViewPane extends ViewPane { } if (this._container) { - this._webview.value.layoutWebviewOverElement(this._container, new Dimension(width, height)); + this._webview.value.layoutWebviewOverElement(this._container, new DOM.Dimension(width, height)); } } private updateTreeVisibility() { if (this.isBodyVisible()) { this.activate(); - this._webview.value?.claim(this); + this._webview.value?.claim(this, undefined); } else { this._webview.value?.release(this); } @@ -172,6 +173,9 @@ export class WebviewViewPane extends ViewPane { this._webviewDisposables.add(webview.onDidUpdateState(() => { this.viewState[storageKeys.webviewState] = webview.state; })); + + this._webviewDisposables.add(new WebviewWindowDragMonitor(() => this._webview.value)); + const source = this._webviewDisposables.add(new CancellationTokenSource()); this.withProgress(async () => { diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index 25049bac..ff54b32e 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -36,9 +36,11 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo for (const welcome of contribution.value) { const id = ViewIdentifierMap[welcome.view] ?? welcome.view; const { group, order } = parseGroupAndOrder(welcome, contribution); + const precondition = ContextKeyExpr.deserialize(welcome.enablement); const disposable = viewsRegistry.registerViewWelcomeContent(id, { content: welcome.contents, when: ContextKeyExpr.deserialize(welcome.when), + precondition, group, order }); diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts index ebaae9f7..6e19480f 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts @@ -11,6 +11,7 @@ export enum ViewsWelcomeExtensionPointFields { contents = 'contents', when = 'when', group = 'group', + enablement = 'enablement', } export interface ViewWelcome { @@ -18,6 +19,7 @@ export interface ViewWelcome { readonly [ViewsWelcomeExtensionPointFields.contents]: string; readonly [ViewsWelcomeExtensionPointFields.when]: string; readonly [ViewsWelcomeExtensionPointFields.group]: string; + readonly [ViewsWelcomeExtensionPointFields.enablement]: string; } export type ViewsWelcomeExtensionPoint = ViewWelcome[]; @@ -64,6 +66,10 @@ const viewsWelcomeExtensionPointSchema = Object.freeze(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(GettingStartedInput.ID, GettingStartedInputFactory); + +if (product.quality !== 'stable') { + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ + ...workbenchConfigurationNodeBase, + properties: { + 'workbench.experimental.gettingStarted': { + type: 'boolean', + description: localize('gettingStartedDescription', "Enables an experimental Getting Started page, accesible via the Help menu."), + default: false, + } + } + }); +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css new file mode 100644 index 00000000..7c199b89 --- /dev/null +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -0,0 +1,248 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.file-icons-enabled .show-file-icons .vs_code_editor_getting_started\.md-name-file-icon.md-ext-file-icon.ext-file-icon.markdown-lang-file-icon.file-icon::before { + content: ' '; + background-image: url('../../../../browser/media/code-icon.svg'); +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide { + width: 100%; + height: 100%; + position: absolute; + transition: left 0.25s, opacity 0.25s; + left: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories { + height: calc(100% - 50px); + display: flex; + flex-direction: column; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .gap { + flex: 150px 0 1 +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .header { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-title { + margin-bottom: 4px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-description-container { + width: 100% +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-progress { + margin-top: 12px; + font-size: 12px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories progress { + font-size: 12pt; + width: 100%; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories #getting-started-categories-container { + display: flex; + flex-wrap: wrap; + justify-content: center; + max-width: 900px; + margin: 32px auto; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .getting-started-category { + width: 330px; + min-height: 80px; + margin: 12px; + text-align: left; + display: flex; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .getting-started-category { + padding-right: 46px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .getting-started-category .codicon { + margin-right: 10px; + font-size: 32px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category { + width: 330px; + display: flex; + padding: 40px 0 20px; + margin-left: 12px; + min-height: auto; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category .codicon { + margin-left:0; + font-size: 22pt; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail #getting-started-detail-columns { + display: flex; + height: 100%; + justify-content: center; + padding: 44px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task { + display: flex; + width: 100%; + overflow: hidden; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .task-description, +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .actions, +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) button, +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) a { + display: none; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task.expanded { + width: 100%; + height: unset; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-title { + font-size: 14pt; +} + + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-description { + margin-top: 4px; + font-size: 13px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .actions { + margin-top: 12px; + display: flex; + align-items: center; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-next { + margin-left: auto; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon.hidden { + display: none; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon { + margin-right: 8px; + font-size: 20px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task-action { + padding: 6px 12px; + font-size: 11pt; + min-width: 100px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail #getting-started-detail-left { + min-width: 330px; + width: 40%; + max-width: 400px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail #getting-started-detail-right { + width: 66%; + text-align: center; + padding-left: 44px; + margin-bottom: 50px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { + border: none; + color: inherit; + text-align: left; + padding: 16px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:not(:first-child) { + margin-top: 12px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { + cursor: pointer; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:focus { + outline-style: solid; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button { + position: absolute; + left: 0; + font-size: 12pt; + margin: 10px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button:hover { + cursor: pointer; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button .codicon { + position: relative; + top: 2px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .skip { + display: block; + width: 150px; + margin: 0 auto; + text-align: center; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h1 { + font-size: 32px; + font-weight: normal; + border-bottom: none; + margin: 0; + padding: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h2 { + font-weight: normal; + margin: 0 0 4px 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h3 { + font-size: 18px; + font-weight: bold; + margin: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .subtitle { + font-size: 16px; + margin: 0; + padding: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .footer { + position: absolute; + text-align: center; + bottom: 0; + width: calc(100% - 40px); + margin-bottom: 20px; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.next { + left: 100%; + opacity: 0; +} + +.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.prev { + left: -100%; + opacity: 0; +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts new file mode 100644 index 00000000..637330e0 --- /dev/null +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -0,0 +1,430 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./gettingStarted'; +import 'vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started'; +import { localize } from 'vs/nls'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { IEditorInputFactory } from 'vs/workbench/common/editor'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { assertIsDefined } from 'vs/base/common/types'; +import { $, addDisposableListener } from 'vs/base/browser/dom'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IGettingStartedCategoryWithProgress, IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { buttonBackground as welcomeButtonBackground, buttonHoverBackground as welcomeButtonHoverBackground, welcomePageBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors'; +import { activeContrastBorder, buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, descriptionForeground, focusBorder, foreground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +export const gettingStartedInputTypeId = 'workbench.editors.gettingStartedInput'; +const telemetryFrom = 'gettingStartedPage'; + +export class GettingStartedInput extends WalkThroughInput { + static readonly ID = gettingStartedInputTypeId; + + selectedCategory: string | undefined; + selectedTask: string | undefined; +} + +export class GettingStartedPage extends Disposable { + readonly editorInput: GettingStartedInput; + private inProgressScroll = Promise.resolve(); + + private dispatchListeners = new DisposableStore(); + + private gettingStartedCategories: IGettingStartedCategoryWithProgress[]; + private currentCategory: IGettingStartedCategoryWithProgress | undefined; + + constructor( + initialState: { selectedCategory?: string, selectedTask?: string }, + @IEditorService private readonly editorService: IEditorService, + @ICommandService private readonly commandService: ICommandService, + @IProductService private readonly productService: IProductService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IGettingStartedService private readonly gettingStartedService: IGettingStartedService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(); + + const resource = FileAccess.asBrowserUri('./vs_code_editor_getting_started.md', require) + .with({ + scheme: Schemas.walkThrough, + query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started' }) + }); + + + this.editorInput = this.instantiationService.createInstance(GettingStartedInput, { + typeId: gettingStartedInputTypeId, + name: localize('editorGettingStarted.title', "Getting Started"), + resource, + telemetryFrom, + onReady: (container: HTMLElement) => this.onReady(container) + }); + + this.editorInput.selectedCategory = initialState.selectedCategory; + this.editorInput.selectedTask = initialState.selectedTask; + + this.gettingStartedCategories = this.gettingStartedService.getCategories(); + this._register(this.dispatchListeners); + this._register(this.gettingStartedService.onDidAddTask(task => console.log('added new task', task, 'that isnt being rendered yet'))); + this._register(this.gettingStartedService.onDidAddCategory(category => console.log('added new category', category, 'that isnt being rendered yet'))); + this._register(this.gettingStartedService.onDidProgressTask(task => { + const category = this.gettingStartedCategories.find(category => category.id === task.category); + if (!category) { throw Error('Could not find category with ID: ' + task.category); } + if (category.content.type !== 'items') { throw Error('internaal error: progressing task in a non-items category'); } + const ourTask = category.content.items.find(_task => _task.id === task.id); + if (!ourTask) { + throw Error('Could not find task with ID: ' + task.id); + } + ourTask.done = task.done; + if (category.id === this.currentCategory?.id) { + const badgeelement = assertIsDefined(document.getElementById('done-task-' + task.id)); + if (task.done) { + badgeelement.classList.remove('codicon-star-empty'); + badgeelement.classList.add('codicon-star-full'); + } + else { + badgeelement.classList.add('codicon-star-empty'); + badgeelement.classList.remove('codicon-star-full'); + } + } + this.updateCategoryProgress(); + })); + } + + public openEditor(options: IEditorOptions = { pinned: true }) { + return this.editorService.openEditor(this.editorInput, options); + } + + private registerDispatchListeners(container: HTMLElement) { + this.dispatchListeners.clear(); + + container.querySelectorAll('[x-dispatch]').forEach(element => { + const [command, argument] = (element.getAttribute('x-dispatch') ?? '').split(':'); + if (command) { + this.dispatchListeners.add(addDisposableListener(element, 'click', (e) => { + + type GettingStartedActionClassification = { + command: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + argument: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + }; + type GettingStartedActionEvent = { + command: string; + argument: string | undefined; + }; + this.telemetryService.publicLog2('gettingStarted.ActionExecuted', { command, argument }); + + switch (command) { + case 'scrollPrev': { + this.scrollPrev(container); + break; + } + case 'skip': { + this.commandService.executeCommand('workbench.action.closeActiveEditor'); + break; + } + case 'selectCategory': { + const selectedCategory = this.gettingStartedCategories.find(category => category.id === argument); + if (!selectedCategory) { throw Error('Could not find category with ID ' + argument); } + if (selectedCategory.content.type === 'command') { + this.commandService.executeCommand(selectedCategory.content.command); + } else { + this.scrollToCategory(container, argument); + } + break; + } + case 'selectTask': { + this.selectTask(argument); + e.stopPropagation(); + break; + } + case 'runTaskAction': { + if (!this.currentCategory || this.currentCategory.content.type !== 'items') { + throw Error('cannot run task action for category of non items type' + this.currentCategory?.id); + } + const taskToRun = assertIsDefined(this.currentCategory?.content.items.find(task => task.id === argument)); + const commandToRun = assertIsDefined(taskToRun.button?.command); + this.commandService.executeCommand(commandToRun); + break; + } + default: { + console.error('Dispatch to', command, argument, 'not defined'); + break; + } + } + })); + } + }); + } + + private selectTask(id: string | undefined) { + const mediaElement = assertIsDefined(document.getElementById('getting-started-media')); + if (id) { + const taskElement = assertIsDefined(document.getElementById('getting-started-task-' + id)); + if (!this.currentCategory || this.currentCategory.content.type !== 'items') { + throw Error('cannot expand task for category of non items type' + this.currentCategory?.id); + } + this.editorInput.selectedTask = id; + const taskToExpand = assertIsDefined(this.currentCategory.content.items.find(task => task.id === id)); + + mediaElement.setAttribute('src', taskToExpand.media.path.toString()); + mediaElement.setAttribute('alt', taskToExpand.media.altText); + taskElement.parentElement?.querySelectorAll('.expanded').forEach(node => node.classList.remove('expanded')); + taskElement.classList.add('expanded'); + } else { + mediaElement.setAttribute('src', ''); + mediaElement.setAttribute('alt', ''); + } + } + + private onReady(container: HTMLElement) { + const categoryElements = this.gettingStartedCategories.map( + category => { + const categoryDescriptionElement = + category.content.type === 'items' ? + $('.category-description-container', {}, + $('h3.category-title', {}, category.title), + $('.category-description.description', {}, category.description), + $('.category-progress', { 'x-data-category-id': category.id, }, $('.message'), $('progress'))) : + $('.category-description-container', {}, + $('h3.category-title', {}, category.title), + $('.category-description.description', {}, category.description)); + + return $('button.getting-started-category', + { 'x-dispatch': 'selectCategory:' + category.id }, + $('.codicon.codicon-' + category.codicon, {}), categoryDescriptionElement); + }); + + const rightColumn = assertIsDefined(container.querySelector('#getting-started-detail-right')); + rightColumn.appendChild($('img#getting-started-media')); + + const categoriesContainer = assertIsDefined(document.getElementById('getting-started-categories-container')); + categoryElements.forEach(element => { + categoriesContainer.appendChild(element); + }); + + this.updateCategoryProgress(); + + assertIsDefined(document.getElementById('product-name')).textContent = this.productService.nameLong; + this.registerDispatchListeners(container); + + const categoriesSlide = assertIsDefined(document.getElementById('gettingStartedSlideCategory')); + const tasksSlide = assertIsDefined(document.getElementById('gettingStartedSlideDetails')); + + if (this.editorInput.selectedCategory) { + this.currentCategory = this.gettingStartedCategories.find(category => category.id === this.editorInput.selectedCategory); + if (!this.currentCategory) { + throw Error('Could not restore to category ' + this.editorInput.selectedCategory + ' as it was not found'); + } + this.buildCategorySlide(container, this.editorInput.selectedCategory, this.editorInput.selectedTask); + categoriesSlide.classList.add('prev'); + } else { + tasksSlide.classList.add('next'); + } + } + + private updateCategoryProgress() { + document.querySelectorAll('.category-progress').forEach(element => { + const categoryID = element.getAttribute('x-data-category-id'); + const category = this.gettingStartedCategories.find(category => category.id === categoryID); + if (!category) { throw Error('Could not find c=ategory with ID ' + categoryID); } + if (category.content.type !== 'items') { throw Error('Category with ID ' + categoryID + ' is not of items type'); } + const numDone = category.content.items.filter(task => task.done).length; + const numTotal = category.content.items.length; + + const message = assertIsDefined(element.firstChild); + const bar = assertIsDefined(element.lastChild) as HTMLProgressElement; + bar.value = numDone; + bar.max = numTotal; + if (numTotal === numDone) { + message.textContent = `All items complete!`; + } + else { + message.textContent = `${numDone} of ${numTotal} items complete`; + } + }); + } + + private async scrollToCategory(container: HTMLElement, categoryID: string) { + this.inProgressScroll = this.inProgressScroll.then(async () => { + this.clearDetialView(); + this.editorInput.selectedCategory = categoryID; + this.currentCategory = this.gettingStartedCategories.find(category => category.id === categoryID); + const slides = [...container.querySelectorAll('.gettingStartedSlide').values()]; + const currentSlide = slides.findIndex(element => !element.classList.contains('prev') && !element.classList.contains('next')); + if (currentSlide < slides.length - 1) { + this.buildCategorySlide(container, categoryID); + slides[currentSlide].classList.add('prev'); + slides[currentSlide + 1].classList.remove('next'); + } + }); + } + + private buildCategorySlide(container: HTMLElement, categoryID: string, selectedItem?: string) { + const category = this.gettingStartedCategories.find(category => category.id === categoryID); + if (!category) { throw Error('could not find category with ID ' + categoryID); } + if (category.content.type !== 'items') { throw Error('category with ID ' + categoryID + ' is not of items type'); } + + const detailTitle = assertIsDefined(document.getElementById('getting-started-detail-title')); + detailTitle.appendChild( + $('.getting-started-category', + {}, + $('.codicon.codicon-' + category.codicon, {}), + $('.category-description-container', {}, + $('h2.category-title', {}, category.title), + $('.category-description.description', {}, category.description)))); + + const categoryElements = category.content.items.map( + (task, i, arr) => $('button.getting-started-task', + { 'x-dispatch': 'selectTask:' + task.id, id: 'getting-started-task-' + task.id }, + $('.codicon' + (task.done ? '.codicon-pass-filled' : '.codicon-circle-large-outline'), { id: 'done-task-' + task.id }), + $('.task-description-container', {}, + $('h3.task-title', {}, task.title), + $('.task-description.description', {}, task.description), + $('.actions', {}, + ...( + task.button + ? [$('button.emphasis.getting-started-task-action', { 'x-dispatch': 'runTaskAction:' + task.id }, + task.button.title + this.getKeybindingLabel(task.button.command) + )] + : []), + ...( + arr[i + 1] + ? [ + $('a.task-next', + { 'x-dispatch': 'selectTask:' + arr[i + 1].id }, localize('next', "Next")), + ] : [] + )) + ))); + + const detailContainer = assertIsDefined(document.getElementById('getting-started-detail-container')); + categoryElements.forEach(element => detailContainer.appendChild(element)); + + const toExpand = category.content.items.find(item => !item.done) ?? category.content.items[0]; + this.selectTask(selectedItem ?? toExpand.id); + this.registerDispatchListeners(container); + } + + private clearDetialView() { + const detailContainer = assertIsDefined(document.getElementById('getting-started-detail-container')); + while (detailContainer.firstChild) { detailContainer.removeChild(detailContainer.firstChild); } + const detailTitle = assertIsDefined(document.getElementById('getting-started-detail-title')); + while (detailTitle.firstChild) { detailTitle.removeChild(detailTitle.firstChild); } + } + + private getKeybindingLabel(command: string) { + const binding = this.keybindingService.lookupKeybinding(command); + if (!binding) { return ''; } + else { return ` (${binding.getLabel()})`; } + } + + private async scrollPrev(container: HTMLElement) { + this.inProgressScroll = this.inProgressScroll.then(async () => { + this.currentCategory = undefined; + this.editorInput.selectedCategory = undefined; + this.editorInput.selectedTask = undefined; + this.selectTask(undefined); + const slides = [...container.querySelectorAll('.gettingStartedSlide').values()]; + const currentSlide = slides.findIndex(element => + !element.classList.contains('prev') && !element.classList.contains('next')); + if (currentSlide > 0) { + slides[currentSlide].classList.add('next'); + assertIsDefined(slides[currentSlide - 1]).classList.remove('prev'); + } + }); + } +} + +export class GettingStartedInputFactory implements IEditorInputFactory { + public canSerialize(editorInput: GettingStartedInput): boolean { + return true; + } + + public serialize(editorInput: GettingStartedInput): string { + return JSON.stringify({ selectedCategory: editorInput.selectedCategory, selectedTask: editorInput.selectedTask }); + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): GettingStartedInput { + try { + const { selectedCategory, selectedTask } = JSON.parse(serializedEditorInput); + return instantiationService.createInstance(GettingStartedPage, { selectedCategory, selectedTask }).editorInput; + } catch { } + return instantiationService.createInstance(GettingStartedPage, {}).editorInput; + } +} + +registerThemingParticipant((theme, collector) => { + const backgroundColor = theme.getColor(welcomePageBackground); + if (backgroundColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .welcomePageContainer { background-color: ${backgroundColor}; }`); + } + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer { color: ${foregroundColor}; }`); + } + const descriptionColor = theme.getColor(descriptionForeground); + if (descriptionColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .description { color: ${descriptionColor}; }`); + } + const buttonColor = getExtraColor(theme, welcomeButtonBackground, { dark: 'rgba(0, 0, 0, .2)', extra_dark: 'rgba(200, 235, 255, .042)', light: 'rgba(0,0,0,.04)', hc: 'black' }); + if (buttonColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { background: ${buttonColor}; }`); + } + + const buttonHoverColor = getExtraColor(theme, welcomeButtonHoverBackground, { dark: 'rgba(200, 235, 255, .072)', extra_dark: 'rgba(200, 235, 255, .072)', light: 'rgba(0,0,0,.10)', hc: null }); + if (buttonHoverColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { background: ${buttonHoverColor}; }`); + } + if (buttonColor && buttonHoverColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.expanded:hover { background: ${buttonColor}; }`); + } + + const emphasisButtonForeground = theme.getColor(buttonForeground); + if (emphasisButtonForeground) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis { color: ${emphasisButtonForeground}; }`); + } + + const emphasisButtonBackground = theme.getColor(buttonBackground); + if (emphasisButtonBackground) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis { background: ${emphasisButtonBackground}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .getting-started-category .codicon { color: ${emphasisButtonBackground} }`); + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon { color: ${emphasisButtonBackground} } `); + } + + const emphasisButtonHoverBackground = theme.getColor(buttonHoverBackground); + if (emphasisButtonHoverBackground) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis:hover { background: ${emphasisButtonHoverBackground}; }`); + } + + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a { color: ${link}; }`); + } + const activeLink = theme.getColor(textLinkActiveForeground); + if (activeLink) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:hover, + .monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:active { color: ${activeLink}; }`); + } + const focusColor = theme.getColor(focusBorder); + if (focusColor) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:focus { outline-color: ${focusColor}; }`); + } + const border = theme.getColor(contrastBorder); + if (border) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { border-color: ${border}; border: 1px solid; }`); + } + const activeBorder = theme.getColor(activeContrastBorder); + if (activeBorder) { + collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { outline-color: ${activeBorder}; }`); + } +}); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started.ts new file mode 100644 index 00000000..dd625452 --- /dev/null +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { escape } from 'vs/base/common/strings'; +import { localize } from 'vs/nls'; + +export default () => ` +
+
+
+
+
+

${escape(localize('gettingStarted.vscode', "Visual Studio Code"))}

+

${escape(localize({ key: 'gettingStarted.editingRedefined', comment: ['Shown as subtitle on the Welcome page.'] }, "Code editing. Redefined"))}

+
+
+
+
+
+ Back +
+
+
+
+
+
+
+
+
+ +
+
+`.replace(/\|/g, '`'); diff --git a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts index 6af6a4b7..a405271a 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts @@ -19,9 +19,8 @@ export default () => `

${escape(localize('welcomePage.start', "Start"))}

diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts index 396871dc..1a59cbb1 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts @@ -13,6 +13,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import product from 'vs/platform/product/common/product'; Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ @@ -21,13 +22,21 @@ Registry.as(ConfigurationExtensions.Configuration) 'workbench.startupEditor': { 'scope': ConfigurationScope.APPLICATION, // Make sure repositories cannot trigger opening a README for tracking. 'type': 'string', - 'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], - 'enumDescriptions': [ + 'enum': [ + ...['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], + ...(product.quality !== 'stable' + ? ['gettingStarted'] + : []) + ], + 'enumDescriptions': [...[ localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page (default)."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty workspace)."), - localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."),], + ...(product.quality !== 'stable' + ? [localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStarted' }, "Open the Getting Started page (experimental).")] + : []) ], 'default': 'welcomePage', 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.") diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 4a61a79f..37a7512d 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -15,7 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; import { Action, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -29,7 +29,7 @@ import { ILifecycleService, StartupKind } from 'vs/workbench/services/lifecycle/ import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; @@ -46,6 +46,8 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { gettingStartedInputTypeId, GettingStartedPage } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted'; +import { buttonBackground, buttonHoverBackground, welcomePageBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors'; const configurationKey = 'workbench.startupEditor'; const oldConfigurationKey = 'workbench.welcome.enabled'; @@ -69,7 +71,8 @@ export class WelcomePageContribution implements IWorkbenchContribution { backupFileService.hasBackups().then(hasBackups => { // Open the welcome even if we opened a set of default editors if ((!editorService.activeEditor || layoutService.openedDefaultEditors) && !hasBackups) { - const openWithReadme = configurationService.getValue(configurationKey) === 'readme'; + const startupEditorSetting = configurationService.getValue(configurationKey) as string; + const openWithReadme = startupEditorSetting === 'readme'; if (openWithReadme) { return Promise.all(contextService.getWorkspace().folders.map(folder => { const folderUri = folder.uri; @@ -101,18 +104,23 @@ export class WelcomePageContribution implements IWorkbenchContribution { return undefined; }); } else { + const startupEditorTypeID = startupEditorSetting === 'gettingStarted' ? gettingStartedInputTypeId : welcomeInputTypeId; + const launchEditor = startupEditorSetting === 'gettingStarted' + ? instantiationService.createInstance(GettingStartedPage, {}) + : instantiationService.createInstance(WelcomePage); + let options: IEditorOptions; let editor = editorService.activeEditor; if (editor) { // Ensure that the welcome editor won't get opened more than once - if (editor.getTypeId() === welcomeInputTypeId || editorService.editors.some(e => e.getTypeId() === welcomeInputTypeId)) { + if (editor.getTypeId() === startupEditorTypeID || editorService.editors.some(e => e.getTypeId() === startupEditorTypeID)) { return undefined; } options = { pinned: false, index: 0 }; } else { options = { pinned: false }; } - return instantiationService.createInstance(WelcomePage).openEditor(options); + return launchEditor.openEditor(options); } } return undefined; @@ -129,7 +137,7 @@ function isWelcomePageEnabled(configurationService: IConfigurationService, conte return welcomeEnabled.value; } } - return startupEditor.value === 'welcomePage' || startupEditor.value === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; + return startupEditor.value === 'welcomePage' || startupEditor.value === 'gettingStarted' || startupEditor.value === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; } export class WelcomePageAction extends Action { @@ -323,7 +331,7 @@ class WelcomePage extends Disposable { showOnStartup.setAttribute('checked', 'checked'); } showOnStartup.addEventListener('click', e => { - this.configurationService.updateValue(configurationKey, showOnStartup.checked ? 'welcomePage' : 'newUntitledFile', ConfigurationTarget.USER); + this.configurationService.updateValue(configurationKey, showOnStartup.checked ? 'welcomePage' : 'newUntitledFile'); }); const prodName = container.querySelector('.welcomePage .title .caption') as HTMLElement; @@ -637,10 +645,6 @@ export class WelcomeInputFactory implements IEditorInputFactory { // theming -export const buttonBackground = registerColor('welcomePage.buttonBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonBackground', 'Background color for the buttons on the Welcome page.')); -export const buttonHoverBackground = registerColor('welcomePage.buttonHoverBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonHoverBackground', 'Hover background color for the buttons on the Welcome page.')); -export const welcomePageBackground = registerColor('welcomePage.background', { light: null, dark: null, hc: null }, localize('welcomePage.background', 'Background color for the Welcome page.')); - registerThemingParticipant((theme, collector) => { const backgroundColor = theme.getColor(welcomePageBackground); if (backgroundColor) { diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePageColors.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePageColors.ts new file mode 100644 index 00000000..e87483cf --- /dev/null +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePageColors.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; + +// Seprate from main module to break dependency cycles between welcomePage and gettingStarted. +export const buttonBackground = registerColor('welcomePage.buttonBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonBackground', 'Background color for the buttons on the Welcome page.')); +export const buttonHoverBackground = registerColor('welcomePage.buttonHoverBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonHoverBackground', 'Hover background color for the buttons on the Welcome page.')); +export const welcomePageBackground = registerColor('welcomePage.background', { light: null, dark: null, hc: null }, localize('welcomePage.background', 'Background color for the Welcome page.')); diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts index 6b2fa23f..3cf7db6d 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -19,7 +19,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution { @@ -28,7 +27,6 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution constructor( @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IOpenerService private readonly openerService: IOpenerService, @INotificationService private readonly notificationService: INotificationService, @IHostService private readonly hostService: IHostService, @@ -40,7 +38,6 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution @IEnvironmentService private readonly environmentService: IEnvironmentService, @IJSONEditingService private readonly jsonEditingService: IJSONEditingService ) { - storageKeysSyncRegistryService.registerStorageKey({ key: AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, version: 1 }); } protected async handleTelemetryOptOut(): Promise { @@ -53,7 +50,7 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution return; // return early if meanwhile another window opened (we only show the opt-out once) } - this.storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL); + this.storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL, StorageTarget.USER); this.privacyUrl = this.productService.privacyStatementUrl || this.productService.telemetryOptOutUrl; @@ -165,7 +162,6 @@ export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IOpenerService openerService: IOpenerService, @INotificationService notificationService: INotificationService, @IHostService hostService: IHostService, @@ -177,7 +173,7 @@ export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut { @IEnvironmentService environmentService: IEnvironmentService, @IJSONEditingService jsonEditingService: IJSONEditingService ) { - super(storageService, storageKeysSyncRegistryService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); + super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); this.handleTelemetryOptOut(); } diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts index 60c4fcfa..81b28a10 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.ts @@ -16,13 +16,11 @@ import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryO import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { constructor( @IStorageService storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IOpenerService openerService: IOpenerService, @INotificationService notificationService: INotificationService, @IHostService hostService: IHostService, @@ -35,7 +33,7 @@ export class NativeTelemetryOptOut extends AbstractTelemetryOptOut { @IJSONEditingService jsonEditingService: IJSONEditingService, @INativeHostService private readonly nativeHostService: INativeHostService ) { - super(storageService, storageKeysSyncRegistryService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); + super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService); this.handleTelemetryOptOut(); } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 04a605f1..7db8ceec 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -40,7 +40,7 @@ export class EditorWalkThroughAction extends Action { public run(): Promise { const input = this.instantiationService.createInstance(WalkThroughInput, inputOptions); - return this.editorService.openEditor(input, { pinned: true }) + return this.editorService.openEditor(input, { pinned: true, override: false }) .then(() => void (0)); } } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css index e2a46b37..0bdf1be5 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.css @@ -7,6 +7,7 @@ box-sizing: border-box; padding: 10px 20px; line-height: 22px; + height: inherit; user-select: initial; -webkit-user-select: initial; } diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index f98b0050..5f159a16 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -45,12 +45,16 @@ import { ConfigurationCache } from 'vs/workbench/services/configuration/electron import { SignService } from 'vs/platform/sign/node/signService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; -import { basename } from 'vs/base/common/resources'; +import { basename } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; import { NativeLogService } from 'vs/workbench/services/log/electron-browser/logService'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { KeyboardLayoutService } from 'vs/workbench/services/keybinding/electron-sandbox/nativeKeyboardLayout'; +import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout'; class DesktopMain extends Disposable { @@ -232,8 +236,11 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Provider - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(this.environmentService.appSettingsHome, this.configuration.backupPath ? URI.file(this.configuration.backupPath) : undefined, diskFileSystemProvider, this.environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, logService)); + // Uri Identity + const uriIdentityService = new UriIdentityService(fileService); + serviceCollection.set(IUriIdentityService, uriIdentityService); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // @@ -257,7 +264,7 @@ class DesktopMain extends Disposable { const payload = await this.resolveWorkspaceInitializationPayload(); const services = await Promise.all([ - this.createWorkspaceService(payload, fileService, remoteAgentService, logService).then(service => { + this.createWorkspaceService(payload, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -273,6 +280,14 @@ class DesktopMain extends Disposable { // Storage serviceCollection.set(IStorageService, service); + return service; + }), + + this.createKeyboardLayoutService(logService, mainProcessService).then(service => { + + // KeyboardLayout + serviceCollection.set(IKeyboardLayoutService, service); + return service; }) ]); @@ -310,8 +325,8 @@ class DesktopMain extends Disposable { // Fallback to empty workspace if we have no payload yet. if (!workspaceInitializationPayload) { let id: string; - if (this.environmentService.backupWorkspaceHome) { - id = basename(this.environmentService.backupWorkspaceHome); // we know the backupPath must be a unique path so we leverage its name as workspace ID + if (this.configuration.backupPath) { + id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID } else if (this.environmentService.isExtensionDevelopment) { id = 'ext-dev'; // extension development window never stores backups and is a singleton } else { @@ -334,10 +349,12 @@ class DesktopMain extends Disposable { } catch (error) { onUnexpectedError(error); } + return; } private async createHash(resource: URI): Promise { + // Return early the folder is not local if (resource.scheme !== Schemas.file) { return createHash('md5').update(resource.toString()).digest('hex'); @@ -362,8 +379,8 @@ class DesktopMain extends Disposable { return createHash('md5').update(resource.fsPath).update(ctime ? String(ctime) : '').digest('hex'); } - private async createWorkspaceService(payload: IWorkspaceInitializationPayload, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { - const workspaceService = new WorkspaceService({ remoteAuthority: this.environmentService.remoteAuthority, configurationCache: new ConfigurationCache(this.environmentService) }, this.environmentService, fileService, remoteAgentService, logService); + private async createWorkspaceService(payload: IWorkspaceInitializationPayload, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService({ remoteAuthority: this.environmentService.remoteAuthority, configurationCache: new ConfigurationCache(this.environmentService) }, this.environmentService, fileService, remoteAgentService, uriIdentityService, logService); try { await workspaceService.initialize(payload); @@ -393,6 +410,20 @@ class DesktopMain extends Disposable { } } + private async createKeyboardLayoutService(logService: ILogService, mainProcessService: IMainProcessService): Promise { + const keyboardLayoutService = new KeyboardLayoutService(mainProcessService); + + try { + await keyboardLayoutService.initialize(); + + return keyboardLayoutService; + } catch (error) { + onUnexpectedError(error); + logService.error(error); + + return keyboardLayoutService; + } + } } export function main(configuration: INativeWorkbenchConfiguration): Promise { diff --git a/src/vs/workbench/electron-sandbox/actions/developerActions.ts b/src/vs/workbench/electron-sandbox/actions/developerActions.ts index 1753e42e..8daa9ffd 100644 --- a/src/vs/workbench/electron-sandbox/actions/developerActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/developerActions.ts @@ -42,6 +42,9 @@ export class ConfigureRuntimeArgumentsAction extends Action { } async run(): Promise { - await this.editorService.openEditor({ resource: this.environmentService.argvResource }); + await this.editorService.openEditor({ + resource: this.environmentService.argvResource, + options: { pinned: true } + }); } } diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 8e5d43e8..63bc327f 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -211,16 +211,17 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; }, 'window.restoreWindows': { 'type': 'string', - 'enum': ['all', 'folders', 'one', 'none'], + 'enum': ['preserve', 'all', 'folders', 'one', 'none'], 'enumDescriptions': [ - nls.localize('window.reopenFolders.all', "Reopen all windows."), - nls.localize('window.reopenFolders.folders', "Reopen all folders. Empty workspaces will not be restored."), - nls.localize('window.reopenFolders.one', "Reopen the last active window."), - nls.localize('window.reopenFolders.none', "Never reopen a window. Always start with an empty one.") + nls.localize('window.reopenFolders.preserve', "Always reopen all windows. If a folder or workspace is opened (e.g. from the command line) it opens as a new window unless it was opened before. If files are opened they will open in one of the restored windows."), + nls.localize('window.reopenFolders.all', "Reopen all windows unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.folders', "Reopen all windows that had folders or workspaces opened unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.one', "Reopen the last active window unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.none', "Never reopen a window. Unless a folder or workspace is opened (e.g. from the command line), an empty window will appear.") ], 'default': 'all', 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart.") + 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after starting for the first time. This setting has no effect when the application is already running.") }, 'window.restoreFullscreen': { 'type': 'boolean', @@ -295,7 +296,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; }, 'window.enableExperimentalProxyLoginDialog': { 'type': 'boolean', - 'default': false, + 'default': true, 'scope': ConfigurationScope.APPLICATION, 'description': nls.localize('window.enableExperimentalProxyLoginDialog', "Enables a new login dialog for proxy authentication. Requires a restart to take effect."), } diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 891df171..eddd5ab6 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -34,6 +34,8 @@ import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHos import { SimpleConfigurationService, simpleFileSystemProvider, SimpleLogService, SimpleRemoteAgentService, SimpleSignService, SimpleStorageService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; class DesktopMain extends Disposable { @@ -205,7 +207,11 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.file, simpleFileSystemProvider); // User Data Provider - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(URI.file('user-home'), undefined, simpleFileSystemProvider, this.environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(Schemas.file, simpleFileSystemProvider, Schemas.userData, logService)); + + // Uri Identity + const uriIdentityService = new UriIdentityService(fileService); + serviceCollection.set(IUriIdentityService, uriIdentityService); const connection = remoteAgentService.getConnection(); if (connection) { diff --git a/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts new file mode 100644 index 00000000..5ed67ad6 --- /dev/null +++ b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IDialogHandler, IDialogResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IDialogsModel, IDialogViewItem } from 'vs/workbench/common/dialogs'; +import { BrowserDialogHandler } from 'vs/workbench/browser/parts/dialogs/dialogHandler'; +import { NativeDialogHandler } from 'vs/workbench/electron-sandbox/parts/dialogs/dialogHandler'; +import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution { + private nativeImpl: IDialogHandler; + private browserImpl: IDialogHandler; + + private model: IDialogsModel; + private currentDialog: IDialogViewItem | undefined; + + constructor( + @IConfigurationService private configurationService: IConfigurationService, + @IDialogService private dialogService: IDialogService, + @ILogService logService: ILogService, + @ILayoutService layoutService: ILayoutService, + @IThemeService themeService: IThemeService, + @IKeybindingService keybindingService: IKeybindingService, + @IProductService productService: IProductService, + @IClipboardService clipboardService: IClipboardService, + @INativeHostService nativeHostService: INativeHostService + ) { + super(); + + this.browserImpl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService); + this.nativeImpl = new NativeDialogHandler(logService, nativeHostService, productService, clipboardService); + + this.model = (this.dialogService as DialogService).model; + + this._register(this.model.onDidShowDialog(() => { + if (!this.currentDialog) { + this.processDialogs(); + } + })); + + this.processDialogs(); + } + + private async processDialogs(): Promise { + while (this.model.dialogs.length) { + this.currentDialog = this.model.dialogs[0]; + + let result: IDialogResult | undefined = undefined; + + // Confirm + if (this.currentDialog.args.confirmArgs) { + const args = this.currentDialog.args.confirmArgs; + result = this.useCustomDialog ? await this.browserImpl.confirm(args.confirmation) : await this.nativeImpl.confirm(args.confirmation); + } + + // Input (custom only) + else if (this.currentDialog.args.inputArgs) { + const args = this.currentDialog.args.inputArgs; + result = await this.browserImpl.input(args.severity, args.message, args.buttons, args.inputs, args.options); + } + + // Message + else if (this.currentDialog.args.showArgs) { + const args = this.currentDialog.args.showArgs; + result = this.useCustomDialog ? + await this.browserImpl.show(args.severity, args.message, args.buttons, args.options) : + await this.nativeImpl.show(args.severity, args.message, args.buttons, args.options); + } + + // About + else { + await this.nativeImpl.about(); + } + + this.currentDialog.close(result); + this.currentDialog = undefined; + } + } + + private get useCustomDialog(): boolean { + return this.configurationService.getValue('window.dialogStyle') === 'custom'; + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts b/src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts similarity index 74% rename from src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts rename to src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts index c6e5fb62..f56a9738 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts +++ b/src/vs/workbench/electron-sandbox/parts/dialogs/dialogHandler.ts @@ -4,22 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import Severity from 'vs/base/common/severity'; -import { isLinux, isWindows } from 'vs/base/common/platform'; -import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IShowResult, IInputResult, IInput } from 'vs/platform/dialogs/common/dialogs'; -import { DialogService as HTMLDialogService } from 'vs/workbench/services/dialogs/browser/dialogService'; -import { ILogService } from 'vs/platform/log/common/log'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; -import { MessageBoxOptions } from 'vs/base/parts/sandbox/common/electronTypes'; import { fromNow } from 'vs/base/common/date'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { isLinux, isWindows } from 'vs/base/common/platform'; +import Severity from 'vs/base/common/severity'; +import { MessageBoxOptions } from 'vs/base/parts/sandbox/common/electronTypes'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfirmation, IConfirmationResult, IDialogHandler, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { IProductService } from 'vs/platform/product/common/productService'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; interface IMassagedMessageBoxOptions { @@ -37,59 +31,7 @@ interface IMassagedMessageBoxOptions { buttonIndexMap: number[]; } -export class DialogService implements IDialogService { - - declare readonly _serviceBrand: undefined; - - private nativeImpl: IDialogService; - private customImpl: IDialogService; - - constructor( - @IConfigurationService private configurationService: IConfigurationService, - @ILogService logService: ILogService, - @ILayoutService layoutService: ILayoutService, - @IThemeService themeService: IThemeService, - @IKeybindingService keybindingService: IKeybindingService, - @IProductService productService: IProductService, - @IClipboardService clipboardService: IClipboardService, - @INativeHostService nativeHostService: INativeHostService - ) { - this.customImpl = new HTMLDialogService(logService, layoutService, themeService, keybindingService, productService, clipboardService); - this.nativeImpl = new NativeDialogService(logService, nativeHostService, productService, clipboardService); - } - - private get useCustomDialog(): boolean { - return this.configurationService.getValue('window.dialogStyle') === 'custom'; - } - - confirm(confirmation: IConfirmation): Promise { - if (this.useCustomDialog) { - return this.customImpl.confirm(confirmation); - } - - return this.nativeImpl.confirm(confirmation); - } - - show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { - if (this.useCustomDialog) { - return this.customImpl.show(severity, message, buttons, options); - } - - return this.nativeImpl.show(severity, message, buttons, options); - } - - input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise { - return this.customImpl.input(severity, message, buttons, inputs, options); - } - - about(): Promise { - return this.nativeImpl.about(); - } -} - -class NativeDialogService implements IDialogService { - - declare readonly _serviceBrand: undefined; +export class NativeDialogHandler implements IDialogHandler { constructor( @ILogService private readonly logService: ILogService, @@ -265,4 +207,3 @@ class NativeDialogService implements IDialogService { } } -registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 7002cb28..ea147a06 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -9,7 +9,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ILabelService } from 'vs/platform/label/common/label'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { IMenuService } from 'vs/platform/actions/common/actions'; @@ -32,11 +32,23 @@ export class TitlebarPart extends BrowserTitleBarPart { private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; + private getMacTitlebarSize() { + const osVersion = this.environmentService.os.release; + if (parseFloat(osVersion) >= 20) { // Big Sur increases title bar height + return 28; + } + + return 22; + } + + get minimumHeight(): number { return isMacintosh ? this.getMacTitlebarSize() / getZoomFactor() : super.minimumHeight; } + get maximumHeight(): number { return this.minimumHeight; } + constructor( @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService protected readonly configurationService: IConfigurationService, @IEditorService editorService: IEditorService, - @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService, + @INativeWorkbenchEnvironmentService protected readonly environmentService: INativeWorkbenchEnvironmentService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @@ -206,7 +218,7 @@ export class TitlebarPart extends BrowserTitleBarPart { updateLayout(dimension: DOM.Dimension): void { this.lastLayoutDimensions = dimension; - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (getTitleBarStyle(this.configurationService) === 'custom') { // Only prevent zooming behavior on macOS or when the menubar is not visible if (isMacintosh || this.currentMenubarVisibility === 'hidden') { this.title.style.zoom = `${1 / getZoomFactor()}`; diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index ed4f2640..ada7a23f 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -25,14 +25,8 @@ import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backu import { ITextSnapshot } from 'vs/editor/common/model'; import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ClassifiedEvent, GDPRClassification, StrictPropertyChecker } from 'vs/platform/telemetry/common/gdprTypings'; -import { IKeyboardLayoutInfo, IKeymapService, ILinuxKeyboardLayoutInfo, ILinuxKeyboardMapping, IMacKeyboardLayoutInfo, IMacKeyboardMapping, IWindowsKeyboardLayoutInfo, IWindowsKeyboardMapping } from 'vs/workbench/services/keybinding/common/keymapInfo'; -import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; -import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; -import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; -import { ChordKeybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; -import { ScanCodeBinding } from 'vs/base/common/scanCode'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { isWindows, OS } from 'vs/base/common/platform'; +import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout'; +import { isWindows } from 'vs/base/common/platform'; import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; @@ -50,7 +44,7 @@ import { CustomTask, ContributedTask, InMemoryTask, TaskRunSource, ConfiguringTa import { TaskSystemInfo } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { IExtensionTipsService, IConfigBasedExtensionTip, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; -import { AsbtractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; +import { AbstractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity'; @@ -59,7 +53,9 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; import type { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import { Schemas } from 'vs/base/common/network'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { BrowserKeyboardLayoutService } from 'vs/workbench/services/keybinding/browser/keyboardLayoutService'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; //#region Environment @@ -87,12 +83,10 @@ export class SimpleNativeWorkbenchEnvironmentService implements INativeWorkbench get tmpDir(): URI { return joinPath(this.userRoamingDataHome, 'tmp'); } get logsPath(): string { return joinPath(this.userRoamingDataHome, 'logs').path; } - get backupWorkspaceHome(): URI { return joinPath(this.userRoamingDataHome, 'Backups', 'workspace'); } - updateBackupPath(newPath: string | undefined): void { } - sessionId = this.configuration.sessionId; machineId = this.configuration.machineId; remoteAuthority = this.configuration.remoteAuthority; + os = { release: 'unknown' }; options?: IWorkbenchConstructionOptions | undefined; logExtensionHostCommunication?: boolean | undefined; @@ -127,7 +121,7 @@ export class SimpleNativeWorkbenchEnvironmentService implements INativeWorkbench sharedIPCHandle: string = undefined!; - extensionsPath?: string | undefined; + extensionsPath: string = undefined!; extensionsDownloadPath: string = undefined!; builtinExtensionsPath: string = undefined!; @@ -433,6 +427,7 @@ export class SimpleRemoteAgentService implements IRemoteAgentService { async getRawEnvironment(): Promise { return null; } async scanExtensions(skipExtensions?: ExtensionIdentifier[]): Promise { return []; } async scanSingleExtension(extensionLocation: URI, isBuiltin: boolean): Promise { return null; } + async whenExtensionsReady(): Promise { } } //#endregion @@ -499,37 +494,11 @@ registerSingleton(ITelemetryService, SimpleTelemetryService); //#endregion -//#region Keymap Service +//#region Keymap Service (borrowed from browser for now to enable keyboard access) -class SimpleKeyboardMapper implements IKeyboardMapper { - dumpDebugInfo(): string { return ''; } - resolveKeybinding(keybinding: ChordKeybinding): ResolvedKeybinding[] { return []; } - resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { - let keybinding = new SimpleKeybinding( - keyboardEvent.ctrlKey, - keyboardEvent.shiftKey, - keyboardEvent.altKey, - keyboardEvent.metaKey, - keyboardEvent.keyCode - ).toChord(); - return new USLayoutResolvedKeybinding(keybinding, OS); - } - resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] { return []; } -} +class SimpleKeyboardLayoutService extends BrowserKeyboardLayoutService { } -class SimpleKeymapService implements IKeymapService { - - declare readonly _serviceBrand: undefined; - - onDidChangeKeyboardMapper = Event.None; - getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { return new SimpleKeyboardMapper(); } - getCurrentKeyboardLayout(): (IWindowsKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (ILinuxKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (IMacKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | null { return null; } - getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { return []; } - getRawKeyboardMapping(): IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping | null { return null; } - validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { } -} - -registerSingleton(IKeymapService, SimpleKeymapService); +registerSingleton(IKeyboardLayoutService, SimpleKeyboardLayoutService); //#endregion @@ -683,7 +652,7 @@ registerSingleton(IUserDataAutoSyncService, SimpleUserDataAutoSyncAccountService //#region User Data Sync Store Management -class SimpleIUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { +class SimpleUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService { declare readonly _serviceBrand: undefined; @@ -696,36 +665,10 @@ class SimpleIUserDataSyncStoreManagementService implements IUserDataSyncStoreMan async getPreviousUserDataSyncStore(): Promise { return undefined; } } -registerSingleton(IUserDataSyncStoreManagementService, SimpleIUserDataSyncStoreManagementService); +registerSingleton(IUserDataSyncStoreManagementService, SimpleUserDataSyncStoreManagementService); //#endregion -//#region IStorageKeysSyncRegistryService - -class SimpleIStorageKeysSyncRegistryService implements IStorageKeysSyncRegistryService { - - declare readonly _serviceBrand: undefined; - - onDidChangeStorageKeys = Event.None; - - storageKeys = []; - - registerStorageKey(): void { } - - onDidChangeExtensionStorageKeys = Event.None; - - extensionsStorageKeys = []; - - getExtensioStorageKeys() { return undefined; } - - registerExtensionStorageKeys(): void { } -} - -registerSingleton(IStorageKeysSyncRegistryService, SimpleIStorageKeysSyncRegistryService); - -//#endregion - - //#region Task class SimpleTaskService implements ITaskService { @@ -754,6 +697,7 @@ class SimpleTaskService implements ITaskService { tryResolveTask(configuringTask: ConfiguringTask): Promise { throw new Error('Method not implemented.'); } getTasksForGroup(group: string): Promise { throw new Error('Method not implemented.'); } getRecentlyUsedTasks(): LinkedMap { throw new Error('Method not implemented.'); } + removeRecentlyUsedTask(taskRecentlyUsedKey: string): void { throw new Error('Method not implemented.'); } migrateRecentTasks(tasks: Task[]): Promise { throw new Error('Method not implemented.'); } createSorter(): TaskSorter { throw new Error('Method not implemented.'); } getTaskDescription(task: CustomTask | ContributedTask | InMemoryTask | ConfiguringTask): string | undefined { throw new Error('Method not implemented.'); } @@ -798,7 +742,7 @@ class SimpleWorkspaceTagsService implements IWorkspaceTagsService { declare readonly _serviceBrand: undefined; async getTags(): Promise { return Object.create(null); } - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { return undefined; } + async getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise { return undefined; } async getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise { return []; } } @@ -809,7 +753,7 @@ registerSingleton(IWorkspaceTagsService, SimpleWorkspaceTagsService); //#region Output Channel -class SimpleOutputChannelModelService extends AsbtractOutputChannelModelService { +class SimpleOutputChannelModelService extends AbstractOutputChannelModelService { declare readonly _serviceBrand: undefined; } @@ -832,3 +776,9 @@ class SimpleIntegrityService implements IIntegrityService { registerSingleton(IIntegrityService, SimpleIntegrityService); //#endregion + +//#region Terminal Instance + +class SimpleTerminalInstanceService extends TerminalInstanceService { } + +registerSingleton(ITerminalInstanceService, SimpleTerminalInstanceService); diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 6f294f3c..37afcaaf 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -32,7 +32,7 @@ import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/wo import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; import { IProductService } from 'vs/platform/product/common/productService'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, IPromptChoice, NeverShowAgainScope, Severity } from 'vs/platform/notification/common/notification'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; @@ -43,7 +43,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { MenubarControl } from '../browser/parts/titlebar/menubarControl'; import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IPreferencesService } from '../services/preferences/common/preferences'; import { IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/common/menubar'; import { IMenubarService } from 'vs/platform/menubar/electron-sandbox/menubar'; @@ -59,7 +59,6 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { Event } from 'vs/base/common/event'; -import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IAddressProvider, IAddress } from 'vs/platform/remote/common/remoteAgentConnection'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -185,14 +184,29 @@ export class NativeWindow extends Disposable { ipcRenderer.on('vscode:addFolders', (event: unknown, request: IAddFoldersRequest) => this.onAddFoldersRequest(request)); // Message support - ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => { - this.notificationService.info(message); - }); + ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => this.notificationService.info(message)); - // Display change events - ipcRenderer.on('vscode:displayChanged', () => { - clearAllFontInfos(); - }); + // Shell Environment Issue Notifications + const choices: IPromptChoice[] = [{ + label: nls.localize('learnMode', "Learn More"), + run: () => this.openerService.open('https://go.microsoft.com/fwlink/?linkid=2149667') + }]; + + ipcRenderer.on('vscode:showShellEnvSlowWarning', () => this.notificationService.prompt( + Severity.Warning, + nls.localize('shellEnvSlowWarning', "Resolving your shell environment is taking very long. Please review your shell configuration."), + choices, + { + sticky: true, + neverShowAgain: { id: 'ignoreShellEnvSlowWarning', scope: NeverShowAgainScope.GLOBAL } + } + )); + + ipcRenderer.on('vscode:showShellEnvTimeoutError', () => this.notificationService.prompt( + Severity.Error, + nls.localize('shellEnvTimeoutError', "Unable to resolve your shell environment in a reasonable time. Please review your shell configuration."), + choices + )); // Fullscreen Events ipcRenderer.on('vscode:enterFullScreen', async () => { @@ -237,7 +251,7 @@ export class NativeWindow extends Disposable { // Update state based on checkbox if (result.checkboxChecked) { - this.storageService.store(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, true, StorageScope.GLOBAL); + this.storageService.store(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, true, StorageScope.GLOBAL, StorageTarget.MACHINE); } else { this.storageService.remove(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, StorageScope.GLOBAL); } @@ -286,7 +300,7 @@ export class NativeWindow extends Disposable { } // Maximize/Restore on doubleclick (for macOS custom title) - if (isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + if (isMacintosh && getTitleBarStyle(this.configurationService) === 'custom') { const titlePart = assertIsDefined(this.layoutService.getContainer(Parts.TITLEBAR_PART)); this._register(DOM.addDisposableListener(titlePart, DOM.EventType.DBLCLICK, e => { @@ -402,7 +416,7 @@ export class NativeWindow extends Disposable { this.customTitleContextMenuDisposable.clear(); // Provide new menu if a file is opened and we are on a custom title - if (!filePath || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') { + if (!filePath || getTitleBarStyle(this.configurationService) !== 'custom') { return; } @@ -434,7 +448,7 @@ export class NativeWindow extends Disposable { private create(): void { // Native menu controller - if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + if (isMacintosh || getTitleBarStyle(this.configurationService) === 'native') { this._register(this.instantiationService.createInstance(NativeMenubarControl)); } diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 74fa84fa..f28d3149 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -14,7 +14,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProductService } from 'vs/platform/product/common/productService'; import { isString } from 'vs/base/common/types'; @@ -375,11 +375,11 @@ export class AuthenticationService extends Disposable implements IAuthentication const allowList = readAllowedExtensions(storageService, providerId, session.account.label); if (!allowList.find(allowed => allowed.id === extensionId)) { allowList.push({ id: extensionId, name: extensionName }); - storageService.store(`${providerId}-${session.account.label}`, JSON.stringify(allowList), StorageScope.GLOBAL); + storageService.store(`${providerId}-${session.account.label}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER); } // And also set it as the preferred account for the extension - storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL); + storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL, StorageTarget.MACHINE); } }); diff --git a/src/vs/workbench/services/backup/browser/backupFileService.ts b/src/vs/workbench/services/backup/browser/backupFileService.ts new file mode 100644 index 00000000..afd98b6c --- /dev/null +++ b/src/vs/workbench/services/backup/browser/backupFileService.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; +import { hash } from 'vs/base/common/hash'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { joinPath } from 'vs/base/common/resources'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + +export class BrowserBackupFileService extends BackupFileService { + + declare readonly _serviceBrand: undefined; + + constructor( + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IFileService fileService: IFileService, + @ILogService logService: ILogService + ) { + super(joinPath(environmentService.userRoamingDataHome, 'Backups', contextService.getWorkspace().id), fileService, logService); + } + + protected hashPath(resource: URI): string { + const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + + return hash(str).toString(16); + } +} + +registerSingleton(IBackupFileService, BrowserBackupFileService); diff --git a/src/vs/workbench/services/backup/common/backupFileService.ts b/src/vs/workbench/services/backup/common/backupFileService.ts index 5049d8f6..16546d87 100644 --- a/src/vs/workbench/services/backup/common/backupFileService.ts +++ b/src/vs/workbench/services/backup/common/backupFileService.ts @@ -6,7 +6,6 @@ import { join } from 'vs/base/common/path'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { hash } from 'vs/base/common/hash'; import { coalesce } from 'vs/base/common/arrays'; import { equals, deepClone } from 'vs/base/common/objects'; import { ResourceQueue } from 'vs/base/common/async'; @@ -15,8 +14,6 @@ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platfo import { ITextSnapshot } from 'vs/editor/common/model'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { ResourceMap } from 'vs/base/common/map'; -import { Schemas } from 'vs/base/common/network'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { VSBuffer } from 'vs/base/common/buffer'; import { TextSnapshotReadable, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -108,42 +105,36 @@ export class BackupFilesModel implements IBackupFilesModel { } } -export class BackupFileService implements IBackupFileService { +export abstract class BackupFileService implements IBackupFileService { declare readonly _serviceBrand: undefined; private impl: BackupFileServiceImpl | InMemoryBackupFileService; constructor( - @IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService, + backupWorkspaceHome: URI | undefined, @IFileService protected fileService: IFileService, @ILogService private readonly logService: ILogService ) { - this.impl = this.initialize(); + this.impl = this.initialize(backupWorkspaceHome); } - protected hashPath(resource: URI): string { - const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + protected abstract hashPath(resource: URI): string; - return hash(str).toString(16); - } - - private initialize(): BackupFileServiceImpl | InMemoryBackupFileService { - const backupWorkspaceResource = this.environmentService.backupWorkspaceHome; - if (backupWorkspaceResource) { - return new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, this.fileService, this.logService); + private initialize(backupWorkspaceHome: URI | undefined): BackupFileServiceImpl | InMemoryBackupFileService { + if (backupWorkspaceHome) { + return new BackupFileServiceImpl(backupWorkspaceHome, this.hashPath, this.fileService, this.logService); } return new InMemoryBackupFileService(this.hashPath); } - reinitialize(): void { + reinitialize(backupWorkspaceHome: URI | undefined): void { // Re-init implementation (unless we are running in-memory) if (this.impl instanceof BackupFileServiceImpl) { - const backupWorkspaceResource = this.environmentService.backupWorkspaceHome; - if (backupWorkspaceResource) { - this.impl.initialize(backupWorkspaceResource); + if (backupWorkspaceHome) { + this.impl.initialize(backupWorkspaceHome); } else { this.impl = new InMemoryBackupFileService(this.hashPath); } diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/electron-browser/backupFileService.ts similarity index 53% rename from src/vs/workbench/services/backup/node/backupFileService.ts rename to src/vs/workbench/services/backup/electron-browser/backupFileService.ts index 74beddef..efbbeec9 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/electron-browser/backupFileService.ts @@ -3,14 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BackupFileService as CommonBackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; +import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import * as crypto from 'crypto'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -export class BackupFileService extends CommonBackupFileService { +export class NativeBackupFileService extends BackupFileService { + + constructor( + @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, + @IFileService fileService: IFileService, + @ILogService logService: ILogService + ) { + super(environmentService.configuration.backupPath ? URI.file(environmentService.configuration.backupPath).with({ scheme: environmentService.userRoamingDataHome.scheme }) : undefined, fileService, logService); + } protected hashPath(resource: URI): string { return hashPath(resource); @@ -26,4 +37,4 @@ export function hashPath(resource: URI): string { return crypto.createHash('md5').update(str).digest('hex'); } -registerSingleton(IBackupFileService, BackupFileService); +registerSingleton(IBackupFileService, NativeBackupFileService); diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 0e00c5a4..1835f277 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -23,7 +23,7 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService } from 'vs/platform/files/common/files'; -import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; +import { hashPath, NativeBackupFileService } from 'vs/workbench/services/backup/electron-browser/backupFileService'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { VSBuffer } from 'vs/base/common/buffer'; import { TestWorkbenchConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices'; @@ -53,7 +53,7 @@ class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService } } -export class NodeTestBackupFileService extends BackupFileService { +export class NodeTestBackupFileService extends NativeBackupFileService { readonly fileService: IFileService; @@ -67,7 +67,7 @@ export class NodeTestBackupFileService extends BackupFileService { const fileService = new FileService(logService); const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, URI.file(workspaceBackupPath), diskFileSystemProvider, environmentService, logService)); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, logService)); super(environmentService, fileService, logService); diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 717a91dd..973ef52f 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { Event, Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; -import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { ConfigurationModel, ConfigurationModelParser, UserSettings } from 'vs/platform/configuration/common/configurationModels'; @@ -92,6 +92,7 @@ class FileServiceBasedConfiguration extends Disposable { ) { super(); this.allResources = [...this.settingsResources, ...this.standAloneConfigurationResources.map(([, resource]) => resource)]; + this._register(combinedDisposable(...this.allResources.map(resource => this.fileService.watch(resources.dirname(resource))))); this._folderSettingsModelParser = new ConfigurationModelParser(name, this.scopes); this._standAloneConfigurations = []; this._cache = new ConfigurationModel(); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 2e23db80..7a1b2cd4 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -32,6 +32,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; class Workspace extends BaseWorkspace { initialized: boolean = false; @@ -55,6 +56,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private readonly logService: ILogService; private readonly fileService: IFileService; + private readonly uriIdentityService: IUriIdentityService; protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; @@ -79,6 +81,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic environmentService: IWorkbenchEnvironmentService, fileService: IFileService, remoteAgentService: IRemoteAgentService, + uriIdentityService: IUriIdentityService, logService: ILogService, ) { super(); @@ -94,6 +97,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic this.defaultConfiguration = new DefaultConfigurationModel(); this.configurationCache = configurationCache; this.fileService = fileService; + this.uriIdentityService = uriIdentityService; this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); @@ -285,13 +289,38 @@ export class WorkspaceService extends Disposable implements IConfigurationServic }); } - reloadConfiguration(folder?: IWorkspaceFolder, key?: string): Promise { - if (folder) { - return this.reloadWorkspaceFolderConfiguration(folder, key); + async reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise { + if (target === undefined) { + const { local, remote } = await this.reloadUserConfiguration(); + await this.reloadWorkspaceConfiguration(); + await this.loadConfiguration(local, remote); + return; + } + + if (IWorkspaceFolder.isIWorkspaceFolder(target)) { + await this.reloadWorkspaceFolderConfiguration(target); + return; + } + + switch (target) { + case ConfigurationTarget.USER: + const { local, remote } = await this.reloadUserConfiguration(); + await this.loadConfiguration(local, remote); + return; + + case ConfigurationTarget.USER_LOCAL: + await this.reloadLocalUserConfiguration(); + return; + + case ConfigurationTarget.USER_REMOTE: + await this.reloadRemoteUserConfiguration(); + return; + + case ConfigurationTarget.WORKSPACE: + case ConfigurationTarget.WORKSPACE_FOLDER: + await this.reloadWorkspaceConfiguration(); + return; } - return this.reloadUserConfiguration() - .then(({ local, remote }) => this.reloadWorkspaceConfiguration() - .then(() => this.loadConfiguration(local, remote))); } inspect(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { @@ -346,20 +375,20 @@ export class WorkspaceService extends Disposable implements IConfigurationServic const workspaceConfigPath = workspaceIdentifier.configPath; const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), workspaceConfigPath); const workspaceId = workspaceIdentifier.id; - const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); + const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = this.workspaceConfiguration.initialized; return workspace; }); } private createSingleFolderWorkspace(singleFolder: ISingleFolderWorkspaceInitializationPayload): Promise { - const workspace = new Workspace(singleFolder.id, [toWorkspaceFolder(singleFolder.folder)]); + const workspace = new Workspace(singleFolder.id, [toWorkspaceFolder(singleFolder.folder)], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = true; return Promise.resolve(workspace); } private createEmptyWorkspace(emptyWorkspace: IEmptyWorkspaceInitializationPayload): Promise { - const workspace = new Workspace(emptyWorkspace.id); + const workspace = new Workspace(emptyWorkspace.id, [], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = true; return Promise.resolve(workspace); } @@ -465,10 +494,10 @@ export class WorkspaceService extends Disposable implements IConfigurationServic return new ConfigurationModel(); } - private reloadWorkspaceConfiguration(key?: string): Promise { + private reloadWorkspaceConfiguration(): Promise { const workbenchState = this.getWorkbenchState(); if (workbenchState === WorkbenchState.FOLDER) { - return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0], key); + return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0]); } if (workbenchState === WorkbenchState.WORKSPACE) { return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged()); @@ -476,8 +505,8 @@ export class WorkspaceService extends Disposable implements IConfigurationServic return Promise.resolve(undefined); } - private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder, key?: string): Promise { - return this.onWorkspaceFolderConfigurationChanged(folder, key); + private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder): Promise { + return this.onWorkspaceFolderConfigurationChanged(folder); } private loadConfiguration(userConfigurationModel: ConfigurationModel, remoteUserConfigurationModel: ConfigurationModel): Promise { @@ -592,7 +621,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } } - private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): Promise { + private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder): Promise { return this.loadFolderConfigurations([folder]) .then(([folderConfiguration]) => { const previous = { data: this._configuration.toData(), workspace: this.workspace }; @@ -700,7 +729,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic case EditableConfigurationTarget.WORKSPACE_FOLDER: const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null; if (workspaceFolder) { - return this.reloadWorkspaceFolderConfiguration(workspaceFolder, key); + return this.reloadWorkspaceFolderConfiguration(workspaceFolder); } } return Promise.resolve(); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index ee38ee00..71256640 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -305,7 +305,7 @@ export class ConfigurationEditingService { } private openFile(resource: URI): void { - this.editorService.openEditor({ resource }); + this.editorService.openEditor({ resource, options: { pinned: true } }); } private reject(code: ConfigurationEditingErrorCode, target: EditableConfigurationTarget, operation: IConfigurationEditOperation): Promise { diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts index c0399d23..ef4976ce 100644 --- a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -8,8 +8,9 @@ import { StandaloneConfigurationModelParser, Configuration } from 'vs/workbench/ import { ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { ResourceMap } from 'vs/base/common/map'; -import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; +import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; suite('FolderSettingsModelParser', () => { diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index f9f3adb5..f33ff608 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -41,6 +41,8 @@ import { ConfigurationCache } from 'vs/workbench/services/configuration/electron import { KeybindingsEditingService, IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -61,6 +63,8 @@ suite('ConfigurationEditingService', () => { let globalTasksFile: string; let workspaceSettingsDir; + const disposables = new DisposableStore(); + suiteSetup(() => { const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ @@ -83,9 +87,9 @@ suite('ConfigurationEditingService', () => { }); }); - setup(() => { - return setUpWorkspace() - .then(() => setUpServices()); + setup(async () => { + await setUpWorkspace(); + await setUpServices(); }); async function setUpWorkspace(): Promise { @@ -99,50 +103,36 @@ suite('ConfigurationEditingService', () => { return await mkdirp(workspaceSettingsDir, 493); } - function setUpServices(noWorkspace: boolean = false): Promise { - // Clear services if they are already created - clearServices(); - + async function setUpServices(noWorkspace: boolean = false): Promise { instantiationService = workbenchInstantiationService(); const environmentService = new TestWorkbenchEnvironmentService(URI.file(workspaceDir)); instantiationService.stub(IEnvironmentService, environmentService); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); - return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => { - instantiationService.stub(IConfigurationService, workspaceService); - instantiationService.stub(IKeybindingEditingService, instantiationService.createInstance(KeybindingsEditingService)); - instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); - instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); - instantiationService.stub(ICommandService, CommandService); - testObject = instantiationService.createInstance(ConfigurationEditingService); - }); + await workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }); + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(IKeybindingEditingService, instantiationService.createInstance(KeybindingsEditingService)); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + instantiationService.stub(ICommandService, CommandService); + testObject = instantiationService.createInstance(ConfigurationEditingService); } teardown(() => { - clearServices(); + disposables.clear(); if (workspaceDir) { return rimraf(workspaceDir, RimRafMode.MOVE); } return undefined; }); - function clearServices(): void { - if (instantiationService) { - const configuraitonService = instantiationService.get(IConfigurationService); - if (configuraitonService) { - configuraitonService.dispose(); - } - instantiationService = null!; - } - } - test('errors cases - invalid key', () => { return testObject.writeConfiguration(EditableConfigurationTarget.WORKSPACE, { key: 'unknown.key', value: 'value' }) .then(() => assert.fail('Should fail with ERROR_UNKNOWN_KEY'), diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 57a0b059..a7f455cd 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -54,6 +54,7 @@ import product from 'vs/platform/product/common/product'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { Event } from 'vs/base/common/event'; +import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -105,6 +106,7 @@ function setUpWorkspace(folders: string[]): Promise<{ parentDir: string, configP suite('WorkspaceContextService - Folder', () => { let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceResource: string, workspaceContextService: IWorkspaceContextService; + const disposables = new DisposableStore(); setup(() => { return setUpFolderWorkspace(workspaceName) @@ -112,19 +114,17 @@ suite('WorkspaceContextService - Folder', () => { parentResource = parentDir; workspaceResource = folderDir; const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, new DiskFileSystemProvider(new NullLogService()), environmentService, new NullLogService())); - workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, { _serviceBrand: undefined, ...product }, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService()), new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService()), Schemas.userData, new NullLogService()))); + workspaceContextService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, { _serviceBrand: undefined, ...product }, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); teardown(() => { - if (workspaceContextService) { - (workspaceContextService).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -167,6 +167,7 @@ suite('WorkspaceContextService - Folder', () => { suite('WorkspaceContextService - Workspace', () => { let parentResource: string, testObject: WorkspaceService, instantiationService: TestInstantiationService; + const disposables = new DisposableStore(); setup(() => { return setUpWorkspace(['a', 'b']) @@ -178,11 +179,11 @@ suite('WorkspaceContextService - Workspace', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -196,9 +197,7 @@ suite('WorkspaceContextService - Workspace', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -227,6 +226,7 @@ suite('WorkspaceContextService - Workspace', () => { suite('WorkspaceContextService - Workspace Editing', () => { let parentResource: string, testObject: WorkspaceService, instantiationService: TestInstantiationService; + const disposables = new DisposableStore(); setup(() => { return setUpWorkspace(['a', 'b']) @@ -238,11 +238,11 @@ suite('WorkspaceContextService - Workspace Editing', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -260,9 +260,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -467,6 +465,7 @@ suite('WorkspaceService - Initialization', () => { let parentResource: string, workspaceConfigPath: URI, testObject: WorkspaceService, globalSettingsFile: string; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + const disposables = new DisposableStore(); suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -499,11 +498,11 @@ suite('WorkspaceService - Initialization', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -519,9 +518,7 @@ suite('WorkspaceService - Initialization', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -727,7 +724,7 @@ suite('WorkspaceConfigurationService - Folder', () => { let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: IConfigurationService, globalSettingsFile: string, globalTasksFile: string, workspaceService: WorkspaceService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); let fileService: IFileService; - let disposableStore: DisposableStore = new DisposableStore(); + const disposables: DisposableStore = new DisposableStore(); suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -776,17 +773,17 @@ suite('WorkspaceConfigurationService - Folder', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - workspaceService = disposableStore.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); // Watch workspace configuration directory - disposableStore.add(fileService.watch(joinPath(URI.file(workspaceDir), '.vscode'))); + disposables.add(fileService.watch(joinPath(URI.file(workspaceDir), '.vscode'))); return workspaceService.initialize(convertToWorkspacePayload(URI.file(folderDir))).then(() => { instantiationService.stub(IFileService, fileService); @@ -800,7 +797,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); teardown(() => { - disposableStore.clear(); + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -1232,6 +1229,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { let parentResource: string, workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: IConfigurationService, globalSettingsFile: string; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + const disposables = new DisposableStore(); suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -1282,11 +1280,11 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const fileService = new FileService(new NullLogService()); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); - const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new NullLogService()); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -1308,9 +1306,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -1841,7 +1837,8 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: WorkspaceService, globalSettingsFile: string, remoteSettingsFile: string, remoteSettingsResource: URI, instantiationService: TestInstantiationService, resolveRemoteEnvironment: () => void; const remoteAuthority = 'configuraiton-tests'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + const disposables = new DisposableStore(); + let diskFileSystemProvider: DiskFileSystemProvider; suiteSetup(() => { configurationRegistry.registerConfiguration({ @@ -1886,11 +1883,12 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const environmentService = new TestWorkbenchEnvironmentService(URI.file(parentDir)); const remoteEnvironmentPromise = new Promise>(c => resolveRemoteEnvironment = () => c({ settingsPath: remoteSettingsResource })); const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); - const fileService = new FileService(new NullLogService()); + const fileService = disposables.add(new FileService(new NullLogService())); + diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, undefined, diskFileSystemProvider, environmentService, new NullLogService())); + fileService.registerProvider(Schemas.userData, disposables.add(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.userData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - testObject = new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService, new NullLogService()); + testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); instantiationService.stub(IEnvironmentService, environmentService); @@ -1919,9 +1917,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { } teardown(() => { - if (testObject) { - (testObject).dispose(); - } + disposables.clear(); if (parentResource) { return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); } @@ -2096,7 +2092,7 @@ suite('ConfigurationService - Configuration Defaults', () => { const remoteAgentService = (workbenchInstantiationService()).createInstance(RemoteAgentService); const environmentService = new BrowserWorkbenchEnvironmentService({ logsPath: URI.file(''), workspaceId: '', configurationDefaults }, TestProductService); const fileService = new FileService(new NullLogService()); - return disposableStore.add(new WorkspaceService({ configurationCache: new BrowserConfigurationCache() }, environmentService, fileService, remoteAgentService, new NullLogService())); + return disposableStore.add(new WorkspaceService({ configurationCache: new BrowserConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); } }); diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index a40d034e..5f22a895 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -44,7 +44,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR getWorkspaceFolderCount: (): number => { return workspaceContextService.getWorkspace().folders.length; }, - getConfigurationValue: (folderUri: uri, suffix: string): string | undefined => { + getConfigurationValue: (folderUri: uri | undefined, suffix: string): string | undefined => { return configurationService.getValue(suffix, folderUri ? { resource: folderUri } : {}); }, getExecPath: (): string | undefined => { @@ -60,6 +60,20 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR } return this.labelService.getUriLabel(fileResource, { noPrefix: true }); }, + getWorkspaceFolderPathForFile: (): string | undefined => { + const fileResource = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, { + supportSideBySide: SideBySideEditor.PRIMARY, + filterByScheme: [Schemas.file, Schemas.userData, Schemas.vscodeRemote] + }); + if (!fileResource) { + return undefined; + } + const wsFolder = workspaceContextService.getWorkspaceFolder(fileResource); + if (!wsFolder) { + return undefined; + } + return this.labelService.getUriLabel(wsFolder.uri, { noPrefix: true }); + }, getSelectedText: (): string | undefined => { const activeTextEditorControl = editorService.activeTextEditorControl; if (isCodeEditor(activeTextEditorControl)) { diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 4d632ea0..70459256 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -19,15 +19,17 @@ import { ILabelService } from 'vs/platform/label/common/label'; export interface IVariableResolveContext { getFolderUri(folderName: string): uri | undefined; getWorkspaceFolderCount(): number; - getConfigurationValue(folderUri: uri, section: string): string | undefined; + getConfigurationValue(folderUri: uri | undefined, section: string): string | undefined; getExecPath(): string | undefined; getFilePath(): string | undefined; + getWorkspaceFolderPathForFile?(): string | undefined; getSelectedText(): string | undefined; getLineNumber(): string | undefined; } export class AbstractVariableResolverService implements IConfigurationResolverService { + static readonly VARIABLE_LHS = '${'; static readonly VARIABLE_REGEXP = /\$\{(.*?)\}/g; declare readonly _serviceBrand: undefined; @@ -38,7 +40,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe protected _contributedVariables: Map Promise> = new Map(); - constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment, private _ignoreEditorVariables = false) { + constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariables?: IProcessEnvironment) { this._context = _context; this._labelService = _labelService; if (_envVariables) { @@ -130,6 +132,10 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe // loop through all variables occurrences in 'value' const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => { + // disallow attempted nesting, see #77289 + if (variable.includes(AbstractVariableResolverService.VARIABLE_LHS)) { + return match; + } let resolvedValue = this.evaluateSingleVariable(match, variable, folderUri, commandValueMapping); @@ -164,18 +170,31 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe if (filePath) { return filePath; } - throw new Error(localize('canNotResolveFile', "'{0}' can not be resolved. Please open an editor.", match)); + throw new Error(localize('canNotResolveFile', "Variable {0} can not be resolved. Please open an editor.", match)); + }; + + // common error handling for all variables that require an open editor + const getFolderPathForFile = (): string => { + + const filePath = getFilePath(); // throws error if no editor open + if (this._context.getWorkspaceFolderPathForFile) { + const folderPath = this._context.getWorkspaceFolderPathForFile(); + if (folderPath) { + return folderPath; + } + } + throw new Error(localize('canNotResolveFolderForFile', "Variable {0}: can not find workspace folder of '{1}'.", match, paths.basename(filePath))); }; // common error handling for all variables that require an open folder and accept a folder name argument - const getFolderUri = (withArg = true): uri => { + const getFolderUri = (): uri => { - if (withArg && argument) { + if (argument) { const folder = this._context.getFolderUri(argument); if (folder) { return folder; } - throw new Error(localize('canNotFindFolder', "'{0}' can not be resolved. No such folder '{1}'.", match, argument)); + throw new Error(localize('canNotFindFolder', "Variable {0} can not be resolved. No such folder '{1}'.", match, argument)); } if (folderUri) { @@ -183,9 +202,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } if (this._context.getWorkspaceFolderCount() > 1) { - throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "'{0}' can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); + throw new Error(localize('canNotResolveWorkspaceFolderMultiRoot', "Variable {0} can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.", match)); } - throw new Error(localize('canNotResolveWorkspaceFolder', "'{0}' can not be resolved. Please open a folder.", match)); + throw new Error(localize('canNotResolveWorkspaceFolder', "Variable {0} can not be resolved. Please open a folder.", match)); }; @@ -202,20 +221,20 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe // For `env` we should do the same as a normal shell does - evaluates undefined envs to an empty string #46436 return ''; } - throw new Error(localize('missingEnvVarName', "'{0}' can not be resolved because no environment variable name is given.", match)); + throw new Error(localize('missingEnvVarName', "Variable {0} can not be resolved because no environment variable name is given.", match)); case 'config': if (argument) { - const config = this._context.getConfigurationValue(getFolderUri(false), argument); + const config = this._context.getConfigurationValue(folderUri, argument); if (types.isUndefinedOrNull(config)) { - throw new Error(localize('configNotFound', "'{0}' can not be resolved because setting '{1}' not found.", match, argument)); + throw new Error(localize('configNotFound', "Variable {0} can not be resolved because setting '{1}' not found.", match, argument)); } if (types.isObject(config)) { - throw new Error(localize('configNoString', "'{0}' can not be resolved because '{1}' is a structured value.", match, argument)); + throw new Error(localize('configNoString', "Variable {0} can not be resolved because '{1}' is a structured value.", match, argument)); } return config; } - throw new Error(localize('missingConfigName', "'{0}' can not be resolved because no settings name is given.", match)); + throw new Error(localize('missingConfigName', "Variable {0} can not be resolved because no settings name is given.", match)); case 'command': return this.resolveFromMap(match, argument, commandValueMapping, 'command'); @@ -238,44 +257,32 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return paths.basename(this.fsPath(getFolderUri())); case 'lineNumber': - if (this._ignoreEditorVariables) { - return match; - } const lineNumber = this._context.getLineNumber(); if (lineNumber) { return lineNumber; } - throw new Error(localize('canNotResolveLineNumber', "'{0}' can not be resolved. Make sure to have a line selected in the active editor.", match)); + throw new Error(localize('canNotResolveLineNumber', "Variable {0} can not be resolved. Make sure to have a line selected in the active editor.", match)); case 'selectedText': - if (this._ignoreEditorVariables) { - return match; - } const selectedText = this._context.getSelectedText(); if (selectedText) { return selectedText; } - throw new Error(localize('canNotResolveSelectedText', "'{0}' can not be resolved. Make sure to have some text selected in the active editor.", match)); + throw new Error(localize('canNotResolveSelectedText', "Variable {0} can not be resolved. Make sure to have some text selected in the active editor.", match)); case 'file': - if (this._ignoreEditorVariables) { - return match; - } return getFilePath(); + case 'fileWorkspaceFolder': + return getFolderPathForFile(); + case 'relativeFile': - if (this._ignoreEditorVariables) { - return match; - } if (folderUri || argument) { return paths.relative(this.fsPath(getFolderUri()), getFilePath()); } return getFilePath(); case 'relativeFileDirname': - if (this._ignoreEditorVariables) { - return match; - } const dirname = paths.dirname(getFilePath()); if (folderUri || argument) { const relative = paths.relative(this.fsPath(getFolderUri()), dirname); @@ -284,30 +291,21 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return dirname; case 'fileDirname': - if (this._ignoreEditorVariables) { - return match; - } return paths.dirname(getFilePath()); case 'fileExtname': - if (this._ignoreEditorVariables) { - return match; - } return paths.extname(getFilePath()); case 'fileBasename': - if (this._ignoreEditorVariables) { - return match; - } return paths.basename(getFilePath()); case 'fileBasenameNoExtension': - if (this._ignoreEditorVariables) { - return match; - } const basename = paths.basename(getFilePath()); return (basename.slice(0, basename.length - paths.extname(basename).length)); + case 'fileDirnameBasename': + return paths.basename(paths.dirname(getFilePath())); + case 'execPath': const ep = this._context.getExecPath(); if (ep) { @@ -315,6 +313,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } return match; + case 'pathSeparator': + return paths.sep; + default: try { return this.resolveFromMap(match, variable, commandValueMapping, undefined); @@ -332,7 +333,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe if (typeof v === 'string') { return v; } - throw new Error(localize('noValueForCommand', "'{0}' can not be resolved because the command has no value.", match)); + throw new Error(localize('noValueForCommand', "Variable {0} can not be resolved because the command has no value.", match)); } return match; } diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 3e3de85f..e1a333b0 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -175,6 +175,10 @@ suite('Configuration Resolver Service', () => { } }); + test('disallows nested keys (#77289)', () => { + assert.strictEqual(configurationResolverService!.resolve(workspace, '${env:key1} ${env:key1${env:key2}}'), 'Value for key1 ${env:key1${env:key2}}'); + }); + // test('substitute keys and values in object', () => { // const myObject = { // '${workspaceRootFolderName}': '${lineNumber}', @@ -211,6 +215,17 @@ suite('Configuration Resolver Service', () => { assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); }); + test('substitute configuration variable with undefined workspace folder', () => { + let configurationService: IConfigurationService = new TestConfigurationService({ + editor: { + fontFamily: 'foo' + } + }); + + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService); + assert.strictEqual(service.resolve(undefined, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); + }); + test('substitute many configuration variables', () => { let configurationService: IConfigurationService; configurationService = new TestConfigurationService({ diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index a2005fe6..09a3e187 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -44,7 +44,7 @@ export class ContextMenuService extends Disposable implements IContextMenuServic super(); // Custom context menu: Linux/Windows if custom title is enabled - if (!isMacintosh && getTitleBarStyle(configurationService, environmentService) === 'custom') { + if (!isMacintosh && getTitleBarStyle(configurationService) === 'custom') { this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService); } diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index fda88516..7cfea530 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -18,6 +18,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { hash } from 'vs/base/common/hash'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; class DecorationRule { @@ -106,9 +107,7 @@ class DecorationStyles { private readonly _decorationRules = new Map(); private readonly _dispoables = new DisposableStore(); - constructor( - private _themeService: IThemeService, - ) { + constructor(private readonly _themeService: IThemeService) { this._themeService.onDidColorThemeChange(this._onThemeChange, this, this._dispoables); } @@ -149,7 +148,7 @@ class DecorationStyles { badgeClassName, tooltip, dispose: () => { - if (rule && rule.release()) { + if (rule?.release()) { this._decorationRules.delete(key); rule.removeCSSRules(this._styleElement); rule = undefined; @@ -168,13 +167,13 @@ class DecorationStyles { class FileDecorationChangeEvent implements IResourceDecorationChangeEvent { - private readonly _data = TernarySearchTree.forUris(); + private readonly _data = TernarySearchTree.forUris(_uri => true); // events ignore all path casings affectsResource(uri: URI): boolean { - return this._data.get(uri) || this._data.findSuperstr(uri) !== undefined; + return this._data.get(uri) ?? this._data.findSuperstr(uri) !== undefined; } - static debouncer(last: FileDecorationChangeEvent | undefined, current: URI | URI[]) { + static debouncer(last: FileDecorationChangeEvent | undefined, current: URI | URI[]): FileDecorationChangeEvent { if (!last) { last = new FileDecorationChangeEvent(); } @@ -201,14 +200,18 @@ class DecorationDataRequest { class DecorationProviderWrapper { - readonly data = TernarySearchTree.forUris(); + readonly data: TernarySearchTree; private readonly _dispoable: IDisposable; constructor( readonly provider: IDecorationsProvider, + uriIdentityService: IUriIdentityService, private readonly _uriEmitter: Emitter, private readonly _flushEmitter: Emitter ) { + + this.data = TernarySearchTree.forUris(uri => uriIdentityService.extUri.ignorePathCasing(uri)); + this._dispoable = this.provider.onDidChange(uris => { if (!uris) { // flush event -> drop all data, can affect everything @@ -233,7 +236,7 @@ class DecorationProviderWrapper { } knowsAbout(uri: URI): boolean { - return Boolean(this.data.get(uri)) || Boolean(this.data.findSuperstr(uri)); + return this.data.has(uri) || Boolean(this.data.findSuperstr(uri)); } getOrRetrieve(uri: URI, includeChildren: boolean, callback: (data: IDecorationData, isChild: boolean) => void): void { @@ -254,9 +257,9 @@ class DecorationProviderWrapper { // (resolved) children const iter = this.data.findSuperstr(uri); if (iter) { - for (let item = iter.next(); !item.done; item = iter.next()) { - if (item.value && !(item.value instanceof DecorationDataRequest)) { - callback(item.value, true); + for (const [, value] of iter) { + if (value && !(value instanceof DecorationDataRequest)) { + callback(value, true); } } } @@ -326,6 +329,7 @@ export class DecorationsService implements IDecorationsService { constructor( @IThemeService themeService: IThemeService, + @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService, ) { this._decorationStyles = new DecorationStyles(themeService); } @@ -340,6 +344,7 @@ export class DecorationsService implements IDecorationsService { const wrapper = new DecorationProviderWrapper( provider, + this._uriIdentityService, this._onDidChangeDecorationsDelayed, this._onDidChangeDecorations ); diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index 9cbdbb2a..5a4b76eb 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -8,8 +8,11 @@ import { DecorationsService } from 'vs/workbench/services/decorations/browser/de import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; +import * as resources from 'vs/base/common/resources'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { mock } from 'vs/base/test/common/mock'; +import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; suite('DecorationsService', function () { @@ -19,7 +22,12 @@ suite('DecorationsService', function () { if (service) { service.dispose(); } - service = new DecorationsService(new TestThemeService()); + service = new DecorationsService( + new TestThemeService(), + new class extends mock() { + extUri = resources.extUri; + } + ); }); test('Async provider, async/evented result', function () { diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index 3d6973ab..222e877c 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -24,6 +24,7 @@ import { trim } from 'vs/base/common/strings'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { Schemas } from 'vs/base/common/network'; export abstract class AbstractFileDialogService implements IFileDialogService { @@ -45,7 +46,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { @IPathService private readonly pathService: IPathService ) { } - defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { + async defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): Promise { // Check for last active file first... let candidate = this.historyService.getLastActiveFile(schemeFilter); @@ -57,10 +58,14 @@ export abstract class AbstractFileDialogService implements IFileDialogService { candidate = candidate && resources.dirname(candidate); } - return candidate || undefined; + if (!candidate) { + candidate = await this.pathService.userHome({ preferLocal: schemeFilter === Schemas.file }); + } + + return candidate; } - defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined { + async defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): Promise { // Check for last active file root first... let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter); @@ -70,10 +75,14 @@ export abstract class AbstractFileDialogService implements IFileDialogService { candidate = this.historyService.getLastActiveFile(schemeFilter); } - return candidate && resources.dirname(candidate) || undefined; + if (!candidate) { + return this.pathService.userHome({ preferLocal: schemeFilter === Schemas.file }); + } else { + return resources.dirname(candidate); + } } - defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow(), filename?: string): URI | undefined { + async defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow(), filename?: string): Promise { let defaultWorkspacePath: URI | undefined; // Check for current workspace config file first... if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -85,7 +94,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { // ...then fallback to default file path if (!defaultWorkspacePath) { - defaultWorkspacePath = this.defaultFilePath(schemeFilter); + defaultWorkspacePath = await this.defaultFilePath(schemeFilter); } if (defaultWorkspacePath && filename) { @@ -155,7 +164,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { if (stat.isDirectory || options.forceNewWindow || preferNewWindow) { return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow }); } else { - return this.openerService.open(uri, { fromUserGesture: true }); + return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } }); } } } @@ -172,7 +181,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { if (options.forceNewWindow || preferNewWindow) { return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow }); } else { - return this.openerService.open(uri, { fromUserGesture: true }); + return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } }); } } } diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index a25f8e00..a6eb25f5 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -15,7 +15,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } return this.pickFileFolderAndOpenSimplified(schema, options, false); @@ -25,7 +25,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } return this.pickFileAndOpenSimplified(schema, options, false); @@ -35,7 +35,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFolderPath(schema); + options.defaultUri = await this.defaultFolderPath(schema); } return this.pickFolderAndOpenSimplified(schema, options); @@ -45,7 +45,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultWorkspacePath(schema); + options.defaultUri = await this.defaultWorkspacePath(schema); } return this.pickWorkspaceAndOpenSimplified(schema, options); diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 67885964..e0eca9eb 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -212,11 +212,16 @@ export class SimpleFileDialog { path = path.replace(/\\/g, '/'); } const uri: URI = this.scheme === Schemas.file ? URI.file(path) : URI.from({ scheme: this.scheme, path }); - return resources.toLocalResource(uri, uri.scheme === Schemas.file ? undefined : this.remoteAuthority, this.pathService.defaultUriScheme); + return resources.toLocalResource(uri, + // If the default scheme is file, then we don't care about the remote authority + uri.scheme === Schemas.file ? undefined : this.remoteAuthority, + // If there is a remote authority, then we should use the system's default URI as the local scheme. + // If there is *no* remote authority, then we should use the default scheme for this dialog as that is already local. + this.remoteAuthority ? this.pathService.defaultUriScheme : uri.scheme); } private getScheme(available: readonly string[] | undefined, defaultUri: URI | undefined): string { - if (available) { + if (available && available.length > 0) { if (defaultUri && (available.indexOf(defaultUri.scheme) >= 0)) { return defaultUri.scheme; } @@ -919,7 +924,8 @@ export class SimpleFileDialog { const ext = resources.extname(file); for (let i = 0; i < this.options.filters.length; i++) { for (let j = 0; j < this.options.filters[i].extensions.length; j++) { - if (ext === ('.' + this.options.filters[i].extensions[j])) { + const testExt = this.options.filters[i].extensions[j]; + if ((testExt === '*') || (ext === ('.' + testExt))) { return true; } } diff --git a/src/vs/workbench/services/dialogs/common/dialogService.ts b/src/vs/workbench/services/dialogs/common/dialogService.ts new file mode 100644 index 00000000..df940085 --- /dev/null +++ b/src/vs/workbench/services/dialogs/common/dialogService.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Severity from 'vs/base/common/severity'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IInput, IInputResult, IShowResult } from 'vs/platform/dialogs/common/dialogs'; +import { DialogsModel, IDialogsModel } from 'vs/workbench/common/dialogs'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class DialogService extends Disposable implements IDialogService { + _serviceBrand: undefined; + + readonly model: IDialogsModel = this._register(new DialogsModel()); + + async confirm(confirmation: IConfirmation): Promise { + const handle = this.model.show({ confirmArgs: { confirmation } }); + return await handle.result as IConfirmationResult; + } + + async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { + const handle = this.model.show({ showArgs: { severity, message, buttons, options } }); + return await handle.result as IShowResult; + } + + async input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise { + const handle = this.model.show({ inputArgs: { severity, message, buttons, inputs, options } }); + return await handle.result as IInputResult; + } + + async about(): Promise { + const handle = this.model.show({}); + await handle.result; + } +} + +registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts index a6287ee7..3398150a 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts @@ -68,7 +68,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } const shouldUseSimplified = this.shouldUseSimplified(schema); @@ -82,7 +82,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); + options.defaultUri = await this.defaultFilePath(schema); } const shouldUseSimplified = this.shouldUseSimplified(schema); @@ -96,7 +96,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultFolderPath(schema); + options.defaultUri = await this.defaultFolderPath(schema); } if (this.shouldUseSimplified(schema).useSimplified) { @@ -109,7 +109,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { - options.defaultUri = this.defaultWorkspacePath(schema); + options.defaultUri = await this.defaultWorkspacePath(schema); } if (this.shouldUseSimplified(schema).useSimplified) { diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index d0474b57..89e43281 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICodeEditor, isCodeEditor, isDiffEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor, isDiffEditor, isCompositeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; @@ -69,12 +69,34 @@ export class CodeEditorService extends CodeEditorServiceImpl { } private async doOpenCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise { + + // Special case: we want to detect the request to open an editor that + // is different from the current one to decide wether the current editor + // should be pinned or not. This ensures that the source of a navigation + // is not being replaced by the target. An example is "Goto definition" + // that otherwise would replace the editor everytime the user navigates. + if ( + source && // we need to know the origin of the navigation + !input.options?.pinned && // we only need to look at preview editors that open + !sideBySide && // we only need to care if editor opens in same group + !isEqual(source.getModel()?.uri, input.resource) // we only need to do this if the editor is about to change + ) { + for (const visiblePane of this.editorService.visibleEditorPanes) { + if (getCodeEditor(visiblePane.getControl()) === source) { + visiblePane.group.pinEditor(); + break; + } + } + } + + // Open as editor const control = await this.editorService.openEditor(input, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); if (control) { const widget = control.getControl(); if (isCodeEditor(widget)) { return widget; } + if (isCompositeEditor(widget) && isCodeEditor(widget.activeCodeEditor)) { return widget.activeCodeEditor; } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index a50d37b3..458e5e41 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -24,7 +24,6 @@ import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from import { coalesce, distinct, insert } from 'vs/base/common/arrays'; import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; -import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { EditorsObserver } from 'vs/workbench/browser/parts/editor/editorsObserver'; @@ -73,7 +72,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @ILabelService private readonly labelService: ILabelService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -817,11 +815,12 @@ export class EditorService extends Disposable implements EditorServiceImpl { const leftInput = this.createEditorInput({ resource: resourceDiffInput.leftResource, forceFile: resourceDiffInput.forceFile }); const rightInput = this.createEditorInput({ resource: resourceDiffInput.rightResource, forceFile: resourceDiffInput.forceFile }); - return new DiffEditorInput( - resourceDiffInput.label || this.toSideBySideLabel(leftInput, rightInput), + return this.instantiationService.createInstance(DiffEditorInput, + resourceDiffInput.label, resourceDiffInput.description, leftInput, - rightInput + rightInput, + undefined ); } @@ -881,7 +880,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // File if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) { - return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); + return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.label, resourceEditorInput.description, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService); } // Resource @@ -897,6 +896,14 @@ export class EditorService extends Disposable implements EditorServiceImpl { else if (!(cachedInput instanceof ResourceEditorInput)) { cachedInput.setPreferredResource(preferredResource); + if (resourceEditorInput.label) { + cachedInput.setPreferredName(resourceEditorInput.label); + } + + if (resourceEditorInput.description) { + cachedInput.setPreferredDescription(resourceEditorInput.description); + } + if (resourceEditorInput.encoding) { cachedInput.setPreferredEncoding(resourceEditorInput.encoding); } @@ -968,19 +975,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { return input; } - private toSideBySideLabel(leftInput: EditorInput, rightInput: EditorInput): string | undefined { - - // If both editors are file inputs, we produce an optimized label - // by adding the relative path of both inputs to the label. This - // makes it easier to understand a file-based comparison. - if (this.fileEditorInputFactory.isFileEditorInput(leftInput) && this.fileEditorInputFactory.isFileEditorInput(rightInput)) { - return `${this.labelService.getUriLabel(leftInput.preferredResource, { relative: true })} ↔ ${this.labelService.getUriLabel(rightInput.preferredResource, { relative: true })}`; - } - - // Signal back that the label should be computed from within the editor - return undefined; - } - //#endregion //#region save/revert diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 851734c7..c54ec57e 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -21,16 +21,6 @@ export const enum GroupDirection { RIGHT } -export function preferredSideBySideGroupDirection(configurationService: IConfigurationService): GroupDirection.DOWN | GroupDirection.RIGHT { - const openSideBySideDirection = configurationService.getValue<'right' | 'down'>('workbench.editor.openSideBySideDirection'); - - if (openSideBySideDirection === 'down') { - return GroupDirection.DOWN; - } - - return GroupDirection.RIGHT; -} - export const enum GroupOrientation { HORIZONTAL, VERTICAL @@ -595,3 +585,18 @@ export interface IEditorGroup { */ focus(): void; } + + +//#region Editor Group Helpers + +export function preferredSideBySideGroupDirection(configurationService: IConfigurationService): GroupDirection.DOWN | GroupDirection.RIGHT { + const openSideBySideDirection = configurationService.getValue<'right' | 'down'>('workbench.editor.openSideBySideDirection'); + + if (openSideBySideDirection === 'down') { + return GroupDirection.DOWN; + } + + return GroupDirection.RIGHT; +} + +//#endregion diff --git a/src/vs/workbench/services/editor/common/editorOpenWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts index f0e0503c..425fa2ab 100644 --- a/src/vs/workbench/services/editor/common/editorOpenWith.ts +++ b/src/vs/workbench/services/editor/common/editorOpenWith.ts @@ -16,6 +16,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { extname, basename, isEqual } from 'vs/base/common/resources'; +import { Codicon } from 'vs/base/common/codicons'; /** * Id of the default editor for open with. @@ -71,7 +72,7 @@ export async function openEditorWith( description: entry.active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, detail: entry.detail, buttons: resourceExt ? [{ - iconClass: 'codicon-settings-gear', + iconClass: Codicon.gear.classNames, tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) }] : undefined }; diff --git a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts index bd08a0b3..5f927c55 100644 --- a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts @@ -298,7 +298,7 @@ suite('EditorsObserver', function () { assert.equal(observer.hasEditor(input2.resource), true); assert.equal(observer.hasEditor(input3.resource), true); - storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN); const restoredObserver = new EditorsObserver(part, storage); await part.whenRestored; @@ -350,7 +350,7 @@ suite('EditorsObserver', function () { assert.equal(observer.hasEditor(input2.resource), true); assert.equal(observer.hasEditor(input3.resource), true); - storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN); const restoredObserver = new EditorsObserver(part, storage); await part.whenRestored; @@ -390,7 +390,7 @@ suite('EditorsObserver', function () { assert.equal(currentEditorsMRU[0].editor, input1); assert.equal(observer.hasEditor(input1.resource), true); - storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); + storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN); const restoredObserver = new EditorsObserver(part, storage); await part.whenRestored; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index a8d43045..8f70949c 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -15,6 +15,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { memoize } from 'vs/base/common/decorators'; import { onUnexpectedError } from 'vs/base/common/errors'; import { parseLineAndColumnAware } from 'vs/base/common/extpath'; +import { LogLevelToString } from 'vs/platform/log/common/log'; class BrowserWorkbenchConfiguration implements IWindowConfiguration { @@ -114,7 +115,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get logsPath(): string { return this.options.logsPath.path; } - get logLevel(): string | undefined { return this.payload?.get('logLevel'); } + @memoize + get logLevel(): string | undefined { return this.payload?.get('logLevel') || (this.options.logLevel !== undefined ? LogLevelToString(this.options.logLevel) : undefined); } @memoize get logFile(): URI { return joinPath(this.options.logsPath, 'window.log'); } @@ -157,9 +159,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } - @memoize - get backupWorkspaceHome(): URI { return joinPath(this.userRoamingDataHome, 'Backups', this.options.workspaceId); } - @memoize get untitledWorkspacesHome(): URI { return joinPath(this.userRoamingDataHome, 'Workspaces'); } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 54164056..61725bf1 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -34,7 +34,6 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly sessionId: string; readonly logFile: URI; - readonly backupWorkspaceHome?: URI; readonly extHostLogsPath: URI; readonly logExtensionHostCommunication?: boolean; diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index 576f2cc5..6746e75b 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -10,6 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { join } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; +import { IOSConfiguration } from 'vs/platform/windows/common/windows'; export class NativeWorkbenchEnvironmentService extends NativeEnvironmentService implements INativeWorkbenchEnvironmentService { @@ -30,20 +31,6 @@ export class NativeWorkbenchEnvironmentService extends NativeEnvironmentService @memoize get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } - // Do NOT! memoize as `backupPath` can change in configuration - // via the `updateBackupPath` method below - get backupWorkspaceHome(): URI | undefined { - if (this.configuration.backupPath) { - return URI.file(this.configuration.backupPath).with({ scheme: this.userRoamingDataHome.scheme }); - } - - return undefined; - } - - updateBackupPath(newBackupPath: string | undefined): void { - this.configuration.backupPath = newBackupPath; - } - @memoize get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); } @@ -82,6 +69,10 @@ export class NativeWorkbenchEnvironmentService extends NativeEnvironmentService return undefined; } + get os(): IOSConfiguration { + return this.configuration.os; + } + constructor( readonly configuration: INativeWorkbenchConfiguration, private readonly productService: IProductService diff --git a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index e534169b..4f1ef89c 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchConfiguration, IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { INativeWindowConfiguration, IOSConfiguration } from 'vs/platform/windows/common/windows'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -27,8 +27,7 @@ export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmen readonly log?: string; - // TODO@ben this is a bit ugly - updateBackupPath(newPath: string | undefined): void; + readonly os: IOSConfiguration; /** * @deprecated this property will go away eventually as it diff --git a/src/vs/workbench/services/experiment/common/experimentService.ts b/src/vs/workbench/services/experiment/common/experimentService.ts index dc84569d..0fa7fda8 100644 --- a/src/vs/workbench/services/experiment/common/experimentService.ts +++ b/src/vs/workbench/services/experiment/common/experimentService.ts @@ -6,18 +6,19 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import * as platform from 'vs/base/common/platform'; import type { IKeyValueStorage, IExperimentationTelemetry, IExperimentationFilterProvider, ExperimentationService as TASClient } from 'tas-client-umd'; import { MementoObject, Memento } from 'vs/workbench/common/memento'; -import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryData } from 'vs/base/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import product from 'vs/platform/product/common/product'; export const ITASExperimentService = createDecorator('TASExperimentService'); export interface ITASExperimentService { readonly _serviceBrand: undefined; getTreatment(name: string): Promise; + getCurrentExperiments(): Promise; } const storageKey = 'VSCode.ABExp.FeatureData'; @@ -37,11 +38,20 @@ class MementoKeyValueStorage implements IKeyValueStorage { } class ExperimentServiceTelemetry implements IExperimentationTelemetry { + private _lastAssignmentContext: string | undefined; constructor(private telemetryService: ITelemetryService) { } + get assignmentContext(): string[] | undefined { + return this._lastAssignmentContext?.split(';'); + } + // __GDPR__COMMON__ "VSCode.ABExp.Features" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } // __GDPR__COMMON__ "abexp.assignmentcontext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } setSharedProperty(name: string, value: string): void { + if (name === product.tasConfig?.assignmentContextTelemetryPropertyName) { + this._lastAssignmentContext = value; + } + this.telemetryService.setExperimentProperty(name, value); } @@ -165,6 +175,7 @@ enum TargetPopulation { export class ExperimentService implements ITASExperimentService { _serviceBrand: undefined; private tasClient: Promise | undefined; + private telemetry: ExperimentServiceTelemetry | undefined; private static MEMENTO_ID = 'experiment.service.memento'; private get experimentsEnabled(): boolean { @@ -172,13 +183,12 @@ export class ExperimentService implements ITASExperimentService { } constructor( - @IProductService private productService: IProductService, @ITelemetryService private telemetryService: ITelemetryService, @IStorageService private storageService: IStorageService, @IConfigurationService private configurationService: IConfigurationService, ) { - if (this.productService.tasConfig && this.experimentsEnabled && this.telemetryService.isOptedIn) { + if (product.tasConfig && this.experimentsEnabled && this.telemetryService.isOptedIn) { this.tasClient = this.setupTASClient(); } } @@ -195,26 +205,40 @@ export class ExperimentService implements ITASExperimentService { return (await this.tasClient).getTreatmentVariable('vscode', name); } + async getCurrentExperiments(): Promise { + if (!this.tasClient) { + return undefined; + } + + if (!this.experimentsEnabled) { + return undefined; + } + + await this.tasClient; + + return this.telemetry?.assignmentContext; + } + private async setupTASClient(): Promise { const telemetryInfo = await this.telemetryService.getTelemetryInfo(); - const targetPopulation = telemetryInfo.msftInternal ? TargetPopulation.Internal : (this.productService.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders); + const targetPopulation = telemetryInfo.msftInternal ? TargetPopulation.Internal : (product.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders); const machineId = telemetryInfo.machineId; const filterProvider = new ExperimentServiceFilterProvider( - this.productService.version, - this.productService.nameLong, + product.version, + product.nameLong, machineId, targetPopulation ); const memento = new Memento(ExperimentService.MEMENTO_ID, this.storageService); - const keyValueStorage = new MementoKeyValueStorage(memento.getMemento(StorageScope.GLOBAL)); + const keyValueStorage = new MementoKeyValueStorage(memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE)); - const telemetry = new ExperimentServiceTelemetry(this.telemetryService); + this.telemetry = new ExperimentServiceTelemetry(this.telemetryService); - const tasConfig = this.productService.tasConfig!; + const tasConfig = product.tasConfig!; const tasClient = new (await import('tas-client-umd')).ExperimentationService({ filterProviders: [filterProvider], - telemetry: telemetry, + telemetry: this.telemetry, storageKey: storageKey, keyValueStorage: keyValueStorage, featuresTelemetryPropertyName: tasConfig.featuresTelemetryPropertyName, diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts b/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts new file mode 100644 index 00000000..7a93a529 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/browser/extensionBisect.ts @@ -0,0 +1,337 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; +import { timeout } from 'vs/base/common/async'; + +// --- bisect service + +export const IExtensionBisectService = createDecorator('IExtensionBisectService'); + +export interface IExtensionBisectService { + + readonly _serviceBrand: undefined; + + isDisabledByBisect(extension: IExtension): boolean; + isActive: boolean; + disabledCount: number; + start(extensions: ILocalExtension[]): Promise; + next(seeingBad: boolean): Promise<{ id: string, bad: boolean } | undefined>; + reset(): Promise; +} + +class BisectState { + + static fromJSON(raw: string | undefined): BisectState | undefined { + if (!raw) { + return undefined; + } + try { + interface Raw extends BisectState { } + const data: Raw = JSON.parse(raw); + return new BisectState(data.extensions, data.low, data.high, data.mid); + } catch { + return undefined; + } + } + + constructor( + readonly extensions: string[], + readonly low: number, + readonly high: number, + readonly mid: number = ((low + high) / 2) | 0 + ) { } +} + +class ExtensionBisectService implements IExtensionBisectService { + + declare readonly _serviceBrand: undefined; + + private static readonly _storageKey = 'extensionBisectState'; + + private readonly _state: BisectState | undefined; + private readonly _disabled = new Map(); + + constructor( + @ILogService logService: ILogService, + @IStorageService private readonly _storageService: IStorageService, + ) { + const raw = _storageService.get(ExtensionBisectService._storageKey, StorageScope.GLOBAL); + this._state = BisectState.fromJSON(raw); + + if (this._state) { + const { mid, high } = this._state; + for (let i = 0; i < this._state.extensions.length; i++) { + const isDisabled = i >= mid && i < high; + this._disabled.set(this._state.extensions[i], isDisabled); + } + logService.warn('extension BISECT active', [...this._disabled]); + } + } + + get isActive() { + return !!this._state; + } + + get disabledCount() { + return this._state ? this._state.high - this._state.mid : -1; + } + + isDisabledByBisect(extension: IExtension): boolean { + if (!this._state) { + return false; + } + const disabled = this._disabled.get(extension.identifier.id); + return disabled ?? false; + } + + async start(extensions: ILocalExtension[]): Promise { + if (this._state) { + throw new Error('invalid state'); + } + const extensionIds = extensions.map(ext => ext.identifier.id); + const newState = new BisectState(extensionIds, 0, extensionIds.length, 0); + this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(newState), StorageScope.GLOBAL, StorageTarget.MACHINE); + await this._storageService.flush(); + } + + async next(seeingBad: boolean): Promise<{ id: string; bad: boolean; } | undefined> { + if (!this._state) { + throw new Error('invalid state'); + } + // check if bad when all extensions are disabled + if (seeingBad && this._state.mid === 0 && this._state.high === this._state.extensions.length) { + return { bad: true, id: '' }; + } + // check if there is only one left + if (this._state.low === this._state.high - 1) { + await this.reset(); + return { id: this._state.extensions[this._state.low], bad: seeingBad }; + } + // the second half is disabled so if there is still bad it must be + // in the first half + const nextState = new BisectState( + this._state.extensions, + seeingBad ? this._state.low : this._state.mid, + seeingBad ? this._state.mid : this._state.high, + ); + this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(nextState), StorageScope.GLOBAL, StorageTarget.MACHINE); + await this._storageService.flush(); + return undefined; + } + + async reset(): Promise { + this._storageService.remove(ExtensionBisectService._storageKey, StorageScope.GLOBAL); + await this._storageService.flush(); + } +} + +registerSingleton(IExtensionBisectService, ExtensionBisectService, true); + +// --- bisect UI + +class ExtensionBisectUi { + + static ctxIsBisectActive = new RawContextKey('isExtensionBisectActive', false); + + constructor( + @IContextKeyService contextKeyService: IContextKeyService, + @IExtensionBisectService private readonly _extensionBisectService: IExtensionBisectService, + @INotificationService private readonly _notificationService: INotificationService, + @ICommandService private readonly _commandService: ICommandService, + ) { + if (_extensionBisectService.isActive) { + ExtensionBisectUi.ctxIsBisectActive.bindTo(contextKeyService).set(true); + this._showBisectPrompt(); + } + } + + private _showBisectPrompt(): void { + + const goodPrompt: IPromptChoice = { + label: 'Good now', + run: () => this._commandService.executeCommand('extension.bisect.next', false) + }; + const badPrompt: IPromptChoice = { + label: 'This is bad', + run: () => this._commandService.executeCommand('extension.bisect.next', true) + }; + const stop: IPromptChoice = { + label: 'Stop Bisect', + run: () => this._commandService.executeCommand('extension.bisect.stop') + }; + + this._notificationService.prompt( + Severity.Info, + localize('bisect', "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", this._extensionBisectService.disabledCount), + [goodPrompt, badPrompt, stop], + { sticky: true } + ); + } +} + +Registry.as(Extensions.Workbench).registerWorkbenchContribution( + ExtensionBisectUi, + LifecyclePhase.Restored +); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'extension.bisect.start', + title: localize('title.start', "Start Extension Bisect"), + category: localize('help', "Help"), + f1: true, + precondition: ExtensionBisectUi.ctxIsBisectActive.negate() + }); + } + + async run(accessor: ServicesAccessor): Promise { + const dialogService = accessor.get(IDialogService); + const hostService = accessor.get(IHostService); + const extensionManagement = accessor.get(IExtensionManagementService); + const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService); + const extensionsBisect = accessor.get(IExtensionBisectService); + + const disabled = new Set(extensionEnablementService.getDisabledExtensions().map(id => id.id)); + const extensions = (await extensionManagement.getInstalled(ExtensionType.User)).filter(ext => !disabled.has(ext.identifier.id)); + + const res = await dialogService.confirm({ + message: localize('msg.start', "Extension Bisect"), + detail: localize('detail.start', "Extension Bisect will use binary search to find an extension that causes a problem. During the process the window reloads repeatedly (~{0} times). Each time you must confirm if you are still seeing problems.", 2 + Math.log2(extensions.length) | 0), + primaryButton: localize('msg2', "Start Extension Bisect") + }); + + if (res.confirmed) { + await extensionsBisect.start(extensions); + hostService.reload(); + } + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'extension.bisect.next', + title: localize('title.isBad', "Continue Extension Bisect"), + category: localize('help', "Help"), + f1: true, + precondition: ExtensionBisectUi.ctxIsBisectActive + }); + } + + async run(accessor: ServicesAccessor, seeingBad: boolean | undefined): Promise { + const dialogService = accessor.get(IDialogService); + const hostService = accessor.get(IHostService); + const bisectService = accessor.get(IExtensionBisectService); + const productService = accessor.get(IProductService); + const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService); + const issueService = accessor.get(IWorkbenchIssueService); + + if (!bisectService.isActive) { + return; + } + if (seeingBad === undefined) { + const goodBadStopCancel = await this._checkForBad(dialogService, bisectService); + if (goodBadStopCancel === null) { + return; + } + seeingBad = goodBadStopCancel; + } + if (seeingBad === undefined) { + await bisectService.reset(); + hostService.reload(); + return; + } + const done = await bisectService.next(seeingBad); + if (!done) { + hostService.reload(); + return; + } + + if (done.bad) { + // DONE but nothing found + await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"), [], { + detail: localize('done.detail2', "Extension Bisect is done but no extension has been identified. This might be a problem with {0}.", productService.nameShort) + }); + + } else { + // DONE and identified extension + const res = await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"), + [localize('report', "Report Issue & Continue"), localize('done', "Continue")], + // [], + { + detail: localize('done.detail', "Extension Bisect is done and has identified {0} as the extension causing the problem.", done.id), + checkbox: { label: localize('done.disbale', "Keep this extension disabled"), checked: true }, + cancelId: 1 + } + ); + if (res.checkboxChecked) { + await extensionEnablementService.disableExtension({ id: done.id }, undefined); + } + if (res.choice === 0) { + issueService.openReporter({ extensionId: done.id }); + await timeout(750); // workaround for https://github.com/microsoft/vscode/issues/111871 + } + } + await bisectService.reset(); + hostService.reload(); + } + + private async _checkForBad(dialogService: IDialogService, bisectService: IExtensionBisectService): Promise { + const options = { + cancelId: 3, + detail: localize('bisect', "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", bisectService.disabledCount), + }; + const res = await dialogService.show( + Severity.Info, + localize('msg.next', "Extension Bisect"), + [localize('next.good', "Good now"), localize('next.bad', "This is bad"), localize('next.stop', "Stop Bisect"), localize('next.cancel', "Cancel")], + options + ); + switch (res.choice) { + case 0: return false; //good now + case 1: return true; //bad + case 2: return undefined; //stop + } + return null; //cancel + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'extension.bisect.stop', + title: localize('title.stop', "Stop Extension Bisect"), + category: localize('help', "Help"), + f1: true, + precondition: ExtensionBisectUi.ctxIsBisectActive + }); + } + + async run(accessor: ServicesAccessor): Promise { + const extensionsBisect = accessor.get(IExtensionBisectService); + const hostService = accessor.get(IHostService); + await extensionsBisect.reset(); + hostService.reload(); + } +}); diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 50d4d812..94d47c9c 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -24,6 +24,7 @@ import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/com import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect'; const SOURCE = 'IWorkbenchExtensionEnablementService'; @@ -50,6 +51,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench @ILifecycleService private readonly lifecycleService: ILifecycleService, @INotificationService private readonly notificationService: INotificationService, @IHostService hostService: IHostService, + @IExtensionBisectService private readonly extensionBisectService: IExtensionBisectService, ) { super(); this.storageManger = this._register(new StorageManager(storageService)); @@ -59,9 +61,9 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench // delay notification for extensions disabled until workbench restored if (this.allUserExtensionsDisabled) { this.lifecycleService.when(LifecyclePhase.Restored).then(() => { - this.notificationService.prompt(Severity.Info, localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ - label: localize('Reload', "Reload"), - run: () => hostService.reload() + this.notificationService.prompt(Severity.Info, localize('extensionsDisabled', "All installed extensions are temporarily disabled."), [{ + label: localize('Reload', "Reload and Enable Extensions"), + run: () => hostService.reload({ disableExtensions: false }) }]); }); } @@ -76,6 +78,9 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } getEnablementState(extension: IExtension): EnablementState { + if (this.extensionBisectService.isDisabledByBisect(extension)) { + return EnablementState.DisabledByEnvironemt; + } if (this._isDisabledInEnv(extension)) { return EnablementState.DisabledByEnvironemt; } @@ -210,14 +215,16 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } } if (extensionKind === 'web') { - const enableLocalWebWorker = this.configurationService.getValue(webWorkerExtHostConfig); - if (enableLocalWebWorker) { - // Web extensions are enabled on all configurations - return false; - } - if (this.extensionManagementServerService.localExtensionManagementServer === null) { - // Web extensions run only in the web - return false; + if (this.extensionManagementServerService.webExtensionManagementServer) { + if (server === this.extensionManagementServerService.webExtensionManagementServer) { + return false; + } + } else if (server === this.extensionManagementServerService.localExtensionManagementServer) { + const enableLocalWebWorker = this.configurationService.getValue(webWorkerExtHostConfig); + if (enableLocalWebWorker) { + // Web extensions are enabled on all configurations + return false; + } } } } diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts new file mode 100644 index 00000000..7ade5f59 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/browser/extensionUrlTrustService.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +class ExtensionUrlTrustService implements IExtensionUrlTrustService { + + declare readonly _serviceBrand: undefined; + + async isExtensionUrlTrusted(): Promise { + return false; + } +} + +registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index e51b2217..380898ba 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; +import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, ILocalExtension, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; @@ -29,6 +29,7 @@ export interface IWorkbenchExtensioManagementService extends IExtensionManagemen readonly _serviceBrand: undefined; installExtensions(extensions: IGalleryExtension[], installOptions?: InstallOptions): Promise; updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise; + getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null } export const enum EnablementState { @@ -96,7 +97,7 @@ export interface IWebExtensionsScannerService { scanExtensions(type?: ExtensionType): Promise; scanAndTranslateExtensions(type?: ExtensionType): Promise; scanAndTranslateSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise; - canAddExtension(galleryExtension: IGalleryExtension): Promise; + canAddExtension(galleryExtension: IGalleryExtension): boolean; addExtension(galleryExtension: IGalleryExtension): Promise; removeExtension(identifier: IExtensionIdentifier, version?: string): Promise; } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index 410c10e0..bd730af5 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -43,12 +43,13 @@ export class ExtensionManagementServerService implements IExtensionManagementSer extensionManagementService, get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); } }; - } else if (isWeb) { + } + if (isWeb) { const extensionManagementService = instantiationService.createInstance(WebExtensionManagementService); this.webExtensionManagementServer = { id: 'web', extensionManagementService, - label: localize('web', "Web") + label: localize('browser', "Browser") }; } } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index de7e301d..b1781e08 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -5,7 +5,7 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { - ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions + ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; @@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { CancellationToken } from 'vs/base/common/cancellation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; -import { prefersExecuteOnUI, canExecuteOnWorkspace, prefersExecuteOnWorkspace, canExecuteOnUI, prefersExecuteOnWeb, canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { prefersExecuteOnUI, getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { IDownloadService } from 'vs/platform/download/common/download'; @@ -23,7 +23,6 @@ import { flatten } from 'vs/base/common/arrays'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { canceled } from 'vs/base/common/errors'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; export class ExtensionManagementService extends Disposable implements IWorkbenchExtensioManagementService { @@ -45,7 +44,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench @IDownloadService protected readonly downloadService: IDownloadService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IDialogService private readonly dialogService: IDialogService, ) { super(); if (this.extensionManagementServerService.localExtensionManagementServer) { @@ -69,7 +68,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return flatten(result); } - async uninstall(extension: ILocalExtension): Promise { + async uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { const server = this.getServer(extension); if (!server) { return Promise.reject(`Invalid location ${extension.location.toString()}`); @@ -78,7 +77,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench if (isLanguagePackExtension(extension.manifest)) { return this.uninstallEverywhere(extension); } - return this.uninstallInServer(extension, server); + return this.uninstallInServer(extension, server, options); } return server.extensionManagementService.uninstall(extension); } @@ -102,7 +101,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return promise; } - private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise { + private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, options?: UninstallOptions): Promise { if (server === this.extensionManagementServerService.localExtensionManagementServer) { const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User); const dependentNonUIExtensions = installedExtensions.filter(i => !prefersExecuteOnUI(i.manifest, this.productService, this.configurationService) @@ -111,7 +110,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions))); } } - return server.extensionManagementService.uninstall(extension, force); + return server.extensionManagementService.uninstall(extension, options); } private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string { @@ -283,75 +282,56 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return Promise.reject(error); } - private getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | undefined { + getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null { // Only local server if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) { return this.extensionManagementServerService.localExtensionManagementServer; } - // 1. Install on preferred location - - // Install UI preferred extension on local server - if (prefersExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer; - } - // Install Workspace preferred extension on remote server - if (prefersExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer; - } - // Install Web preferred extension on web server - if (prefersExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { - return this.extensionManagementServerService.webExtensionManagementServer; + const extensionKind = getExtensionKind(manifest, this.productService, this.configurationService); + for (const kind of extensionKind) { + if (kind === 'ui' && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer; + } + if (kind === 'workspace' && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer; + } + if (kind === 'web' && this.extensionManagementServerService.webExtensionManagementServer) { + return this.extensionManagementServerService.webExtensionManagementServer; + } } - // 2. Install on supported location - - // Install UI supported extension on local server - if (canExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer; - } - // Install Workspace supported extension on remote server - if (canExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer; - } - // Install Web supported extension on web server - if (canExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { - return this.extensionManagementServerService.webExtensionManagementServer; - } - - return undefined; + // Local server can accept any extension. So return local server if not compatible server found. + return this.extensionManagementServerService.localExtensionManagementServer; } private async hasToFlagExtensionsMachineScoped(extensions: IGalleryExtension[]): Promise { if (!this.userDataAutoSyncEnablementService.isEnabled() || !this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions)) { return false; } - return this.instantiationService.invokeFunction(async accessor => { - const dialogService = accessor.get(IDialogService); - const result = await dialogService.show( - Severity.Info, - extensions.length === 1 ? localize('install extension', "Install Extension") : localize('install extensions', "Install Extensions"), - [ - localize('install', "Install"), - localize('install and do no sync', "Install (Do not sync)"), - localize('cancel', "Cancel"), - ], - { - cancelId: 2, - detail: extensions.length === 1 - ? localize('install single extension', "Would you like to install and synchronize '{0}' extension across your devices?", extensions[0].displayName) - : localize('install multiple extensions', "Would you like to install and synchronize extensions across your devices?") - } - ); - switch (result.choice) { - case 0: - return false; - case 1: - return true; + const result = await this.dialogService.show( + Severity.Info, + extensions.length === 1 ? localize('install extension', "Install Extension") : localize('install extensions', "Install Extensions"), + [ + localize('install', "Install"), + localize('install and do no sync', "Install (Do not sync)"), + localize('cancel', "Cancel"), + ], + { + cancelId: 2, + detail: extensions.length === 1 + ? localize('install single extension', "Would you like to install and synchronize '{0}' extension across your devices?", extensions[0].displayName) + : localize('install multiple extensions', "Would you like to install and synchronize extensions across your devices?") } - throw canceled(); - }); + ); + switch (result.choice) { + case 0: + return false; + case 1: + return true; + } + throw canceled(); } getExtensionsReport(): Promise { diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts index 1bf8114d..f0cc1bc8 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts @@ -25,6 +25,7 @@ import { Event } from 'vs/base/common/event'; import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { localize } from 'vs/nls'; import * as semver from 'vs/base/common/semver/semver'; +import { isArray } from 'vs/base/common/types'; interface IUserExtension { identifier: IExtensionIdentifier; @@ -125,25 +126,31 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten private async readDefaultUserWebExtensions(): Promise { const result: IStaticExtension[] = []; - const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions') || []; - for (const webExtension of defaultUserWebExtensions) { - const extensionLocation = URI.parse(webExtension.location); - const manifestLocation = joinPath(extensionLocation, 'package.json'); - const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None); - if (!isSuccess(context)) { - this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation); - continue; + const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions'); + if (isArray(defaultUserWebExtensions)) { + for (const webExtension of defaultUserWebExtensions) { + try { + const extensionLocation = URI.parse(webExtension.location); + const manifestLocation = joinPath(extensionLocation, 'package.json'); + const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None); + if (!isSuccess(context)) { + this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation); + continue; + } + const content = await asText(context); + if (!content) { + this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation); + continue; + } + const packageJSON = JSON.parse(content); + result.push({ + packageJSON, + extensionLocation, + }); + } catch (error) { + this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', webExtension); + } } - const content = await asText(context); - if (!content) { - this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation); - continue; - } - const packageJSON = JSON.parse(content); - result.push({ - packageJSON, - extensionLocation, - }); } return result; } @@ -231,12 +238,12 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten }; } - async canAddExtension(galleryExtension: IGalleryExtension): Promise { + canAddExtension(galleryExtension: IGalleryExtension): boolean { return !!galleryExtension.properties.webExtension && !!galleryExtension.webResource; } async addExtension(galleryExtension: IGalleryExtension): Promise { - if (!(await this.canAddExtension(galleryExtension))) { + if (!this.canAddExtension(galleryExtension)) { const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension is not a web extension.", galleryExtension.displayName || galleryExtension.name)); error.name = INSTALL_ERROR_NOT_SUPPORTED; throw error; diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts index a58a136a..e8a05aff 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts @@ -15,8 +15,8 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { IProductService } from 'vs/platform/product/common/productService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { joinPath } from 'vs/base/common/resources'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; export class ExtensionManagementService extends BaseExtensionManagementService { @@ -29,9 +29,9 @@ export class ExtensionManagementService extends BaseExtensionManagementService { @IDownloadService downloadService: IDownloadService, @IUserDataAutoSyncEnablementService userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, - @IInstantiationService instantiationService: IInstantiationService, + @IDialogService dialogService: IDialogService, ) { - super(extensionManagementServerService, extensionGalleryService, configurationService, productService, downloadService, userDataAutoSyncEnablementService, userDataSyncResourceEnablementService, instantiationService); + super(extensionManagementServerService, extensionGalleryService, configurationService, productService, downloadService, userDataAutoSyncEnablementService, userDataSyncResourceEnablementService, dialogService); } protected async installVSIX(vsix: URI, server: IExtensionManagementServer): Promise { diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService.ts new file mode 100644 index 00000000..3cabb495 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; + +class ExtensionUrlTrustService { + + declare readonly _serviceBrand: undefined; + + constructor(@IMainProcessService mainProcessService: IMainProcessService) { + return createChannelSender(mainProcessService.getChannel('extensionUrlTrust')); + } +} + +registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService); diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index fe48d4f1..7259d069 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -28,6 +28,8 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { mock } from 'vs/base/test/common/mock'; +import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect'; function createStorageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -61,7 +63,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService), instantiationService.get(ILifecycleService) || instantiationService.stub(ILifecycleService, new TestLifecycleService()), instantiationService.get(INotificationService) || instantiationService.stub(INotificationService, new TestNotificationService()), - instantiationService.get(IHostService) + instantiationService.get(IHostService), + new class extends mock() { isDisabledByBisect() { return false; } } ); } @@ -542,36 +545,48 @@ suite('ExtensionEnablementService Test', () => { assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), true); }); - test('test web extension on local server is disabled by kind', async () => { + test('test web extension on local server is disabled by kind when web worker is not enabled', async () => { instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: false }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(!testObject.isEnabled(localWorkspaceExtension)); + assert.equal(testObject.isEnabled(localWorkspaceExtension), false); assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); }); - test('test web extension on remote server is not disabled by kind when there is no local server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); - const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + test('test web extension on local server is not disabled by kind when web worker is enabled', async () => { + instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); + const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: true }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); + assert.equal(testObject.isEnabled(localWorkspaceExtension), true); assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); }); - test('test web extension with no server is not disabled by kind when there is no local server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); - const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) }); + test('test web extension on remote server is disabled by kind when web worker is not enabled', async () => { + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(anExtensionManagementServer('vscode-local', instantiationService), anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); + const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: 'vscode-remote' }) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: false }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); - assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); + assert.equal(testObject.isEnabled(localWorkspaceExtension), false); + assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); }); - test('test web extension with no server is not disabled by kind when there is no local and remote server', async () => { - instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, null, anExtensionManagementServer('web', instantiationService))); - const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) }); + test('test web extension on remote server is disabled by kind when web worker is enabled', async () => { + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(anExtensionManagementServer('vscode-local', instantiationService), anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); + const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: 'vscode-remote' }) }); + (instantiationService.get(IConfigurationService)).setUserConfiguration('extensions', { webWorker: true }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); - assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); + assert.equal(testObject.isEnabled(localWorkspaceExtension), false); + assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); + }); + + test('test web extension on web server is not disabled by kind', async () => { + instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(anExtensionManagementServer('vscode-local', instantiationService), anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService))); + const webExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: 'web' }) }); + testObject = new TestExtensionEnablementService(instantiationService); + assert.equal(testObject.isEnabled(webExtension), true); + assert.deepEqual(testObject.getEnablementState(webExtension), EnablementState.EnabledGlobally); }); }); @@ -595,7 +610,7 @@ function anExtensionManagementServerService(localExtensionManagementServer: IExt _serviceBrand: undefined, localExtensionManagementServer, remoteExtensionManagementServer, - webExtensionManagementServer: null, + webExtensionManagementServer, getExtensionManagementServer: (extension: IExtension) => { if (extension.location.scheme === Schemas.file) { return localExtensionManagementServer; diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts index ce9782b3..d2a6f001 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts @@ -7,8 +7,7 @@ import { distinct } from 'vs/base/common/arrays'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IExtensionIgnoredRecommendationsService, IgnoredRecommendationChangeNotification } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; @@ -35,12 +34,10 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement constructor( @IWorkpsaceExtensionsConfigService private readonly workpsaceExtensionsConfigService: IWorkpsaceExtensionsConfigService, @IStorageService private readonly storageService: IStorageService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, ) { super(); - storageKeysSyncRegistryService.registerStorageKey({ key: ignoredRecommendationsStorageKey, version: 1 }); this._globalIgnoredRecommendations = this.getCachedIgnoredRecommendations(); - this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + this._register(this.storageService.onDidChangeValue(e => this.onDidStorageChange(e))); this.initIgnoredWorkspaceRecommendations(); } @@ -72,7 +69,7 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement return ignoredRecommendations.map(e => e.toLowerCase()); } - private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { + private onDidStorageChange(e: IStorageValueChangeEvent): void { if (e.key === ignoredRecommendationsStorageKey && e.scope === StorageScope.GLOBAL && this.ignoredRecommendationsValue !== this.getStoredIgnoredRecommendationsValue() /* This checks if current window changed the value or not */) { this._ignoredRecommendationsValue = undefined; @@ -106,7 +103,7 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement } private setStoredIgnoredRecommendationsValue(value: string): void { - this.storageService.store(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL); + this.storageService.store(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL, StorageTarget.USER); } } diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts index 21fb2dcf..0d76094d 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts @@ -7,11 +7,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IStringDictionary } from 'vs/base/common/collections'; import { Event } from 'vs/base/common/event'; -export interface IExtensionsConfigContent { - recommendations: string[]; - unwantedRecommendations: string[]; -} - export type DynamicRecommendation = 'dynamic'; export type ConfigRecommendation = 'config'; export type ExecutableRecommendation = 'executable'; diff --git a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts index dfcdaf50..dc16b357 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts @@ -3,20 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce, distinct, flatten } from 'vs/base/common/arrays'; +import { distinct, flatten } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IFileService } from 'vs/platform/files/common/files'; +import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; +import { FileKind, IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { localize } from 'vs/nls'; +import { URI } from 'vs/base/common/uri'; +import { IJSONEditingService, IJSONValue } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { ResourceMap } from 'vs/base/common/map'; export const EXTENSIONS_CONFIG = '.vscode/extensions.json'; export interface IExtensionsConfigContent { - recommendations: string[]; - unwantedRecommendations: string[]; + recommendations?: string[]; + unwantedRecommendations?: string[]; } export const IWorkpsaceExtensionsConfigService = createDecorator('IWorkpsaceExtensionsConfigService'); @@ -26,8 +34,11 @@ export interface IWorkpsaceExtensionsConfigService { onDidChangeExtensionsConfigs: Event; getExtensionsConfigs(): Promise; + getRecommendations(): Promise; getUnwantedRecommendations(): Promise; + toggleRecommendation(extensionId: string): Promise; + toggleUnwantedRecommendation(extensionId: string): Promise; } export class WorkspaceExtensionsConfigService extends Disposable implements IWorkpsaceExtensionsConfigService { @@ -40,53 +51,217 @@ export class WorkspaceExtensionsConfigService extends Disposable implements IWor constructor( @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IFileService private readonly fileService: IFileService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService, + @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, ) { super(); - this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(e => this._onDidChangeExtensionsConfigs.fire())); + this._register(workspaceContextService.onDidChangeWorkspaceFolders(e => this._onDidChangeExtensionsConfigs.fire())); + this._register(fileService.onDidFilesChange(e => { + const workspace = workspaceContextService.getWorkspace(); + if ((workspace.configuration && e.affects(workspace.configuration)) + || workspace.folders.some(folder => e.affects(folder.toResource(EXTENSIONS_CONFIG))) + ) { + this._onDidChangeExtensionsConfigs.fire(); + } + })); } async getExtensionsConfigs(): Promise { const workspace = this.workspaceContextService.getWorkspace(); - const result = await Promise.all([ - this.resolveWorkspaceExtensionConfig(workspace), - ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)) - ]); - return coalesce(result); + const result: IExtensionsConfigContent[] = []; + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + if (workspaceExtensionsConfigContent) { + result.push(workspaceExtensionsConfigContent); + } + result.push(...await Promise.all(workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)))); + return result; + } + + async getRecommendations(): Promise { + const configs = await this.getExtensionsConfigs(); + return distinct(flatten(configs.map(c => c.recommendations ? c.recommendations.map(c => c.toLowerCase()) : []))); } async getUnwantedRecommendations(): Promise { const configs = await this.getExtensionsConfigs(); - return distinct(flatten(configs.map(c => c.unwantedRecommendations.map(c => c.toLowerCase())))); + return distinct(flatten(configs.map(c => c.unwantedRecommendations ? c.unwantedRecommendations.map(c => c.toLowerCase()) : []))); } - private async resolveWorkspaceExtensionConfig(workspace: IWorkspace): Promise { - try { - if (workspace.configuration) { - const content = await this.fileService.readFile(workspace.configuration); - const extensionsConfigContent = parse(content.value.toString())['extensions']; - return this.parseExtensionConfig(extensionsConfigContent); + async toggleRecommendation(extensionId: string): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + const workspaceFolderExtensionsConfigContents = new ResourceMap(); + await Promise.all(workspace.folders.map(async workspaceFolder => { + const extensionsConfigContent = await this.resolveWorkspaceFolderExtensionConfig(workspaceFolder); + workspaceFolderExtensionsConfigContents.set(workspaceFolder.uri, extensionsConfigContent); + })); + + const isWorkspaceRecommended = workspaceExtensionsConfigContent && workspaceExtensionsConfigContent.recommendations?.some(r => r === extensionId); + const recommendedWorksapceFolders = workspace.folders.filter(workspaceFolder => workspaceFolderExtensionsConfigContents.get(workspaceFolder.uri)?.recommendations?.some(r => r === extensionId)); + const isRecommended = isWorkspaceRecommended || recommendedWorksapceFolders.length > 0; + + const workspaceOrFolders = isRecommended + ? await this.pickWorkspaceOrFolders(recommendedWorksapceFolders, isWorkspaceRecommended ? workspace : undefined, localize('select for remove', "Remove extension recommendation from")) + : await this.pickWorkspaceOrFolders(workspace.folders, workspace.configuration ? workspace : undefined, localize('select for add', "Add extension recommendation to")); + + for (const workspaceOrWorkspaceFolder of workspaceOrFolders) { + if (IWorkspace.isIWorkspace(workspaceOrWorkspaceFolder)) { + await this.addOrRemoveWorkspaceRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceExtensionsConfigContent, !isRecommended); + } else { + await this.addOrRemoveWorkspaceFolderRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceFolderExtensionsConfigContents.get(workspaceOrWorkspaceFolder.uri)!, !isRecommended); } - } catch (e) { /* Ignore */ } - return null; + } } - private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { + async toggleUnwantedRecommendation(extensionId: string): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const workspaceExtensionsConfigContent = workspace.configuration ? await this.resolveWorkspaceExtensionConfig(workspace.configuration) : undefined; + const workspaceFolderExtensionsConfigContents = new ResourceMap(); + await Promise.all(workspace.folders.map(async workspaceFolder => { + const extensionsConfigContent = await this.resolveWorkspaceFolderExtensionConfig(workspaceFolder); + workspaceFolderExtensionsConfigContents.set(workspaceFolder.uri, extensionsConfigContent); + })); + + const isWorkspaceUnwanted = workspaceExtensionsConfigContent && workspaceExtensionsConfigContent.unwantedRecommendations?.some(r => r === extensionId); + const unWantedWorksapceFolders = workspace.folders.filter(workspaceFolder => workspaceFolderExtensionsConfigContents.get(workspaceFolder.uri)?.unwantedRecommendations?.some(r => r === extensionId)); + const isUnwanted = isWorkspaceUnwanted || unWantedWorksapceFolders.length > 0; + + const workspaceOrFolders = isUnwanted + ? await this.pickWorkspaceOrFolders(unWantedWorksapceFolders, isWorkspaceUnwanted ? workspace : undefined, localize('select for remove', "Remove extension recommendation from")) + : await this.pickWorkspaceOrFolders(workspace.folders, workspace.configuration ? workspace : undefined, localize('select for add', "Add extension recommendation to")); + + for (const workspaceOrWorkspaceFolder of workspaceOrFolders) { + if (IWorkspace.isIWorkspace(workspaceOrWorkspaceFolder)) { + await this.addOrRemoveWorkspaceUnwantedRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceExtensionsConfigContent, !isUnwanted); + } else { + await this.addOrRemoveWorkspaceFolderUnwantedRecommendation(extensionId, workspaceOrWorkspaceFolder, workspaceFolderExtensionsConfigContents.get(workspaceOrWorkspaceFolder.uri)!, !isUnwanted); + } + } + } + + private async addOrRemoveWorkspaceFolderRecommendation(extensionId: string, workspaceFolder: IWorkspaceFolder, extensionsConfigContent: IExtensionsConfigContent, add: boolean): Promise { + const values: IJSONValue[] = []; + if (add) { + values.push({ path: ['recommendations'], value: [...extensionsConfigContent.recommendations || [], extensionId] }); + if (extensionsConfigContent.unwantedRecommendations && extensionsConfigContent.unwantedRecommendations.some(e => e === extensionId)) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.recommendations) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + + if (values.length) { + return this.jsonEditingService.write(workspaceFolder.toResource(EXTENSIONS_CONFIG), values, true); + } + } + + private async addOrRemoveWorkspaceRecommendation(extensionId: string, workspace: IWorkspace, extensionsConfigContent: IExtensionsConfigContent | undefined, add: boolean): Promise { + const values: IJSONValue[] = []; + if (extensionsConfigContent) { + if (add) { + values.push({ path: ['recommendations'], value: [...extensionsConfigContent.recommendations || [], extensionId] }); + if (extensionsConfigContent.unwantedRecommendations && extensionsConfigContent.unwantedRecommendations.some(e => e === extensionId)) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.recommendations) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (add) { + values.push({ path: ['extensions'], value: { recommendations: [extensionId] } }); + } + + if (values.length) { + return this.jsonEditingService.write(workspace.configuration!, values, true); + } + } + + private async addOrRemoveWorkspaceFolderUnwantedRecommendation(extensionId: string, workspaceFolder: IWorkspaceFolder, extensionsConfigContent: IExtensionsConfigContent, add: boolean): Promise { + const values: IJSONValue[] = []; + if (add) { + values.push({ path: ['unwantedRecommendations'], value: [...extensionsConfigContent.unwantedRecommendations || [], extensionId] }); + if (extensionsConfigContent.recommendations && extensionsConfigContent.recommendations.some(e => e === extensionId)) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.unwantedRecommendations) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + if (values.length) { + return this.jsonEditingService.write(workspaceFolder.toResource(EXTENSIONS_CONFIG), values, true); + } + } + + private async addOrRemoveWorkspaceUnwantedRecommendation(extensionId: string, workspace: IWorkspace, extensionsConfigContent: IExtensionsConfigContent | undefined, add: boolean): Promise { + const values: IJSONValue[] = []; + if (extensionsConfigContent) { + if (add) { + values.push({ path: ['unwantedRecommendations'], value: [...extensionsConfigContent.unwantedRecommendations || [], extensionId] }); + if (extensionsConfigContent.recommendations && extensionsConfigContent.recommendations.some(e => e === extensionId)) { + values.push({ path: ['recommendations'], value: extensionsConfigContent.recommendations.filter(e => e !== extensionId) }); + } + } else if (extensionsConfigContent.unwantedRecommendations) { + values.push({ path: ['unwantedRecommendations'], value: extensionsConfigContent.unwantedRecommendations.filter(e => e !== extensionId) }); + } + } else if (add) { + values.push({ path: ['extensions'], value: { unwantedRecommendations: [extensionId] } }); + } + + if (values.length) { + return this.jsonEditingService.write(workspace.configuration!, values, true); + } + } + + private async pickWorkspaceOrFolders(workspaceFolders: IWorkspaceFolder[], workspace: IWorkspace | undefined, placeHolder: string): Promise<(IWorkspace | IWorkspaceFolder)[]> { + const workspaceOrFolders = workspace ? [...workspaceFolders, workspace] : [...workspaceFolders]; + if (workspaceOrFolders.length === 1) { + return workspaceOrFolders; + } + + const folderPicks: (IQuickPickItem & { workspaceOrFolder: IWorkspace | IWorkspaceFolder } | IQuickPickSeparator)[] = workspaceFolders.map(workspaceFolder => { + return { + label: workspaceFolder.name, + description: localize('workspace folder', "Workspace Folder"), + workspaceOrFolder: workspaceFolder, + iconClasses: getIconClasses(this.modelService, this.modeService, workspaceFolder.uri, FileKind.ROOT_FOLDER) + }; + }); + + if (workspace) { + folderPicks.push({ type: 'separator' }); + folderPicks.push({ + label: localize('workspace', "Workspace"), + workspaceOrFolder: workspace, + }); + } + + const result = await this.quickInputService.pick(folderPicks, { placeHolder, canPickMany: true }) || []; + return result.map(r => r.workspaceOrFolder!); + } + + private async resolveWorkspaceExtensionConfig(workspaceConfigurationResource: URI): Promise { + try { + const content = await this.fileService.readFile(workspaceConfigurationResource); + const extensionsConfigContent = parse(content.value.toString())['extensions']; + return extensionsConfigContent ? this.parseExtensionConfig(extensionsConfigContent) : undefined; + } catch (e) { /* Ignore */ } + return undefined; + } + + private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { try { const content = await this.fileService.readFile(workspaceFolder.toResource(EXTENSIONS_CONFIG)); const extensionsConfigContent = parse(content.value.toString()); return this.parseExtensionConfig(extensionsConfigContent); } catch (e) { /* ignore */ } - return null; + return {}; } - private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent | undefined): IExtensionsConfigContent | null { - if (extensionsConfigContent) { - return { - recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), - unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) - }; - } - return null; + private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent): IExtensionsConfigContent { + return { + recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), + unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) + }; } } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 1dff19bf..c6eb7829 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -54,7 +54,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten new ExtensionRunningLocationClassifier( productService, configurationService, - (extensionKinds, isInstalledLocally, isInstalledRemotely) => this._pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely) + (extensionKinds, isInstalledLocally, isInstalledRemotely) => ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely) ), instantiationService, notificationService, @@ -137,11 +137,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten }; } - private _pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation { + public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation { + let canRunRemotely = false; for (const extensionKind of extensionKinds) { if (extensionKind === 'ui' && isInstalledRemotely) { - // ui extensions run remotely if possible - return ExtensionRunningLocation.Remote; + // ui extensions run remotely if possible (but only as a last resort) + canRunRemotely = true; } if (extensionKind === 'workspace' && isInstalledRemotely) { // workspace extensions run remotely if possible @@ -152,7 +153,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten return ExtensionRunningLocation.LocalWebWorker; } } - return ExtensionRunningLocation.None; + return (canRunRemotely ? ExtensionRunningLocation.Remote : ExtensionRunningLocation.None); } protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] { diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index 207a43ec..129ab322 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -13,7 +13,7 @@ import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbe import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IURLHandler, IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -26,24 +26,25 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; const URL_TO_HANDLE = 'extensionUrlHandler.urlToHandle'; -const CONFIRMED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds'; -const CONFIRMED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions'; +const USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds'; +const USER_TRUSTED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions'; function isExtensionId(value: string): boolean { return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value); } -class ConfirmedExtensionIdStorage { +class UserTrustedExtensionIdStorage { get extensions(): string[] { - const confirmedExtensionIdsJson = this.storageService.get(CONFIRMED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); + const userTrustedExtensionIdsJson = this.storageService.get(USER_TRUSTED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); try { - return JSON.parse(confirmedExtensionIdsJson); + return JSON.parse(userTrustedExtensionIdsJson); } catch { return []; } @@ -60,7 +61,7 @@ class ConfirmedExtensionIdStorage { } set(ids: string[]): void { - this.storageService.store(CONFIRMED_EXTENSIONS_STORAGE_KEY, JSON.stringify(ids), StorageScope.GLOBAL); + this.storageService.store(USER_TRUSTED_EXTENSIONS_STORAGE_KEY, JSON.stringify(ids), StorageScope.GLOBAL, StorageTarget.MACHINE); } } @@ -87,7 +88,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { private extensionHandlers = new Map(); private uriBuffer = new Map(); - private storage: ConfirmedExtensionIdStorage; + private userTrustedExtensionsStorage: UserTrustedExtensionIdStorage; private disposable: IDisposable; constructor( @@ -101,9 +102,10 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IProgressService private readonly progressService: IProgressService + @IProgressService private readonly progressService: IProgressService, + @IExtensionUrlTrustService private readonly extensionUrlTrustService: IExtensionUrlTrustService ) { - this.storage = new ConfirmedExtensionIdStorage(storageService); + this.userTrustedExtensionsStorage = new UserTrustedExtensionIdStorage(storageService); const interval = setInterval(() => this.garbageCollect(), THIRTY_SECONDS); const urlToHandleValue = this.storageService.get(URL_TO_HANDLE, StorageScope.WORKSPACE); @@ -118,7 +120,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { ); const cache = ExtensionUrlBootstrapHandler.cache; - setTimeout(() => cache.forEach(uri => this.handleURL(uri))); + setTimeout(() => cache.forEach(([uri, option]) => this.handleURL(uri, option))); } async handleURL(uri: URI, options?: IOpenURLOptions): Promise { @@ -135,14 +137,11 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return true; } - let showConfirm: boolean; - if (options && options.trusted) { - showConfirm = false; - } else { - showConfirm = !this.isConfirmed(ExtensionIdentifier.toKey(extensionId)); - } + const trusted = options?.trusted + || (options?.originalUrl ? await this.extensionUrlTrustService.isExtensionUrlTrusted(extensionId, options.originalUrl) : false) + || this.didUserTrustExtension(ExtensionIdentifier.toKey(extensionId)); - if (showConfirm) { + if (!trusted) { let uriString = uri.toString(false); if (uriString.length > 40) { @@ -164,7 +163,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } if (result.checkboxChecked) { - this.storage.add(ExtensionIdentifier.toKey(extensionId)); + this.userTrustedExtensionsStorage.add(ExtensionIdentifier.toKey(extensionId)); } } @@ -293,7 +292,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } private async reloadAndHandle(url: URI): Promise { - this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE); + this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE, StorageTarget.MACHINE); await this.hostService.reload(); } @@ -313,22 +312,22 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.uriBuffer = uriBuffer; } - private isConfirmed(id: string): boolean { - if (this.storage.has(id)) { + private didUserTrustExtension(id: string): boolean { + if (this.userTrustedExtensionsStorage.has(id)) { return true; } - return this.getConfirmedExtensionIdsFromConfiguration().indexOf(id) > -1; + return this.getConfirmedTrustedExtensionIdsFromConfiguration().indexOf(id) > -1; } - private getConfirmedExtensionIdsFromConfiguration(): Array { - const confirmedExtensionIds = this.configurationService.getValue>(CONFIRMED_EXTENSIONS_CONFIGURATION_KEY); + private getConfirmedTrustedExtensionIdsFromConfiguration(): Array { + const trustedExtensionIds = this.configurationService.getValue>(USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY); - if (!Array.isArray(confirmedExtensionIds)) { + if (!Array.isArray(trustedExtensionIds)) { return []; } - return confirmedExtensionIds; + return trustedExtensionIds; } dispose(): void { @@ -346,10 +345,10 @@ registerSingleton(IExtensionUrlHandler, ExtensionUrlHandler); */ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandler { - private static _cache: URI[] = []; + private static _cache: [URI, IOpenURLOptions | undefined][] = []; private static disposable: IDisposable; - static get cache(): URI[] { + static get cache(): [URI, IOpenURLOptions | undefined][] { ExtensionUrlBootstrapHandler.disposable.dispose(); const result = ExtensionUrlBootstrapHandler._cache; @@ -361,12 +360,12 @@ class ExtensionUrlBootstrapHandler implements IWorkbenchContribution, IURLHandle ExtensionUrlBootstrapHandler.disposable = urlService.registerHandler(this); } - async handleURL(uri: URI): Promise { + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { if (!isExtensionId(uri.authority)) { return false; } - ExtensionUrlBootstrapHandler._cache.push(uri); + ExtensionUrlBootstrapHandler._cache.push([uri, options]); return true; } } @@ -391,7 +390,7 @@ class ManageAuthorizedExtensionURIsAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const storageService = accessor.get(IStorageService); const quickInputService = accessor.get(IQuickInputService); - const storage = new ConfirmedExtensionIdStorage(storageService); + const storage = new UserTrustedExtensionIdStorage(storageService); const items = storage.extensions.map(label => ({ label, picked: true } as IQuickPickItem)); if (items.length === 0) { diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index e6b9d6fc..b91ba18a 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -60,7 +60,6 @@ export class ExtensionHostMain { const instaService: IInstantiationService = new InstantiationService(services, true); - // todo@joh // ugly self - inject const terminalService = instaService.invokeFunction(accessor => accessor.get(IExtHostTerminalService)); this._disposables.add(terminalService); @@ -71,7 +70,6 @@ export class ExtensionHostMain { logService.info('extension host started'); logService.trace('initData', initData); - // todo@joh // ugly self - inject // must call initialize *after* creating the extension service // because `initialize` itself creates instances that depend on it diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index a68c6114..f974db68 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -273,18 +273,14 @@ export class CachedExtensionScanner { finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(builtinExtensions, extraBuiltinExtensions); } - const userExtensions = ( - !environmentService.extensionsPath - ? Promise.resolve([]) - : this._scanExtensionsWithCache( - hostService, - notificationService, - environmentService, - USER_MANIFEST_CACHE_FILE, - new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations), - log - ) - ); + const userExtensions = (this._scanExtensionsWithCache( + hostService, + notificationService, + environmentService, + USER_MANIFEST_CACHE_FILE, + new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations), + log + )); // Always load developed extensions while extensions development let developedExtensions: Promise = Promise.resolve([]); diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 87fee781..3050bc6e 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -468,9 +468,12 @@ let _caCertificates: ReturnType | Promise; async function getCaCertificates(extHostLogService: ILogService) { if (!_caCertificates) { _caCertificates = readCaCertificates() - .then(res => res && res.certs.length ? res : undefined) + .then(res => { + extHostLogService.debug('ProxyResolver#getCaCertificates count', res && res.certs.length); + return res && res.certs.length ? res : undefined; + }) .catch(err => { - extHostLogService.error('ProxyResolver#getCertificates', toErrorMessage(err)); + extHostLogService.error('ProxyResolver#getCaCertificates error', toErrorMessage(err)); return undefined; }); } diff --git a/src/vs/workbench/services/extensions/test/electron-browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/electron-browser/extensionService.test.ts new file mode 100644 index 00000000..77a803df --- /dev/null +++ b/src/vs/workbench/services/extensions/test/electron-browser/extensionService.test.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ExtensionService as BrowserExtensionService } from 'vs/workbench/services/extensions/browser/extensionService'; +import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService'; + +suite('BrowserExtensionService', () => { + test('pickRunningLocation', () => { + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], false, true), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation([], true, true), ExtensionRunningLocation.None); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true), ExtensionRunningLocation.LocalWebWorker); + + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker); + + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true), ExtensionRunningLocation.Remote); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true), ExtensionRunningLocation.LocalWebWorker); + + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false), ExtensionRunningLocation.None); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true), ExtensionRunningLocation.Remote); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false), ExtensionRunningLocation.LocalWebWorker); + assert.deepEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true), ExtensionRunningLocation.Remote); + }); +}); diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts index b39a5cbb..f5c55522 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts @@ -8,14 +8,20 @@ let MonacoEnvironment = (self).MonacoEnvironment; let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../../../'; + const trustedTypesPolicy = self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value }); + if (typeof (self).define !== 'function' || !(self).define.amd) { - importScripts(monacoBaseUrl + 'vs/loader.js'); + let loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js'; + if (trustedTypesPolicy) { + loaderSrc = trustedTypesPolicy.createScriptURL(loaderSrc); + } + importScripts(loaderSrc as string); } require.config({ baseUrl: monacoBaseUrl, catchError: true, - createTrustedScriptURL: (value: string) => value + trustedTypesPolicy }); require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err)); diff --git a/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html index a99d3df2..392b4b4a 100644 --- a/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html @@ -1,7 +1,7 @@ - +