mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-24 16:35:55 +01:00
Merge branch 'main' into fix-maxTokenizationLineLength-for-monaco
This commit is contained in:
@@ -248,6 +248,7 @@
|
||||
"url",
|
||||
"util",
|
||||
"v8-inspect-profiler",
|
||||
"vscode-policy-watcher",
|
||||
"vscode-proxy-agent",
|
||||
"vscode-regexpp",
|
||||
"vscode-textmate",
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
name: Basic checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
main:
|
||||
if: github.ref != 'refs/heads/main'
|
||||
name: Compilation, Unit and Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# TODO: rename azure-pipelines/linux/xvfb.init to github-actions
|
||||
- name: Setup Build Environment
|
||||
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
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules21-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules21-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn directory
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
- name: Compile and Download
|
||||
run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions
|
||||
|
||||
- name: Compile Integration Tests
|
||||
run: yarn --cwd test/integration/browser compile
|
||||
|
||||
- name: Run Unit Tests
|
||||
id: electron-unit-tests
|
||||
run: DISPLAY=:10 ./scripts/test.sh
|
||||
|
||||
- name: Run Integration Tests (Electron)
|
||||
id: electron-integration-tests
|
||||
run: DISPLAY=:10 ./scripts/test-integration.sh
|
||||
|
||||
hygiene:
|
||||
if: github.ref != 'refs/heads/main'
|
||||
name: Hygiene and Layering
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules21-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules21-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn directory
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
run: yarn --frozen-lockfile --network-timeout 180000
|
||||
|
||||
- name: Download Playwright
|
||||
run: yarn playwright-install
|
||||
|
||||
- name: Run Hygiene Checks
|
||||
run: yarn gulp hygiene
|
||||
|
||||
- name: Run Valid Layers Checks
|
||||
run: yarn valid-layers-check
|
||||
|
||||
- name: Compile /build/
|
||||
run: yarn --cwd build compile
|
||||
|
||||
- name: Check clean git state
|
||||
run: ./.github/workflows/check-clean-git-state.sh
|
||||
|
||||
- name: Run eslint
|
||||
run: yarn eslint
|
||||
|
||||
- name: Run vscode-dts Compile Checks
|
||||
run: yarn vscode-dts-compile-check
|
||||
|
||||
- name: Run Trusted Types Checks
|
||||
run: yarn tsec-compile-check
|
||||
|
||||
warm-cache:
|
||||
name: Warm up node modules cache
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules21-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules21-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache yarn directory
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarnCacheDirPath.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-yarnCacheDir-
|
||||
- name: Execute yarn
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
run: yarn --frozen-lockfile --network-timeout 180000
|
||||
@@ -28,9 +28,11 @@ jobs:
|
||||
uses: ./actions/new-release
|
||||
with:
|
||||
label: new release
|
||||
token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
|
||||
appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}}
|
||||
labelColor: "006b75"
|
||||
labelDescription: Issues found in a recent release of VS Code
|
||||
oldVersionMessage: "Thanks for creating this issue! It looks like you may be using an old version of VS Code, the latest stable release is {currentVersion}. Please try upgrading to the latest version and checking whether this issue remains.\n\nHappy Coding!"
|
||||
days: 5
|
||||
|
||||
- name: Run Clipboard Labeler
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
name: PR Chat
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, ready_for_review]
|
||||
types: [opened, ready_for_review, closed]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !github.event.pull_request.draft }}
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
@@ -20,4 +21,5 @@ jobs:
|
||||
with:
|
||||
token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
|
||||
slack_token: ${{ secrets.SLACK_TOKEN }}
|
||||
slack_bot_name: "VSCodeBot"
|
||||
notification_channel: codereview
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\n\n$MILESTONE=milestone:\"April 2022\""
|
||||
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\r\n\r\n$MILESTONE=milestone:\"May 2022\""
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal\n\n$MILESTONE=milestone:\"April 2022\"\n\n$MINE=assignee:@me"
|
||||
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal\n\n$MILESTONE=milestone:\"May 2022\"\n\n$MINE=assignee:@me"
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev 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-emmet-helper repo:microsoft/vscode-jupyter repo:microsoft/vscode-python\n$milestone=milestone:\"March 2022\""
|
||||
"value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev 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-emmet-helper repo:microsoft/vscode-jupyter repo:microsoft/vscode-python\n$milestone=milestone:\"May 2022\""
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
|
||||
+4
-4
@@ -7,7 +7,7 @@
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "repo:microsoft/vscode-dev milestone:\"December 2021\" is:open"
|
||||
"value": "repo:microsoft/vscode-dev milestone:\"May 2022\" is:open"
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
@@ -32,11 +32,11 @@
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "repo:microsoft/vscode-remote-repositories-github milestone:\"December 2021\" is:open"
|
||||
"value": "repo:microsoft/vscode-remote-repositories-github milestone:\"May 2022\" is:open"
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "repo:microsoft/vscode-remotehub milestone:\"December 2021\" is:open"
|
||||
"value": "repo:microsoft/vscode-remotehub milestone:\"May 2022\" is:open"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
disturl "https://electronjs.org/headers"
|
||||
target "17.4.3"
|
||||
target "17.4.4"
|
||||
runtime "electron"
|
||||
build_from_source "true"
|
||||
|
||||
@@ -107,6 +107,14 @@ vscode-encrypt/binding.gyp
|
||||
vscode-encrypt/README.md
|
||||
!vscode-encrypt/build/Release/vscode-encrypt-native.node
|
||||
|
||||
vscode-policy-watcher/build/**
|
||||
vscode-policy-watcher/.husky/**
|
||||
vscode-policy-watcher/src/**
|
||||
vscode-policy-watcher/binding.gyp
|
||||
vscode-policy-watcher/README.md
|
||||
vscode-policy-watcher/index.d.ts
|
||||
!vscode-policy-watcher/build/Release/vscode-policy-watcher.node
|
||||
|
||||
vscode-windows-ca-certs/**/*
|
||||
!vscode-windows-ca-certs/package.json
|
||||
!vscode-windows-ca-certs/**/*.node
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
parameters:
|
||||
- name: VSCODE_QUALITY
|
||||
type: string
|
||||
- name: VSCODE_RUN_UNIT_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_RUN_INTEGRATION_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_RUN_SMOKE_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
@@ -165,91 +174,126 @@ steps:
|
||||
VSCODE_ARCH=$(VSCODE_ARCH) 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)
|
||||
timeoutInMinutes: 15
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
./scripts/test.sh --build --tfs "Unit Tests"
|
||||
displayName: Run unit tests (Electron)
|
||||
timeoutInMinutes: 15
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
yarn test-node --build
|
||||
displayName: Run unit tests (node.js)
|
||||
timeoutInMinutes: 15
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
yarn test-node --build
|
||||
displayName: Run unit tests (node.js)
|
||||
timeoutInMinutes: 15
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests"
|
||||
displayName: Run unit tests (Browser, Chromium & Webkit)
|
||||
timeoutInMinutes: 30
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests"
|
||||
displayName: Run unit tests (Browser, Chromium & Webkit)
|
||||
timeoutInMinutes: 30
|
||||
|
||||
- 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-$(VSCODE_ARCH)" \
|
||||
./scripts/test-integration.sh --build --tfs "Integration Tests"
|
||||
displayName: Run integration tests (Electron)
|
||||
timeoutInMinutes: 20
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
yarn gulp \
|
||||
compile-extension:css-language-features-server \
|
||||
compile-extension:emmet \
|
||||
compile-extension:git \
|
||||
compile-extension:github-authentication \
|
||||
compile-extension:html-language-features-server \
|
||||
compile-extension:ipynb \
|
||||
compile-extension:json-language-features-server \
|
||||
compile-extension:markdown-language-features \
|
||||
compile-extension-media \
|
||||
compile-extension:microsoft-authentication \
|
||||
compile-extension:typescript-language-features \
|
||||
compile-extension:vscode-api-tests \
|
||||
compile-extension:vscode-colorize-tests \
|
||||
compile-extension:vscode-custom-editor-tests \
|
||||
compile-extension:vscode-notebook-tests \
|
||||
compile-extension:vscode-test-resolver
|
||||
displayName: Build integration tests
|
||||
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-darwin-$(VSCODE_ARCH)" \
|
||||
./scripts/test-web-integration.sh --browser webkit
|
||||
displayName: Run integration tests (Browser, Webkit)
|
||||
timeoutInMinutes: 20
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, 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-$(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-$(VSCODE_ARCH)" \
|
||||
./scripts/test-integration.sh --build --tfs "Integration Tests"
|
||||
displayName: Run integration tests (Electron)
|
||||
timeoutInMinutes: 20
|
||||
|
||||
- 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-$(VSCODE_ARCH)" \
|
||||
./scripts/test-remote-integration.sh
|
||||
displayName: Run integration tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \
|
||||
./scripts/test-web-integration.sh --browser webkit
|
||||
displayName: Run integration tests (Browser, Webkit)
|
||||
timeoutInMinutes: 20
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
displayName: Diagnostics before smoke test run
|
||||
continueOnError: true
|
||||
condition: succeededOrFailed()
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- 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-$(VSCODE_ARCH)" \
|
||||
./scripts/test-remote-integration.sh
|
||||
displayName: Run integration tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --web --tracing --headless
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Browser, Chromium)
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
displayName: Diagnostics before smoke test run
|
||||
continueOnError: true
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Electron)
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --web --tracing --headless
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Browser, Chromium)
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Remote)
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Electron)
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
displayName: Diagnostics after smoke test run
|
||||
continueOnError: true
|
||||
condition: succeededOrFailed()
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
yarn gulp compile-extension:vscode-test-resolver
|
||||
APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Remote)
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
displayName: Diagnostics after smoke test run
|
||||
continueOnError: true
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
parameters:
|
||||
- name: VSCODE_QUALITY
|
||||
type: string
|
||||
- name: VSCODE_RUN_UNIT_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_RUN_INTEGRATION_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_RUN_SMOKE_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
@@ -113,16 +122,6 @@ steps:
|
||||
- script: |
|
||||
set -e
|
||||
export npm_config_arch=$(NPM_ARCH)
|
||||
# node-gyp@9.0.0 shipped with node@16.15.0 starts using config.gypi
|
||||
# from the custom headers path if dist-url option was set instead of
|
||||
# using the config value from the process. Electron builds with pointer compression
|
||||
# enabled for x64 and arm64, but incorrectly ships a single copy of config.gypi
|
||||
# with v8_enable_pointer_compression option always set for all target architectures.
|
||||
# We use the force_process_config option to use the config.gypi from the
|
||||
# nodejs process executing npm for 32-bit architectures.
|
||||
if [ "$NPM_ARCH" = "armv7l" ]; then
|
||||
export npm_config_force_process_config="true"
|
||||
fi
|
||||
|
||||
if [ -z "$CC" ] || [ -z "$CXX" ]; then
|
||||
# Download clang based on chromium revision used by vscode
|
||||
@@ -221,104 +220,139 @@ steps:
|
||||
stat $ELECTRON_ROOT/chrome-sandbox
|
||||
displayName: Change setuid helper binary permission
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
./scripts/test.sh --build --tfs "Unit Tests"
|
||||
displayName: Run unit tests (Electron)
|
||||
timeoutInMinutes: 15
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
./scripts/test.sh --build --tfs "Unit Tests"
|
||||
displayName: Run unit tests (Electron)
|
||||
timeoutInMinutes: 15
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
yarn test-node --build
|
||||
displayName: Run unit tests (node.js)
|
||||
timeoutInMinutes: 15
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
yarn test-node --build
|
||||
displayName: Run unit tests (node.js)
|
||||
timeoutInMinutes: 15
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
DEBUG=*browser* yarn test-browser-no-install --build --browser chromium --tfs "Browser Unit Tests"
|
||||
displayName: Run unit tests (Browser, Chromium)
|
||||
timeoutInMinutes: 15
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
DEBUG=*browser* yarn test-browser-no-install --build --browser chromium --tfs "Browser Unit Tests"
|
||||
displayName: Run unit tests (Browser, Chromium)
|
||||
timeoutInMinutes: 15
|
||||
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)" \
|
||||
./scripts/test-integration.sh --build --tfs "Integration Tests"
|
||||
displayName: Run integration tests (Electron)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
yarn gulp \
|
||||
compile-extension:css-language-features-server \
|
||||
compile-extension:emmet \
|
||||
compile-extension:git \
|
||||
compile-extension:github-authentication \
|
||||
compile-extension:html-language-features-server \
|
||||
compile-extension:ipynb \
|
||||
compile-extension:json-language-features-server \
|
||||
compile-extension:markdown-language-features \
|
||||
compile-extension-media \
|
||||
compile-extension:microsoft-authentication \
|
||||
compile-extension:typescript-language-features \
|
||||
compile-extension:vscode-api-tests \
|
||||
compile-extension:vscode-colorize-tests \
|
||||
compile-extension:vscode-custom-editor-tests \
|
||||
compile-extension:vscode-notebook-tests \
|
||||
compile-extension:vscode-test-resolver
|
||||
displayName: Build integration tests
|
||||
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)" \
|
||||
./scripts/test-web-integration.sh --browser chromium
|
||||
displayName: Run integration tests (Browser, Chromium)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, 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-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)" \
|
||||
./scripts/test-integration.sh --build --tfs "Integration Tests"
|
||||
displayName: Run integration tests (Electron)
|
||||
timeoutInMinutes: 20
|
||||
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_APP_NAME="$APP_NAME" \
|
||||
INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \
|
||||
./scripts/test-remote-integration.sh
|
||||
displayName: Run integration tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \
|
||||
./scripts/test-web-integration.sh --browser chromium
|
||||
displayName: Run integration tests (Browser, Chromium)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
cat /proc/sys/fs/inotify/max_user_watches
|
||||
lsof | wc -l
|
||||
displayName: Diagnostics before smoke test run (processes, max_user_watches, number of opened file handles)
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- 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)" \
|
||||
./scripts/test-remote-integration.sh
|
||||
displayName: Run integration tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
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)" \
|
||||
yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Browser, Chromium)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
cat /proc/sys/fs/inotify/max_user_watches
|
||||
lsof | wc -l
|
||||
displayName: Diagnostics before smoke test run (processes, max_user_watches, number of opened file handles)
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)
|
||||
yarn smoketest-no-compile --tracing --build "$APP_PATH"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Electron)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Browser, Chromium)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --tracing --remote --build "$APP_PATH"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Remote)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)
|
||||
yarn smoketest-no-compile --tracing --build "$APP_PATH"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Electron)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
cat /proc/sys/fs/inotify/max_user_watches
|
||||
lsof | wc -l
|
||||
displayName: Diagnostics after smoke test run (processes, max_user_watches, number of opened file handles)
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
yarn gulp compile-extension:vscode-test-resolver
|
||||
APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)
|
||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \
|
||||
yarn smoketest-no-compile --tracing --remote --build "$APP_PATH"
|
||||
timeoutInMinutes: 20
|
||||
displayName: Run smoke tests (Remote)
|
||||
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- script: |
|
||||
set -e
|
||||
ps -ef
|
||||
cat /proc/sys/fs/inotify/max_user_watches
|
||||
lsof | wc -l
|
||||
displayName: Diagnostics after smoke test run (processes, max_user_watches, number of opened file handles)
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
trigger: none
|
||||
trigger:
|
||||
- main
|
||||
- release/*
|
||||
|
||||
pr:
|
||||
branches:
|
||||
@@ -19,7 +21,7 @@ variables:
|
||||
- name: skipComponentGovernanceDetection
|
||||
value: true
|
||||
- name: ENABLE_TERRAPIN
|
||||
value: true
|
||||
value: false
|
||||
- name: VSCODE_PUBLISH
|
||||
value: false
|
||||
- name: VSCODE_QUALITY
|
||||
@@ -31,8 +33,7 @@ stages:
|
||||
- stage: Compile
|
||||
jobs:
|
||||
- job: Compile
|
||||
pool:
|
||||
vmImage: ubuntu-18.04
|
||||
pool: vscode-1es-vscode-linux-18.04
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
@@ -42,8 +43,7 @@ stages:
|
||||
|
||||
- stage: LinuxServerDependencies
|
||||
dependsOn: []
|
||||
pool:
|
||||
vmImage: ubuntu-18.04
|
||||
pool: vscode-1es-vscode-linux-18.04
|
||||
jobs:
|
||||
- job: x64
|
||||
container: centos7-devtoolset8-x64
|
||||
@@ -58,10 +58,10 @@ stages:
|
||||
- stage: Windows
|
||||
dependsOn:
|
||||
- Compile
|
||||
pool:
|
||||
vmImage: windows-2019
|
||||
pool: vscode-1es-vscode-windows-2019
|
||||
jobs:
|
||||
- job: Windows
|
||||
- job: WindowsUnitTests
|
||||
displayName: Unit Tests
|
||||
timeoutInMinutes: 120
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
@@ -69,15 +69,42 @@ stages:
|
||||
- template: win32/product-build-win32.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: true
|
||||
VSCODE_RUN_INTEGRATION_TESTS: false
|
||||
VSCODE_RUN_SMOKE_TESTS: false
|
||||
- job: WindowsIntegrationTests
|
||||
displayName: Integration Tests
|
||||
timeoutInMinutes: 120
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: false
|
||||
VSCODE_RUN_INTEGRATION_TESTS: true
|
||||
VSCODE_RUN_SMOKE_TESTS: false
|
||||
- job: WindowsSmokeTests
|
||||
displayName: Smoke Tests
|
||||
timeoutInMinutes: 120
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: win32/product-build-win32.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: false
|
||||
VSCODE_RUN_INTEGRATION_TESTS: false
|
||||
VSCODE_RUN_SMOKE_TESTS: true
|
||||
|
||||
- stage: Linux
|
||||
dependsOn:
|
||||
- Compile
|
||||
- LinuxServerDependencies
|
||||
pool:
|
||||
vmImage: ubuntu-18.04
|
||||
pool: vscode-1es-vscode-linux-18.04
|
||||
jobs:
|
||||
- job: Linuxx64
|
||||
- job: Linuxx64UnitTest
|
||||
displayName: Unit Tests
|
||||
container: vscode-bionic-x64
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
@@ -87,6 +114,37 @@ stages:
|
||||
- template: linux/product-build-linux-client.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: true
|
||||
VSCODE_RUN_INTEGRATION_TESTS: false
|
||||
VSCODE_RUN_SMOKE_TESTS: false
|
||||
- job: Linuxx64IntegrationTest
|
||||
displayName: Integration Tests
|
||||
container: vscode-bionic-x64
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
NPM_ARCH: x64
|
||||
DISPLAY: ":10"
|
||||
steps:
|
||||
- template: linux/product-build-linux-client.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: false
|
||||
VSCODE_RUN_INTEGRATION_TESTS: true
|
||||
VSCODE_RUN_SMOKE_TESTS: false
|
||||
- job: Linuxx64SmokeTest
|
||||
displayName: Smoke Tests
|
||||
container: vscode-bionic-x64
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
NPM_ARCH: x64
|
||||
DISPLAY: ":10"
|
||||
steps:
|
||||
- template: linux/product-build-linux-client.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: false
|
||||
VSCODE_RUN_INTEGRATION_TESTS: false
|
||||
VSCODE_RUN_SMOKE_TESTS: true
|
||||
|
||||
- stage: macOS
|
||||
dependsOn:
|
||||
@@ -96,7 +154,8 @@ stages:
|
||||
variables:
|
||||
BUILDSECMON_OPT_IN: true
|
||||
jobs:
|
||||
- job: macOSTest
|
||||
- job: macOSUnitTest
|
||||
displayName: Unit Tests
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
@@ -104,3 +163,30 @@ stages:
|
||||
- template: darwin/product-build-darwin-test.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: true
|
||||
VSCODE_RUN_INTEGRATION_TESTS: false
|
||||
VSCODE_RUN_SMOKE_TESTS: false
|
||||
- job: macOSIntegrationTest
|
||||
displayName: Integration Tests
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: darwin/product-build-darwin-test.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: false
|
||||
VSCODE_RUN_INTEGRATION_TESTS: true
|
||||
VSCODE_RUN_SMOKE_TESTS: false
|
||||
- job: macOSSmokeTest
|
||||
displayName: Smoke Tests
|
||||
timeoutInMinutes: 90
|
||||
variables:
|
||||
VSCODE_ARCH: x64
|
||||
steps:
|
||||
- template: darwin/product-build-darwin-test.yml
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_RUN_UNIT_TESTS: false
|
||||
VSCODE_RUN_INTEGRATION_TESTS: false
|
||||
VSCODE_RUN_SMOKE_TESTS: true
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
parameters:
|
||||
- name: VSCODE_QUALITY
|
||||
type: string
|
||||
- name: VSCODE_RUN_UNIT_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_RUN_INTEGRATION_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
- name: VSCODE_RUN_SMOKE_TESTS
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
@@ -94,14 +103,6 @@ steps:
|
||||
. build/azure-pipelines/win32/retry.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$env:npm_config_arch="$(VSCODE_ARCH)"
|
||||
# node-gyp@9.0.0 shipped with node@16.15.0 starts using config.gypi
|
||||
# from the custom headers path if dist-url option was set instead of
|
||||
# using the config value from the process. Electron builds with pointer compression
|
||||
# enabled for x64 and arm64, but incorrectly ships a single copy of config.gypi
|
||||
# with v8_enable_pointer_compression option always set for all target architectures.
|
||||
# We use the force_process_config option to use the config.gypi from the
|
||||
# nodejs process executing npm for 32-bit architectures.
|
||||
if ('$(VSCODE_ARCH)' -eq 'ia32') { $env:npm_config_force_process_config="true" }
|
||||
$env:CHILD_CONCURRENCY="1"
|
||||
retry { exec { yarn --frozen-lockfile --check-files } }
|
||||
env:
|
||||
@@ -135,13 +136,6 @@ steps:
|
||||
displayName: Download Electron
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { node build\lib\policies }
|
||||
displayName: Generate Group Policy definitions
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
@@ -184,105 +178,142 @@ steps:
|
||||
displayName: Download Playwright
|
||||
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 { yarn electron $(VSCODE_ARCH) }
|
||||
exec { .\scripts\test.bat --build --tfs "Unit Tests" }
|
||||
displayName: Run unit tests (Electron)
|
||||
timeoutInMinutes: 15
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- 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)
|
||||
timeoutInMinutes: 15
|
||||
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 { yarn test-node --build }
|
||||
displayName: Run unit tests (node.js)
|
||||
timeoutInMinutes: 15
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { yarn test-node --build }
|
||||
displayName: Run unit tests (node.js)
|
||||
timeoutInMinutes: 15
|
||||
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 { yarn test-browser-no-install --sequential --build --browser chromium --browser firefox --tfs "Browser Unit Tests" }
|
||||
displayName: Run unit tests (Browser, Chromium & Firefox)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { yarn test-browser-no-install --sequential --build --browser chromium --browser firefox --tfs "Browser Unit Tests" }
|
||||
displayName: Run unit tests (Browser, Chromium & Firefox)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), 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)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { yarn gulp `
|
||||
compile-extension:css-language-features-server `
|
||||
compile-extension:emmet `
|
||||
compile-extension:git `
|
||||
compile-extension:github-authentication `
|
||||
compile-extension:html-language-features-server `
|
||||
compile-extension:ipynb `
|
||||
compile-extension:json-language-features-server `
|
||||
compile-extension:markdown-language-features `
|
||||
compile-extension-media `
|
||||
compile-extension:microsoft-authentication `
|
||||
compile-extension:typescript-language-features `
|
||||
compile-extension:vscode-api-tests `
|
||||
compile-extension:vscode-colorize-tests `
|
||||
compile-extension:vscode-custom-editor-tests `
|
||||
compile-extension:vscode-notebook-tests `
|
||||
compile-extension:vscode-test-resolver `
|
||||
}
|
||||
displayName: Build integration tests
|
||||
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)"; .\scripts\test-web-integration.bat --browser firefox }
|
||||
displayName: Run integration tests (Browser, Firefox)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- 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)
|
||||
timeoutInMinutes: 20
|
||||
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)"; .\scripts\test-remote-integration.bat }
|
||||
displayName: Run integration tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox }
|
||||
displayName: Run integration tests (Browser, Firefox)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
exec {.\build\azure-pipelines\win32\listprocesses.bat }
|
||||
displayName: Diagnostics before smoke test run
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}:
|
||||
- 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)"; .\scripts\test-remote-integration.bat }
|
||||
displayName: Run integration tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"
|
||||
exec { yarn smoketest-no-compile --web --tracing --headless }
|
||||
displayName: Run smoke tests (Browser, Chromium)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
exec {.\build\azure-pipelines\win32\listprocesses.bat }
|
||||
displayName: Diagnostics before smoke test run
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)"
|
||||
exec { yarn smoketest-no-compile --tracing --build "$AppRoot" }
|
||||
displayName: Run smoke tests (Electron)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"
|
||||
exec { yarn smoketest-no-compile --web --tracing --headless }
|
||||
displayName: Run smoke tests (Browser, Chromium)
|
||||
timeoutInMinutes: 20
|
||||
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)"
|
||||
$env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"
|
||||
exec { yarn smoketest-no-compile --tracing --remote --build "$AppRoot" }
|
||||
displayName: Run smoke tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)"
|
||||
exec { yarn smoketest-no-compile --tracing --build "$AppRoot" }
|
||||
displayName: Run smoke tests (Electron)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
exec {.\build\azure-pipelines\win32\listprocesses.bat }
|
||||
displayName: Diagnostics after smoke test run
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)"
|
||||
$env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"
|
||||
exec { yarn gulp compile-extension:vscode-test-resolver }
|
||||
exec { yarn smoketest-no-compile --tracing --remote --build "$AppRoot" }
|
||||
displayName: Run smoke tests (Remote)
|
||||
timeoutInMinutes: 20
|
||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
exec {.\build\azure-pipelines\win32\listprocesses.bat }
|
||||
displayName: Diagnostics after smoke test run
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
|
||||
@@ -65,7 +65,6 @@ const vscodeResources = [
|
||||
'out-build/vs/base/browser/ui/codicons/codicon/**',
|
||||
'out-build/vs/base/parts/sandbox/electron-browser/preload.js',
|
||||
'out-build/vs/platform/environment/node/userDataPath.js',
|
||||
'out-build/vs/platform/extensions/node/extensionHostStarterWorkerMain.js',
|
||||
'out-build/vs/workbench/browser/media/*-theme.css',
|
||||
'out-build/vs/workbench/contrib/debug/**/*.json',
|
||||
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
|
||||
@@ -289,6 +288,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' }));
|
||||
} else if (platform === 'darwin') {
|
||||
const shortcut = gulp.src('resources/darwin/bin/code.sh')
|
||||
.pipe(replace('@@APPNAME@@', product.applicationName))
|
||||
.pipe(rename('bin/code'));
|
||||
|
||||
all = es.merge(all, shortcut);
|
||||
@@ -331,13 +331,10 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' })
|
||||
.pipe(rename(product.nameShort + '.VisualElementsManifest.xml')));
|
||||
|
||||
result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' })
|
||||
.pipe(rename(f => f.dirname = `policies/${f.dirname}`)));
|
||||
|
||||
} else if (platform === 'linux') {
|
||||
result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' })
|
||||
.pipe(replace('@@PRODNAME@@', product.nameLong))
|
||||
.pipe(replace('@@NAME@@', product.applicationName))
|
||||
.pipe(replace('@@APPNAME@@', product.applicationName))
|
||||
.pipe(rename('bin/' + product.applicationName)));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ function hygiene(some, linting = true) {
|
||||
const m = /([^\t\n\r\x20-\x7E⊃⊇✔︎✓🎯⚠️🛑🔴🚗🚙🚕🎉✨❗⇧⌥⌘×÷¦⋯…↑↓→→←↔⟷·•●◆▼⟪⟫┌└├⏎↩√φ]+)/g.exec(line);
|
||||
if (m) {
|
||||
console.error(
|
||||
file.relative + `(${i + 1},${m.index + 1}): Unexpected unicode character: "${m[0]}". To suppress, use // allow-any-unicode-next-line`
|
||||
file.relative + `(${i + 1},${m.index + 1}): Unexpected unicode character: "${m[0]}" (charCode: ${m[0].charCodeAt(0)}). To suppress, use // allow-any-unicode-next-line`
|
||||
);
|
||||
errorCount++;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ function writeControlFile(control) {
|
||||
fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2));
|
||||
}
|
||||
function getBuiltInExtensions() {
|
||||
log('Syncronizing built-in extensions...');
|
||||
log('Synchronizing built-in extensions...');
|
||||
log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`);
|
||||
const control = readControlFile();
|
||||
const streams = [];
|
||||
|
||||
@@ -136,7 +136,7 @@ function writeControlFile(control: IControlFile): void {
|
||||
}
|
||||
|
||||
export function getBuiltInExtensions(): Promise<void> {
|
||||
log('Syncronizing built-in extensions...');
|
||||
log('Synchronizing built-in extensions...');
|
||||
log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`);
|
||||
|
||||
const control = readControlFile();
|
||||
|
||||
@@ -119,7 +119,11 @@
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/localizations",
|
||||
"name": "vs/workbench/contrib/mergeEditor",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/localization",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
|
||||
+2
-2
@@ -60,12 +60,12 @@
|
||||
"git": {
|
||||
"name": "electron",
|
||||
"repositoryUrl": "https://github.com/electron/electron",
|
||||
"commitHash": "322f1c3f8907f2592eef5b5e03a97045e30df9e3"
|
||||
"commitHash": "085a15fd95969f3c61a52b39d64a7048d306dabe"
|
||||
}
|
||||
},
|
||||
"isOnlyProductionDependency": true,
|
||||
"license": "MIT",
|
||||
"version": "17.4.3"
|
||||
"version": "17.4.4"
|
||||
},
|
||||
{
|
||||
"component": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtensionContext, Uri } from 'vscode';
|
||||
import { LanguageClientOptions } from 'vscode-languageclient';
|
||||
import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient';
|
||||
import { startClient, LanguageClientConstructor } from '../cssClient';
|
||||
import { LanguageClient } from 'vscode-languageclient/browser';
|
||||
|
||||
@@ -15,8 +15,10 @@ declare const TextDecoder: {
|
||||
new(encoding?: string): { decode(buffer: ArrayBuffer): string };
|
||||
};
|
||||
|
||||
let client: BaseLanguageClient | undefined;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: ExtensionContext) {
|
||||
export async function activate(context: ExtensionContext) {
|
||||
const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js');
|
||||
try {
|
||||
const worker = new Worker(serverMain.toString());
|
||||
@@ -24,9 +26,17 @@ export function activate(context: ExtensionContext) {
|
||||
return new LanguageClient(id, name, clientOptions, worker);
|
||||
};
|
||||
|
||||
startClient(context, newLanguageClient, { TextDecoder });
|
||||
client = await startClient(context, newLanguageClient, { TextDecoder });
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deactivate(): Promise<void> {
|
||||
if (client) {
|
||||
await client.stop();
|
||||
client = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList, FormattingOptions, workspace } from 'vscode';
|
||||
import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, CommonLanguageClient, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient';
|
||||
import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, BaseLanguageClient, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { getCustomDataSource } from './customData';
|
||||
import { RequestService, serveFileSystemRequests } from './requests';
|
||||
@@ -15,7 +15,7 @@ namespace CustomDataChangedNotification {
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient;
|
||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;
|
||||
|
||||
export interface Runtime {
|
||||
TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string } };
|
||||
@@ -39,7 +39,7 @@ interface CSSFormatSettings {
|
||||
|
||||
const cssFormatSettingKeys: (keyof CSSFormatSettings)[] = ['newlineBetweenSelectors', 'newlineBetweenRules', 'spaceAroundSelectorSeparator', 'braceStyle', 'preserveNewLines', 'maxPreserveNewLines'];
|
||||
|
||||
export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) {
|
||||
export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<BaseLanguageClient> {
|
||||
|
||||
const customDataSource = getCustomDataSource(context.subscriptions);
|
||||
|
||||
@@ -100,31 +100,25 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
||||
// Create the language client and start the client.
|
||||
let client = newLanguageClient('css', localize('cssserver.name', 'CSS Language Server'), clientOptions);
|
||||
client.registerProposedFeatures();
|
||||
client.onReady().then(() => {
|
||||
|
||||
await client.start();
|
||||
|
||||
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
|
||||
customDataSource.onDidChange(() => {
|
||||
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
|
||||
customDataSource.onDidChange(() => {
|
||||
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
|
||||
});
|
||||
|
||||
// manually register / deregister format provider based on the `css/less/scss.format.enable` setting avoiding issues with late registration. See #71652.
|
||||
for (const registration of formatterRegistrations) {
|
||||
updateFormatterRegistration(registration);
|
||||
context.subscriptions.push({ dispose: () => registration.provider?.dispose() });
|
||||
context.subscriptions.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(registration.settingId) && updateFormatterRegistration(registration)));
|
||||
}
|
||||
|
||||
serveFileSystemRequests(client, runtime);
|
||||
});
|
||||
|
||||
let disposable = client.start();
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
context.subscriptions.push(disposable);
|
||||
// manually register / deregister format provider based on the `css/less/scss.format.enable` setting avoiding issues with late registration. See #71652.
|
||||
for (const registration of formatterRegistrations) {
|
||||
updateFormatterRegistration(registration);
|
||||
context.subscriptions.push({ dispose: () => registration.provider?.dispose() });
|
||||
context.subscriptions.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(registration.settingId) && updateFormatterRegistration(registration)));
|
||||
}
|
||||
|
||||
client.onReady().then(() => {
|
||||
context.subscriptions.push(initCompletionProvider());
|
||||
});
|
||||
serveFileSystemRequests(client, runtime);
|
||||
|
||||
|
||||
context.subscriptions.push(initCompletionProvider());
|
||||
|
||||
function initCompletionProvider(): Disposable {
|
||||
const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/;
|
||||
@@ -204,11 +198,10 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(params.options));
|
||||
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
||||
client.protocol2CodeConverter.asTextEdits,
|
||||
(error) => {
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []);
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
);
|
||||
@@ -216,4 +209,6 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
import { getNodeFSRequestService } from './nodeFs';
|
||||
import { ExtensionContext, extensions } from 'vscode';
|
||||
import { startClient, LanguageClientConstructor } from '../cssClient';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node';
|
||||
import { TextDecoder } from 'util';
|
||||
|
||||
|
||||
let client: BaseLanguageClient | undefined;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: ExtensionContext) {
|
||||
export async 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`;
|
||||
@@ -30,5 +33,12 @@ export function activate(context: ExtensionContext) {
|
||||
return new LanguageClient(id, name, serverOptions, clientOptions);
|
||||
};
|
||||
|
||||
startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder });
|
||||
client = await startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder });
|
||||
}
|
||||
|
||||
export async function deactivate(): Promise<void> {
|
||||
if (client) {
|
||||
await client.stop();
|
||||
client = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Uri, workspace } from 'vscode';
|
||||
import { RequestType, CommonLanguageClient } from 'vscode-languageclient';
|
||||
import { RequestType, BaseLanguageClient } from 'vscode-languageclient';
|
||||
import { Runtime } from './cssClient';
|
||||
|
||||
export namespace FsContentRequest {
|
||||
@@ -18,7 +18,7 @@ export namespace FsReadDirRequest {
|
||||
export const type: RequestType<string, [string, FileType][], any> = new RequestType('fs/readDir');
|
||||
}
|
||||
|
||||
export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) {
|
||||
export function serveFileSystemRequests(client: BaseLanguageClient, runtime: Runtime) {
|
||||
client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string }) => {
|
||||
const uri = Uri.parse(param.uri);
|
||||
if (uri.scheme === 'file' && runtime.fs) {
|
||||
|
||||
@@ -336,7 +336,10 @@
|
||||
"type": "string",
|
||||
"scope": "resource",
|
||||
"default": "collapse",
|
||||
"enum": ["collapse", "expand"],
|
||||
"enum": [
|
||||
"collapse",
|
||||
"expand"
|
||||
],
|
||||
"markdownDescription": "%css.format.braceStyle.desc%"
|
||||
},
|
||||
"css.format.preserveNewLines": {
|
||||
@@ -638,7 +641,10 @@
|
||||
"type": "string",
|
||||
"scope": "resource",
|
||||
"default": "collapse",
|
||||
"enum": ["collapse", "expand"],
|
||||
"enum": [
|
||||
"collapse",
|
||||
"expand"
|
||||
],
|
||||
"markdownDescription": "%scss.format.braceStyle.desc%"
|
||||
},
|
||||
"scss.format.preserveNewLines": {
|
||||
@@ -941,7 +947,10 @@
|
||||
"type": "string",
|
||||
"scope": "resource",
|
||||
"default": "collapse",
|
||||
"enum": ["collapse", "expand"],
|
||||
"enum": [
|
||||
"collapse",
|
||||
"expand"
|
||||
],
|
||||
"markdownDescription": "%less.format.braceStyle.desc%"
|
||||
},
|
||||
"less.format.preserveNewLines": {
|
||||
@@ -985,7 +994,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^7.0.0",
|
||||
"vscode-languageclient": "^8.0.1",
|
||||
"vscode-nls": "^5.0.0",
|
||||
"vscode-uri": "^3.0.3"
|
||||
},
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"main": "./out/node/cssServerMain",
|
||||
"browser": "./dist/browser/cssServerMain",
|
||||
"dependencies": {
|
||||
"vscode-css-languageservice": "^5.4.2",
|
||||
"vscode-languageserver": "^7.0.0",
|
||||
"vscode-css-languageservice": "^6.0.1",
|
||||
"vscode-languageserver": "^8.0.1",
|
||||
"vscode-uri": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType, Disposable, TextDocumentIdentifier, Range, FormattingOptions, TextEdit
|
||||
Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType, Disposable, TextDocumentIdentifier, Range, FormattingOptions, TextEdit, Diagnostic
|
||||
} from 'vscode-languageserver';
|
||||
import { URI } from 'vscode-uri';
|
||||
import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position, CSSFormatConfiguration } from 'vscode-css-languageservice';
|
||||
import { getLanguageModelCache } from './languageModelCache';
|
||||
import { formatError, runSafeAsync } from './utils/runner';
|
||||
import { runSafeAsync } from './utils/runner';
|
||||
import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation';
|
||||
import { getDocumentContext } from './utils/documentContext';
|
||||
import { fetchDataProviders } from './customData';
|
||||
import { RequestService, getRequestService } from './requests';
|
||||
@@ -56,6 +57,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
|
||||
let dataProvidersReady: Promise<any> = Promise.resolve();
|
||||
|
||||
let diagnosticsSupport: DiagnosticsSupport | undefined;
|
||||
|
||||
const languageServices: { [id: string]: LanguageService } = {};
|
||||
|
||||
const notReady = () => Promise.reject('Not Ready');
|
||||
@@ -64,6 +67,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
// After the server has started the client sends an initialize request. The server receives
|
||||
// in the passed params the rootPath of the workspace plus the client capabilities.
|
||||
connection.onInitialize((params: InitializeParams): InitializeResult => {
|
||||
|
||||
const initializationOptions = params.initializationOptions as any || {};
|
||||
|
||||
workspaceFolders = (<any>params).workspaceFolders;
|
||||
if (!Array.isArray(workspaceFolders)) {
|
||||
workspaceFolders = [];
|
||||
@@ -72,7 +78,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
}
|
||||
}
|
||||
|
||||
requestService = getRequestService(params.initializationOptions?.handledSchemas || ['file'], connection, runtime);
|
||||
requestService = getRequestService(initializationOptions?.handledSchemas || ['file'], connection, runtime);
|
||||
|
||||
function getClientCapability<T>(name: string, def: T) {
|
||||
const keys = name.split('.');
|
||||
@@ -88,12 +94,20 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
|
||||
scopedSettingsSupport = !!getClientCapability('workspace.configuration', false);
|
||||
foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
|
||||
formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
|
||||
|
||||
formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
|
||||
|
||||
languageServices.css = getCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities });
|
||||
languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities });
|
||||
languageServices.less = getLESSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities });
|
||||
|
||||
const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined);
|
||||
if (supportsDiagnosticPull === undefined) {
|
||||
diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument);
|
||||
} else {
|
||||
diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument);
|
||||
}
|
||||
|
||||
const capabilities: ServerCapabilities = {
|
||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||
completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-', ':'] } : undefined,
|
||||
@@ -110,8 +124,13 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
colorProvider: {},
|
||||
foldingRangeProvider: true,
|
||||
selectionRangeProvider: true,
|
||||
documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true,
|
||||
documentFormattingProvider: params.initializationOptions?.provideFormatter === true,
|
||||
diagnosticProvider: {
|
||||
documentSelector: null,
|
||||
interFileDependencies: false,
|
||||
workspaceDiagnostics: false
|
||||
},
|
||||
documentRangeFormattingProvider: initializationOptions?.provideFormatter === true,
|
||||
documentFormattingProvider: initializationOptions?.provideFormatter === true,
|
||||
};
|
||||
return { capabilities };
|
||||
});
|
||||
@@ -135,7 +154,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
let promise = documentSettings[textDocument.uri];
|
||||
if (!promise) {
|
||||
const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] };
|
||||
promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]);
|
||||
promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0] as LanguageSettings | undefined);
|
||||
documentSettings[textDocument.uri] = promise;
|
||||
}
|
||||
return promise;
|
||||
@@ -145,62 +164,25 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
|
||||
// The settings have changed. Is send on server activation as well.
|
||||
connection.onDidChangeConfiguration(change => {
|
||||
updateConfiguration(<Settings>change.settings);
|
||||
updateConfiguration(change.settings as any);
|
||||
});
|
||||
|
||||
function updateConfiguration(settings: Settings) {
|
||||
function updateConfiguration(settings: any) {
|
||||
for (const languageId in languageServices) {
|
||||
languageServices[languageId].configure((settings as any)[languageId]);
|
||||
languageServices[languageId].configure(settings[languageId]);
|
||||
}
|
||||
// reset all document settings
|
||||
documentSettings = {};
|
||||
// Revalidate any open text documents
|
||||
documents.all().forEach(triggerValidation);
|
||||
diagnosticsSupport?.requestRefresh();
|
||||
}
|
||||
|
||||
const pendingValidationRequests: { [uri: string]: Disposable } = {};
|
||||
const validationDelayMs = 500;
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent(change => {
|
||||
triggerValidation(change.document);
|
||||
});
|
||||
|
||||
// a document has closed: clear all diagnostics
|
||||
documents.onDidClose(event => {
|
||||
cleanPendingValidation(event.document);
|
||||
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
||||
});
|
||||
|
||||
function cleanPendingValidation(textDocument: TextDocument): void {
|
||||
const request = pendingValidationRequests[textDocument.uri];
|
||||
if (request) {
|
||||
request.dispose();
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerValidation(textDocument: TextDocument): void {
|
||||
cleanPendingValidation(textDocument);
|
||||
pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => {
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
validateTextDocument(textDocument);
|
||||
}, validationDelayMs);
|
||||
}
|
||||
|
||||
function validateTextDocument(textDocument: TextDocument): void {
|
||||
async function validateTextDocument(textDocument: TextDocument): Promise<Diagnostic[]> {
|
||||
const settingsPromise = getDocumentSettings(textDocument);
|
||||
Promise.all([settingsPromise, dataProvidersReady]).then(async ([settings]) => {
|
||||
const stylesheet = stylesheets.get(textDocument);
|
||||
const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings);
|
||||
// Send the computed diagnostics to VSCode.
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||
}, e => {
|
||||
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
|
||||
});
|
||||
}
|
||||
const [settings] = await Promise.all([settingsPromise, dataProvidersReady]);
|
||||
|
||||
const stylesheet = stylesheets.get(textDocument);
|
||||
return getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings);
|
||||
}
|
||||
|
||||
function updateDataProviders(dataPaths: string[]) {
|
||||
dataProvidersReady = fetchDataProviders(dataPaths, requestService).then(customDataProviders => {
|
||||
|
||||
@@ -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 { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver';
|
||||
import { TextDocument } from 'vscode-css-languageservice';
|
||||
import { formatError, runSafeAsync } from './runner';
|
||||
import { RuntimeEnvironment } from '../cssServer';
|
||||
|
||||
export type Validator = (textDocument: TextDocument) => Promise<Diagnostic[]>;
|
||||
export type DiagnosticsSupport = {
|
||||
dispose(): void;
|
||||
requestRefresh(): void;
|
||||
};
|
||||
|
||||
export function registerDiagnosticsPushSupport(documents: TextDocuments<TextDocument>, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport {
|
||||
|
||||
const pendingValidationRequests: { [uri: string]: Disposable } = {};
|
||||
const validationDelayMs = 500;
|
||||
|
||||
const disposables: Disposable[] = [];
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent(change => {
|
||||
triggerValidation(change.document);
|
||||
}, undefined, disposables);
|
||||
|
||||
// a document has closed: clear all diagnostics
|
||||
documents.onDidClose(event => {
|
||||
cleanPendingValidation(event.document);
|
||||
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
||||
}, undefined, disposables);
|
||||
|
||||
function cleanPendingValidation(textDocument: TextDocument): void {
|
||||
const request = pendingValidationRequests[textDocument.uri];
|
||||
if (request) {
|
||||
request.dispose();
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerValidation(textDocument: TextDocument): void {
|
||||
cleanPendingValidation(textDocument);
|
||||
const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => {
|
||||
if (request === pendingValidationRequests[textDocument.uri]) {
|
||||
try {
|
||||
const diagnostics = await validate(textDocument);
|
||||
if (request === pendingValidationRequests[textDocument.uri]) {
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||
}
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
} catch (e) {
|
||||
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
|
||||
}
|
||||
}
|
||||
}, validationDelayMs);
|
||||
}
|
||||
|
||||
return {
|
||||
requestRefresh: () => {
|
||||
documents.all().forEach(triggerValidation);
|
||||
},
|
||||
dispose: () => {
|
||||
disposables.forEach(d => d.dispose());
|
||||
disposables.length = 0;
|
||||
const keys = Object.keys(pendingValidationRequests);
|
||||
for (const key of keys) {
|
||||
pendingValidationRequests[key].dispose();
|
||||
delete pendingValidationRequests[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function registerDiagnosticsPullSupport(documents: TextDocuments<TextDocument>, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport {
|
||||
|
||||
function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport {
|
||||
return {
|
||||
kind: DocumentDiagnosticReportKind.Full,
|
||||
items: diagnostics
|
||||
};
|
||||
}
|
||||
|
||||
const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => {
|
||||
return runSafeAsync(runtime, async () => {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return newDocumentDiagnosticReport(await validate(document));
|
||||
}
|
||||
return newDocumentDiagnosticReport([]);
|
||||
|
||||
}, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token);
|
||||
});
|
||||
|
||||
function requestRefresh(): void {
|
||||
connection.languages.diagnostics.refresh();
|
||||
}
|
||||
|
||||
return {
|
||||
requestRefresh,
|
||||
dispose: () => {
|
||||
registration.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -12,50 +12,50 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
vscode-css-languageservice@^5.4.2:
|
||||
version "5.4.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.4.2.tgz#69ea74c000bd653dfc8e458a1720d28b9ffa5cfb"
|
||||
integrity sha512-DT7+7vfdT2HDNjDoXWtYJ0lVDdeDEdbMNdK4PKqUl2MS8g7PWt7J5G9B6k9lYox8nOfhCEjLnoNC3UKHHCR1lg==
|
||||
vscode-css-languageservice@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.0.1.tgz#ccf94944e094dcc5833d1b4ac276994b698e9283"
|
||||
integrity sha512-81n/eeYuJwQdvpoy6IK1258PtPbO720fl13FcJ5YQECPyHMFkmld1qKHwPJkyLbLPfboqJPM53ys4xW8v+iBVw==
|
||||
dependencies:
|
||||
vscode-languageserver-textdocument "^1.0.4"
|
||||
vscode-languageserver-types "^3.16.0"
|
||||
vscode-nls "^5.0.0"
|
||||
vscode-languageserver-types "^3.17.1"
|
||||
vscode-nls "^5.0.1"
|
||||
vscode-uri "^3.0.3"
|
||||
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
|
||||
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
|
||||
vscode-jsonrpc@8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e"
|
||||
integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==
|
||||
|
||||
vscode-languageserver-protocol@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
|
||||
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
|
||||
vscode-languageserver-protocol@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed"
|
||||
integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==
|
||||
dependencies:
|
||||
vscode-jsonrpc "6.0.0"
|
||||
vscode-languageserver-types "3.16.0"
|
||||
vscode-jsonrpc "8.0.1"
|
||||
vscode-languageserver-types "3.17.1"
|
||||
|
||||
vscode-languageserver-textdocument@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157"
|
||||
integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ==
|
||||
|
||||
vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
vscode-languageserver-types@3.17.1, vscode-languageserver-types@^3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
|
||||
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
|
||||
|
||||
vscode-languageserver@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0"
|
||||
integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==
|
||||
vscode-languageserver@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.1.tgz#56bd7a01f5c88af075a77f1d220edcb30fc4bdc7"
|
||||
integrity sha512-sn7SjBwWm3OlmLtgg7jbM0wBULppyL60rj8K5HF0ny/MzN+GzPBX1kCvYdybhl7UW63V5V5tRVnyB8iwC73lSQ==
|
||||
dependencies:
|
||||
vscode-languageserver-protocol "3.16.0"
|
||||
vscode-languageserver-protocol "3.17.1"
|
||||
|
||||
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==
|
||||
vscode-nls@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2"
|
||||
integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A==
|
||||
|
||||
vscode-uri@^3.0.3:
|
||||
version "3.0.3"
|
||||
|
||||
@@ -39,39 +39,39 @@ minimatch@^3.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
semver@^7.3.4:
|
||||
version "7.3.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
|
||||
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
|
||||
semver@^7.3.5:
|
||||
version "7.3.7"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
|
||||
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
|
||||
vscode-jsonrpc@8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e"
|
||||
integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==
|
||||
|
||||
vscode-languageclient@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2"
|
||||
integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==
|
||||
vscode-languageclient@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301"
|
||||
integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw==
|
||||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
semver "^7.3.4"
|
||||
vscode-languageserver-protocol "3.16.0"
|
||||
semver "^7.3.5"
|
||||
vscode-languageserver-protocol "3.17.1"
|
||||
|
||||
vscode-languageserver-protocol@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
|
||||
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
|
||||
vscode-languageserver-protocol@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed"
|
||||
integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==
|
||||
dependencies:
|
||||
vscode-jsonrpc "6.0.0"
|
||||
vscode-languageserver-types "3.16.0"
|
||||
vscode-jsonrpc "8.0.1"
|
||||
vscode-languageserver-types "3.17.1"
|
||||
|
||||
vscode-languageserver-types@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
vscode-languageserver-types@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
|
||||
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
|
||||
|
||||
vscode-nls@^5.0.0:
|
||||
version "5.0.0"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
|
||||
"enabledApiProposals": [
|
||||
"diffCommand",
|
||||
"contribMergeEditorToolbar",
|
||||
"contribViewsWelcome",
|
||||
"scmActionButton",
|
||||
"scmSelectedProvider",
|
||||
@@ -22,7 +23,8 @@
|
||||
],
|
||||
"activationEvents": [
|
||||
"*",
|
||||
"onFileSystem:git"
|
||||
"onFileSystem:git",
|
||||
"onFileSystem:git-show"
|
||||
],
|
||||
"extensionDependencies": [
|
||||
"vscode.git-base"
|
||||
@@ -564,6 +566,11 @@
|
||||
"command": "git.api.getRemoteSources",
|
||||
"title": "%command.api.getRemoteSources%",
|
||||
"category": "Git API"
|
||||
},
|
||||
{
|
||||
"command": "git.acceptMerge",
|
||||
"title": "%command.git.acceptMerge%",
|
||||
"category": "Git"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
@@ -1490,6 +1497,12 @@
|
||||
"when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
|
||||
}
|
||||
],
|
||||
"merge/toolbar": [
|
||||
{
|
||||
"command": "git.acceptMerge",
|
||||
"when": "isMergeEditor"
|
||||
}
|
||||
],
|
||||
"scm/change/title": [
|
||||
{
|
||||
"command": "git.stageChange",
|
||||
@@ -1852,7 +1865,8 @@
|
||||
"git.branchPrefix": {
|
||||
"type": "string",
|
||||
"description": "%config.branchPrefix%",
|
||||
"default": ""
|
||||
"default": "",
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.branchProtection": {
|
||||
"type": "array",
|
||||
@@ -1860,7 +1874,8 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
"default": [],
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.branchProtectionPrompt": {
|
||||
"type": "string",
|
||||
@@ -1875,7 +1890,8 @@
|
||||
"%config.branchProtectionPrompt.alwaysCommitToNewBranch%",
|
||||
"%config.branchProtectionPrompt.alwaysPrompt%"
|
||||
],
|
||||
"default": "alwaysPrompt"
|
||||
"default": "alwaysPrompt",
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.branchValidationRegex": {
|
||||
"type": "string",
|
||||
@@ -1890,7 +1906,8 @@
|
||||
"git.branchRandomName.enable": {
|
||||
"type": "boolean",
|
||||
"description": "%config.branchRandomNameEnable%",
|
||||
"default": false
|
||||
"default": false,
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.branchRandomName.dictionary": {
|
||||
"type": "array",
|
||||
@@ -1901,7 +1918,8 @@
|
||||
"default": [
|
||||
"adjectives",
|
||||
"animals"
|
||||
]
|
||||
],
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.confirmSync": {
|
||||
"type": "boolean",
|
||||
@@ -2427,6 +2445,12 @@
|
||||
],
|
||||
"markdownDescription": "%config.logLevel%",
|
||||
"scope": "window"
|
||||
},
|
||||
"git.experimental.mergeEditor": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%config.experimental.mergeEditor%",
|
||||
"scope": "window"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
"command.api.getRepositories": "Get Repositories",
|
||||
"command.api.getRepositoryState": "Get Repository State",
|
||||
"command.api.getRemoteSources": "Get Remote Sources",
|
||||
"command.git.acceptMerge": "Accept Merge",
|
||||
"config.enabled": "Whether git is enabled.",
|
||||
"config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows). This can also be an array of string values containing multiple paths to look up.",
|
||||
"config.autoRepositoryDetection": "Configures when repositories should be automatically detected.",
|
||||
@@ -235,6 +236,7 @@
|
||||
"config.logLevel.error": "Log only error, and critical information",
|
||||
"config.logLevel.critical": "Log only critical information",
|
||||
"config.logLevel.off": "Log nothing",
|
||||
"config.experimental.mergeEditor": "Open the _experimental_ merge editor for files that are currently under conflict.",
|
||||
"submenu.explorer": "Git",
|
||||
"submenu.commit": "Commit",
|
||||
"submenu.commit.amend": "Amend",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as picomatch from 'picomatch';
|
||||
import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity } from 'vscode';
|
||||
import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText } from 'vscode';
|
||||
import TelemetryReporter from '@vscode/extension-telemetry';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
|
||||
@@ -15,7 +15,7 @@ import { Git, Stash } from './git';
|
||||
import { Model } from './model';
|
||||
import { Repository, Resource, ResourceGroupType } from './repository';
|
||||
import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging';
|
||||
import { fromGitUri, toGitUri, isGitUri } from './uri';
|
||||
import { fromGitUri, toGitUri, isGitUri, toMergeUris } from './uri';
|
||||
import { grep, isDescendant, pathEquals, relativePath } from './util';
|
||||
import { LogLevel, OutputChannelLogger } from './log';
|
||||
import { GitTimelineItem } from './timelineProvider';
|
||||
@@ -405,6 +405,51 @@ export class CommandCenter {
|
||||
}
|
||||
}
|
||||
|
||||
@command('_git.openMergeEditor')
|
||||
async openMergeEditor(uri: unknown) {
|
||||
if (!(uri instanceof Uri)) {
|
||||
return;
|
||||
}
|
||||
const repo = this.model.getRepository(uri);
|
||||
if (!repo) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
type InputData = { uri: Uri; detail?: string; description?: string };
|
||||
const mergeUris = toMergeUris(uri);
|
||||
let input1: InputData = { uri: mergeUris.ours };
|
||||
let input2: InputData = { uri: mergeUris.theirs };
|
||||
|
||||
try {
|
||||
const [head, mergeHead] = await Promise.all([repo.getCommit('HEAD'), repo.getCommit('MERGE_HEAD')]);
|
||||
// ours (current branch and commit)
|
||||
input1.detail = head.refNames.map(s => s.replace(/^HEAD ->/, '')).join(', ');
|
||||
input1.description = head.hash.substring(0, 7);
|
||||
|
||||
// theirs
|
||||
input2.detail = mergeHead.refNames.join(', ');
|
||||
input2.description = mergeHead.hash.substring(0, 7);
|
||||
|
||||
} catch (error) {
|
||||
// not so bad, can continue with just uris
|
||||
console.error('FAILED to read HEAD, MERGE_HEAD commits');
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
const options = {
|
||||
ancestor: mergeUris.base,
|
||||
input1,
|
||||
input2,
|
||||
output: uri
|
||||
};
|
||||
|
||||
await commands.executeCommand(
|
||||
'_open.mergeEditor',
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise<void> {
|
||||
if (!url || typeof url !== 'string') {
|
||||
url = await pickRemoteSource({
|
||||
@@ -416,6 +461,7 @@ export class CommandCenter {
|
||||
if (!url) {
|
||||
/* __GDPR__
|
||||
"clone" : {
|
||||
"owner": "lszomoru",
|
||||
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -441,6 +487,7 @@ export class CommandCenter {
|
||||
if (!uris || uris.length === 0) {
|
||||
/* __GDPR__
|
||||
"clone" : {
|
||||
"owner": "lszomoru",
|
||||
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -499,6 +546,7 @@ export class CommandCenter {
|
||||
|
||||
/* __GDPR__
|
||||
"clone" : {
|
||||
"owner": "lszomoru",
|
||||
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
|
||||
}
|
||||
@@ -518,6 +566,7 @@ export class CommandCenter {
|
||||
if (/already exists and is not an empty directory/.test(err && err.stderr || '')) {
|
||||
/* __GDPR__
|
||||
"clone" : {
|
||||
"owner": "lszomoru",
|
||||
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -527,6 +576,7 @@ export class CommandCenter {
|
||||
} else {
|
||||
/* __GDPR__
|
||||
"clone" : {
|
||||
"owner": "lszomoru",
|
||||
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -1035,6 +1085,34 @@ export class CommandCenter {
|
||||
await this._stageChanges(textEditor, selectedChanges);
|
||||
}
|
||||
|
||||
@command('git.acceptMerge')
|
||||
async acceptMerge(uri: Uri | unknown): Promise<void> {
|
||||
if (!(uri instanceof Uri)) {
|
||||
return;
|
||||
}
|
||||
const repository = this.model.getRepository(uri);
|
||||
if (!repository) {
|
||||
console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't belong to any repository`);
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString());
|
||||
if (!doc) {
|
||||
console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't match a document`);
|
||||
return;
|
||||
}
|
||||
|
||||
await doc.save();
|
||||
await repository.add([uri]);
|
||||
|
||||
// TODO@jrieken there isn't a `TabInputTextMerge` instance yet, till now the merge editor
|
||||
// uses the `TabInputText` for the out-resource and we use that to identify and CLOSE the tab
|
||||
const { activeTab } = window.tabGroups.activeTabGroup;
|
||||
if (activeTab && activeTab?.input instanceof TabInputText && activeTab.input.uri.toString() === uri.toString()) {
|
||||
await window.tabGroups.close(activeTab, true);
|
||||
}
|
||||
}
|
||||
|
||||
private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise<void> {
|
||||
const modifiedDocument = textEditor.document;
|
||||
const modifiedUri = modifiedDocument.uri;
|
||||
@@ -2920,6 +2998,7 @@ export class CommandCenter {
|
||||
|
||||
/* __GDPR__
|
||||
"git.command" : {
|
||||
"owner": "lszomoru",
|
||||
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -354,7 +354,7 @@ function sanitizePath(path: string): string {
|
||||
return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`);
|
||||
}
|
||||
|
||||
const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%B';
|
||||
const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B';
|
||||
|
||||
export interface ICloneOptions {
|
||||
readonly parentPath: string;
|
||||
@@ -660,6 +660,7 @@ export interface Commit {
|
||||
authorName?: string;
|
||||
authorEmail?: string;
|
||||
commitDate?: Date;
|
||||
refNames: string[];
|
||||
}
|
||||
|
||||
export class GitStatusParser {
|
||||
@@ -790,7 +791,7 @@ export function parseGitmodules(raw: string): Submodule[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm;
|
||||
const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm;
|
||||
|
||||
export function parseGitCommits(data: string): Commit[] {
|
||||
let commits: Commit[] = [];
|
||||
@@ -801,6 +802,7 @@ export function parseGitCommits(data: string): Commit[] {
|
||||
let authorDate;
|
||||
let commitDate;
|
||||
let parents;
|
||||
let refNames;
|
||||
let message;
|
||||
let match;
|
||||
|
||||
@@ -810,7 +812,7 @@ export function parseGitCommits(data: string): Commit[] {
|
||||
break;
|
||||
}
|
||||
|
||||
[, ref, authorName, authorEmail, authorDate, commitDate, parents, message] = match;
|
||||
[, ref, authorName, authorEmail, authorDate, commitDate, parents, refNames, message] = match;
|
||||
|
||||
if (message[message.length - 1] === '\n') {
|
||||
message = message.substr(0, message.length - 1);
|
||||
@@ -825,6 +827,7 @@ export function parseGitCommits(data: string): Commit[] {
|
||||
authorName: ` ${authorName}`.substr(1),
|
||||
authorEmail: ` ${authorEmail}`.substr(1),
|
||||
commitDate: new Date(Number(commitDate) * 1000),
|
||||
refNames: refNames.split(',').map(s => s.trim())
|
||||
});
|
||||
} while (true);
|
||||
|
||||
|
||||
@@ -192,7 +192,9 @@ export async function _activate(context: ExtensionContext): Promise<GitExtension
|
||||
outputChannelLogger.logWarning(err.message);
|
||||
|
||||
/* __GDPR__
|
||||
"git.missing" : {}
|
||||
"git.missing" : {
|
||||
"owner": "lszomoru"
|
||||
}
|
||||
*/
|
||||
telemetryReporter.sendTelemetryEvent('git.missing');
|
||||
|
||||
|
||||
@@ -296,6 +296,10 @@ export class Resource implements SourceControlResourceState {
|
||||
const command = this._commandResolver.resolveChangeCommand(this);
|
||||
await commands.executeCommand<void>(command.command, ...(command.arguments || []));
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new Resource(this._commandResolver, this._resourceGroupType, this._resourceUri, this._type, this._useIcons, this._renameResourceUri);
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Operation {
|
||||
@@ -546,7 +550,7 @@ class DotGitWatcher implements IFileWatcher {
|
||||
|
||||
// Ignore changes to the "index.lock" file, and watchman fsmonitor hook (https://git-scm.com/docs/githooks#_fsmonitor_watchman) cookie files.
|
||||
// Watchman creates a cookie file inside the git directory whenever a query is run (https://facebook.github.io/watchman/docs/cookies.html).
|
||||
const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path));
|
||||
const filteredRootWatcher = filterEvent(rootWatcher.event, uri => uri.scheme === 'file' && !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path));
|
||||
this.event = anyEvent(filteredRootWatcher, this.emitter.event);
|
||||
|
||||
repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables);
|
||||
@@ -603,11 +607,20 @@ class ResourceCommandResolver {
|
||||
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]
|
||||
};
|
||||
const bothModified = resource.type === Status.BOTH_MODIFIED;
|
||||
if (resource.rightUri && bothModified && workspace.getConfiguration('git').get<boolean>('experimental.mergeEditor', false)) {
|
||||
return {
|
||||
command: '_git.openMergeEditor',
|
||||
title: localize('open.merge', "Open Merge"),
|
||||
arguments: [resource.rightUri]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
command: 'vscode.open',
|
||||
title: localize('open', "Open"),
|
||||
arguments: [resource.rightUri, { override: bothModified ? false : undefined }, title]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
command: 'vscode.diff',
|
||||
@@ -912,6 +925,12 @@ export class Repository implements Disposable {
|
||||
onConfigListener(updateIndexGroupVisibility, this, this.disposables);
|
||||
updateIndexGroupVisibility();
|
||||
|
||||
workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('git.experimental.mergeEditor')) {
|
||||
this.mergeGroup.resourceStates = this.mergeGroup.resourceStates.map(r => r.clone());
|
||||
}
|
||||
}, undefined, this.disposables);
|
||||
|
||||
filterEvent(workspace.onDidChangeConfiguration, e =>
|
||||
e.affectsConfiguration('git.branchSortOrder', root)
|
||||
|| e.affectsConfiguration('git.untrackedChanges', root)
|
||||
@@ -1382,15 +1401,15 @@ export class Repository implements Disposable {
|
||||
}
|
||||
|
||||
@throttle
|
||||
async fetchAll(): Promise<void> {
|
||||
await this._fetch({ all: true });
|
||||
async fetchAll(cancellationToken?: CancellationToken): Promise<void> {
|
||||
await this._fetch({ all: true, cancellationToken });
|
||||
}
|
||||
|
||||
async fetch(options: FetchOptions): Promise<void> {
|
||||
await this._fetch(options);
|
||||
}
|
||||
|
||||
private async _fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean } = {}): Promise<void> {
|
||||
private async _fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean; cancellationToken?: CancellationToken } = {}): Promise<void> {
|
||||
if (!options.prune) {
|
||||
const config = workspace.getConfiguration('git', Uri.file(this.root));
|
||||
const prune = config.get<boolean>('pruneOnFetch');
|
||||
@@ -1435,7 +1454,7 @@ export class Repository implements Disposable {
|
||||
|
||||
// When fetchOnPull is enabled, fetch all branches when pulling
|
||||
if (fetchOnPull) {
|
||||
await this.repository.fetch({ all: true });
|
||||
await this.fetchAll();
|
||||
}
|
||||
|
||||
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
|
||||
@@ -1506,7 +1525,7 @@ export class Repository implements Disposable {
|
||||
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.fetchAll(cancellationToken);
|
||||
}
|
||||
|
||||
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
|
||||
@@ -1864,6 +1883,7 @@ export class Repository implements Disposable {
|
||||
if (didHitLimit) {
|
||||
/* __GDPR__
|
||||
"statusLimit" : {
|
||||
"owner": "lszomoru",
|
||||
"ignoreSubmodules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"limit": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"statusLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
|
||||
@@ -205,6 +205,7 @@ john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
8e5a374372b8393906c7e380dbb09349c5385554
|
||||
main,branch
|
||||
This is a commit message.\x00`;
|
||||
|
||||
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{
|
||||
@@ -215,6 +216,7 @@ This is a commit message.\x00`;
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
refNames: ['main', 'branch'],
|
||||
}]);
|
||||
});
|
||||
|
||||
@@ -225,6 +227,7 @@ john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
|
||||
main
|
||||
This is a commit message.\x00`;
|
||||
|
||||
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_MULTIPLE_PARENTS), [{
|
||||
@@ -235,6 +238,7 @@ This is a commit message.\x00`;
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
refNames: ['main'],
|
||||
}]);
|
||||
});
|
||||
|
||||
@@ -245,6 +249,7 @@ john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
|
||||
main
|
||||
This is a commit message.\x00`;
|
||||
|
||||
assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_NO_PARENTS), [{
|
||||
@@ -255,6 +260,7 @@ This is a commit message.\x00`;
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
refNames: ['main'],
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,3 +51,14 @@ export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Ur
|
||||
query: JSON.stringify(params)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming `uri` is being merged it creates uris for `base`, `ours`, and `theirs`
|
||||
*/
|
||||
export function toMergeUris(uri: Uri): { base: Uri; ours: Uri; theirs: Uri } {
|
||||
return {
|
||||
base: toGitUri(uri, ':1'),
|
||||
ours: toGitUri(uri, ':2'),
|
||||
theirs: toGitUri(uri, ':3'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -496,6 +496,7 @@ export class GitHubServer implements IGitHubServer {
|
||||
|
||||
/* __GDPR__
|
||||
"session" : {
|
||||
"owner": "TylerLeonhardt",
|
||||
"isEdu": { "classification": "NonIdentifiableDemographicInfo", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -530,6 +531,7 @@ export class GitHubServer implements IGitHubServer {
|
||||
|
||||
/* __GDPR__
|
||||
"ghe-session" : {
|
||||
"owner": "TylerLeonhardt",
|
||||
"version": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -601,6 +603,7 @@ export class GitHubEnterpriseServer implements IGitHubServer {
|
||||
|
||||
/* __GDPR__
|
||||
"ghe-session" : {
|
||||
"owner": "TylerLeonhardt",
|
||||
"version": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -102,13 +102,14 @@ async function handlePushError(repository: Repository, remote: Remote, refspec:
|
||||
let title = `Update ${remoteName}`;
|
||||
const head = repository.state.HEAD?.name;
|
||||
|
||||
let body: string | undefined;
|
||||
|
||||
if (head) {
|
||||
const commit = await repository.getCommit(head);
|
||||
title = commit.message.replace(/\n.*$/m, '');
|
||||
title = commit.message.split('\n')[0];
|
||||
body = commit.message.slice(title.length + 1).trim();
|
||||
}
|
||||
|
||||
let body: string | undefined;
|
||||
|
||||
const templates = await findPullRequestTemplates(repository.rootUri);
|
||||
if (templates.length > 0) {
|
||||
templates.sort((a, b) => a.path.localeCompare(b.path));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, ExtensionContext, Uri } from 'vscode';
|
||||
import { LanguageClientOptions } from 'vscode-languageclient';
|
||||
import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient';
|
||||
import { startClient, LanguageClientConstructor } from '../htmlClient';
|
||||
import { LanguageClient } from 'vscode-languageclient/browser';
|
||||
|
||||
@@ -15,8 +15,10 @@ declare const TextDecoder: {
|
||||
new(encoding?: string): { decode(buffer: ArrayBuffer): string };
|
||||
};
|
||||
|
||||
let client: BaseLanguageClient | undefined;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: ExtensionContext) {
|
||||
export async function activate(context: ExtensionContext) {
|
||||
const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/htmlServerMain.js');
|
||||
try {
|
||||
const worker = new Worker(serverMain.toString());
|
||||
@@ -31,9 +33,17 @@ export function activate(context: ExtensionContext) {
|
||||
}
|
||||
};
|
||||
|
||||
startClient(context, newLanguageClient, { TextDecoder, timer });
|
||||
client = await startClient(context, newLanguageClient, { TextDecoder, timer });
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deactivate(): Promise<void> {
|
||||
if (client) {
|
||||
await client.stop();
|
||||
client = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from 'vscode';
|
||||
import {
|
||||
LanguageClientOptions, RequestType, DocumentRangeFormattingParams,
|
||||
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, TextDocumentIdentifier, RequestType0, Range as LspRange, Position as LspPosition, NotificationType, CommonLanguageClient
|
||||
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, TextDocumentIdentifier, RequestType0, Range as LspRange, Position as LspPosition, NotificationType, BaseLanguageClient
|
||||
} from 'vscode-languageclient';
|
||||
import { FileSystemProvider, serveFileSystemRequests } from './requests';
|
||||
import { getCustomDataSource } from './customData';
|
||||
@@ -72,7 +72,7 @@ export interface TelemetryReporter {
|
||||
}): void;
|
||||
}
|
||||
|
||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient;
|
||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;
|
||||
|
||||
export interface Runtime {
|
||||
TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string } };
|
||||
@@ -83,18 +83,17 @@ export interface Runtime {
|
||||
};
|
||||
}
|
||||
|
||||
export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) {
|
||||
export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<BaseLanguageClient> {
|
||||
|
||||
let toDispose = context.subscriptions;
|
||||
const toDispose = context.subscriptions;
|
||||
|
||||
|
||||
let documentSelector = ['html', 'handlebars'];
|
||||
let embeddedLanguages = { css: true, javascript: true };
|
||||
const documentSelector = ['html', 'handlebars'];
|
||||
const embeddedLanguages = { css: true, javascript: true };
|
||||
|
||||
let rangeFormatting: Disposable | undefined = undefined;
|
||||
|
||||
// Options to control the language client
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
documentSelector,
|
||||
synchronize: {
|
||||
configurationSection: ['html', 'css', 'javascript'], // the settings to synchronize
|
||||
@@ -135,67 +134,65 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
||||
let client = newLanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), clientOptions);
|
||||
client.registerProposedFeatures();
|
||||
|
||||
let disposable = client.start();
|
||||
toDispose.push(disposable);
|
||||
client.onReady().then(() => {
|
||||
await client.start();
|
||||
|
||||
toDispose.push(serveFileSystemRequests(client, runtime));
|
||||
toDispose.push(serveFileSystemRequests(client, runtime));
|
||||
|
||||
const customDataSource = getCustomDataSource(runtime, context.subscriptions);
|
||||
const customDataSource = getCustomDataSource(runtime, context.subscriptions);
|
||||
|
||||
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
|
||||
customDataSource.onDidChange(() => {
|
||||
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
|
||||
customDataSource.onDidChange(() => {
|
||||
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
|
||||
});
|
||||
client.onRequest(CustomDataContent.type, customDataSource.getContent);
|
||||
|
||||
|
||||
const insertRequestor = (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position): Promise<string> => {
|
||||
let param: AutoInsertParams = {
|
||||
kind,
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
position: client.code2ProtocolConverter.asPosition(position)
|
||||
};
|
||||
return client.sendRequest(AutoInsertRequest.type, param);
|
||||
};
|
||||
let disposable = activateAutoInsertion(insertRequestor, { html: true, handlebars: true }, runtime);
|
||||
toDispose.push(disposable);
|
||||
|
||||
disposable = client.onTelemetry(e => {
|
||||
runtime.telemetry?.sendTelemetryEvent(e.key, e.data);
|
||||
});
|
||||
toDispose.push(disposable);
|
||||
|
||||
// manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652.
|
||||
updateFormatterRegistration();
|
||||
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
|
||||
toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(SettingIds.formatEnable) && updateFormatterRegistration()));
|
||||
|
||||
client.sendRequest(SemanticTokenLegendRequest.type).then(legend => {
|
||||
if (legend) {
|
||||
const provider: DocumentSemanticTokensProvider & DocumentRangeSemanticTokensProvider = {
|
||||
provideDocumentSemanticTokens(doc) {
|
||||
const params: SemanticTokenParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
|
||||
};
|
||||
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
|
||||
return data && new SemanticTokens(new Uint32Array(data));
|
||||
});
|
||||
},
|
||||
provideDocumentRangeSemanticTokens(doc, range) {
|
||||
const params: SemanticTokenParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
|
||||
ranges: [client.code2ProtocolConverter.asRange(range)]
|
||||
};
|
||||
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
|
||||
return data && new SemanticTokens(new Uint32Array(data));
|
||||
});
|
||||
}
|
||||
};
|
||||
toDispose.push(languages.registerDocumentSemanticTokensProvider(documentSelector, provider, new SemanticTokensLegend(legend.types, legend.modifiers)));
|
||||
}
|
||||
});
|
||||
});
|
||||
client.onRequest(CustomDataContent.type, customDataSource.getContent);
|
||||
|
||||
|
||||
const insertRequestor = (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position): Promise<string> => {
|
||||
const param: AutoInsertParams = {
|
||||
kind,
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
position: client.code2ProtocolConverter.asPosition(position)
|
||||
};
|
||||
return client.sendRequest(AutoInsertRequest.type, param);
|
||||
};
|
||||
const disposable = activateAutoInsertion(insertRequestor, { html: true, handlebars: true }, runtime);
|
||||
toDispose.push(disposable);
|
||||
|
||||
const disposable2 = client.onTelemetry(e => {
|
||||
runtime.telemetry?.sendTelemetryEvent(e.key, e.data);
|
||||
});
|
||||
toDispose.push(disposable2);
|
||||
|
||||
// manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652.
|
||||
updateFormatterRegistration();
|
||||
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
|
||||
toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(SettingIds.formatEnable) && updateFormatterRegistration()));
|
||||
|
||||
client.sendRequest(SemanticTokenLegendRequest.type).then(legend => {
|
||||
if (legend) {
|
||||
const provider: DocumentSemanticTokensProvider & DocumentRangeSemanticTokensProvider = {
|
||||
provideDocumentSemanticTokens(doc) {
|
||||
const params: SemanticTokenParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
|
||||
};
|
||||
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
|
||||
return data && new SemanticTokens(new Uint32Array(data));
|
||||
});
|
||||
},
|
||||
provideDocumentRangeSemanticTokens(doc, range) {
|
||||
const params: SemanticTokenParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
|
||||
ranges: [client.code2ProtocolConverter.asRange(range)]
|
||||
};
|
||||
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
|
||||
return data && new SemanticTokens(new Uint32Array(data));
|
||||
});
|
||||
}
|
||||
};
|
||||
toDispose.push(languages.registerDocumentSemanticTokensProvider(documentSelector, provider, new SemanticTokensLegend(legend.types, legend.modifiers)));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function updateFormatterRegistration() {
|
||||
const formatEnabled = workspace.getConfiguration().get(SettingIds.formatEnable);
|
||||
@@ -219,7 +216,7 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
||||
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
||||
client.protocol2CodeConverter.asTextEdits,
|
||||
(error) => {
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []);
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
);
|
||||
@@ -300,4 +297,6 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
|
||||
}
|
||||
|
||||
@@ -6,16 +6,17 @@
|
||||
import { getNodeFileFS } from './nodeFs';
|
||||
import { Disposable, ExtensionContext } from 'vscode';
|
||||
import { startClient, LanguageClientConstructor } from '../htmlClient';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node';
|
||||
import { TextDecoder } from 'util';
|
||||
import * as fs from 'fs';
|
||||
import TelemetryReporter from '@vscode/extension-telemetry';
|
||||
|
||||
|
||||
let telemetry: TelemetryReporter | undefined;
|
||||
let client: BaseLanguageClient | undefined;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: ExtensionContext) {
|
||||
export async function activate(context: ExtensionContext) {
|
||||
|
||||
let clientPackageJSON = getPackageInfo(context);
|
||||
telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey);
|
||||
@@ -44,7 +45,14 @@ export function activate(context: ExtensionContext) {
|
||||
}
|
||||
};
|
||||
|
||||
startClient(context, newLanguageClient, { fileFs: getNodeFileFS(), TextDecoder, telemetry, timer });
|
||||
client = await startClient(context, newLanguageClient, { fileFs: getNodeFileFS(), TextDecoder, telemetry, timer });
|
||||
}
|
||||
|
||||
export async function deactivate(): Promise<void> {
|
||||
if (client) {
|
||||
await client.stop();
|
||||
client = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
interface IPackageInfo {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Uri, workspace, Disposable } from 'vscode';
|
||||
import { RequestType, CommonLanguageClient } from 'vscode-languageclient';
|
||||
import { RequestType, BaseLanguageClient } from 'vscode-languageclient';
|
||||
import { Runtime } from './htmlClient';
|
||||
|
||||
export namespace FsStatRequest {
|
||||
@@ -15,7 +15,7 @@ export namespace FsReadDirRequest {
|
||||
export const type: RequestType<string, [string, FileType][], any> = new RequestType('fs/readDir');
|
||||
}
|
||||
|
||||
export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime): Disposable {
|
||||
export function serveFileSystemRequests(client: BaseLanguageClient, runtime: Runtime): Disposable {
|
||||
const disposables = [];
|
||||
disposables.push(client.onRequest(FsReadDirRequest.type, (uriString: string) => {
|
||||
const uri = Uri.parse(uriString);
|
||||
|
||||
@@ -261,9 +261,9 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vscode/extension-telemetry": "0.4.10",
|
||||
"vscode-languageclient": "^7.0.0",
|
||||
"vscode-nls": "^5.0.0",
|
||||
"@vscode/extension-telemetry": "0.5.1",
|
||||
"vscode-languageclient": "^8.0.1",
|
||||
"vscode-nls": "^5.0.1",
|
||||
"vscode-uri": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -31,7 +31,7 @@ module.exports = function () {
|
||||
queue.push(name);
|
||||
};
|
||||
|
||||
enqueue('es6');
|
||||
enqueue('es2020.full');
|
||||
|
||||
var result = [];
|
||||
while (queue.length > 0) {
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
},
|
||||
"main": "./out/node/htmlServerMain",
|
||||
"dependencies": {
|
||||
"vscode-css-languageservice": "^5.4.2",
|
||||
"vscode-html-languageservice": "^4.2.5",
|
||||
"vscode-languageserver": "^7.0.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.3",
|
||||
"vscode-nls": "^5.0.0",
|
||||
"vscode-css-languageservice": "^6.0.1",
|
||||
"vscode-html-languageservice": "^5.0.0",
|
||||
"vscode-languageserver": "^8.0.1",
|
||||
"vscode-languageserver-textdocument": "^1.0.4",
|
||||
"vscode-nls": "^5.0.1",
|
||||
"vscode-uri": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from 'vscode-languageserver';
|
||||
import {
|
||||
getLanguageModes, LanguageModes, Settings, TextDocument, Position, Diagnostic, WorkspaceFolder, ColorInformation,
|
||||
Range, DocumentLink, SymbolInformation, TextDocumentIdentifier
|
||||
Range, DocumentLink, SymbolInformation, TextDocumentIdentifier, isCompletionItemData
|
||||
} from './modes/languageModes';
|
||||
|
||||
import { format } from './modes/formatting';
|
||||
@@ -19,6 +19,7 @@ import { pushAll } from './utils/arrays';
|
||||
import { getDocumentContext } from './utils/documentContext';
|
||||
import { URI } from 'vscode-uri';
|
||||
import { formatError, runSafe } from './utils/runner';
|
||||
import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation';
|
||||
|
||||
import { getFoldingRanges } from './modes/htmlFolding';
|
||||
import { fetchHTMLDataProviders } from './customData';
|
||||
@@ -92,6 +93,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
|
||||
let languageModes: LanguageModes;
|
||||
|
||||
let diagnosticsSupport: DiagnosticsSupport | undefined;
|
||||
|
||||
let clientSnippetSupport = false;
|
||||
let dynamicFormatterRegistration = false;
|
||||
let scopedSettingsSupport = false;
|
||||
@@ -129,7 +132,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
// After the server has started the client sends an initialize request. The server receives
|
||||
// in the passed params the rootPath of the workspace plus the client capabilities
|
||||
connection.onInitialize((params: InitializeParams): InitializeResult => {
|
||||
const initializationOptions = params.initializationOptions;
|
||||
const initializationOptions = params.initializationOptions as any || {};
|
||||
|
||||
workspaceFolders = (<any>params).workspaceFolders;
|
||||
if (!Array.isArray(workspaceFolders)) {
|
||||
@@ -179,14 +182,22 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
scopedSettingsSupport = getClientCapability('workspace.configuration', false);
|
||||
workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false);
|
||||
foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
|
||||
formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
|
||||
formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
|
||||
|
||||
const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined);
|
||||
if (supportsDiagnosticPull === undefined) {
|
||||
diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument);
|
||||
} else {
|
||||
diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument);
|
||||
}
|
||||
|
||||
const capabilities: ServerCapabilities = {
|
||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||
completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined,
|
||||
hoverProvider: true,
|
||||
documentHighlightProvider: true,
|
||||
documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true,
|
||||
documentFormattingProvider: params.initializationOptions?.provideFormatter === true,
|
||||
documentRangeFormattingProvider: initializationOptions?.provideFormatter === true,
|
||||
documentFormattingProvider: initializationOptions?.provideFormatter === true,
|
||||
documentLinkProvider: { resolveProvider: false },
|
||||
documentSymbolProvider: true,
|
||||
definitionProvider: true,
|
||||
@@ -196,7 +207,12 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
foldingRangeProvider: true,
|
||||
selectionRangeProvider: true,
|
||||
renameProvider: true,
|
||||
linkedEditingRangeProvider: true
|
||||
linkedEditingRangeProvider: true,
|
||||
diagnosticProvider: {
|
||||
documentSelector: null,
|
||||
interFileDependencies: false,
|
||||
workspaceDiagnostics: false
|
||||
}
|
||||
};
|
||||
return { capabilities };
|
||||
});
|
||||
@@ -217,7 +233,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
}
|
||||
}
|
||||
workspaceFolders = updatedFolders.concat(toAdd);
|
||||
documents.all().forEach(triggerValidation);
|
||||
diagnosticsSupport?.requestRefresh();
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -226,9 +242,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
|
||||
// The settings have changed. Is send on server activation as well.
|
||||
connection.onDidChangeConfiguration((change) => {
|
||||
globalSettings = change.settings;
|
||||
globalSettings = change.settings as Settings;
|
||||
documentSettings = {}; // reset all document settings
|
||||
documents.all().forEach(triggerValidation);
|
||||
diagnosticsSupport?.requestRefresh();
|
||||
|
||||
// dynamically enable & disable the formatter
|
||||
if (dynamicFormatterRegistration) {
|
||||
@@ -248,37 +264,6 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
}
|
||||
});
|
||||
|
||||
const pendingValidationRequests: { [uri: string]: Disposable } = {};
|
||||
const validationDelayMs = 500;
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent(change => {
|
||||
triggerValidation(change.document);
|
||||
});
|
||||
|
||||
// a document has closed: clear all diagnostics
|
||||
documents.onDidClose(event => {
|
||||
cleanPendingValidation(event.document);
|
||||
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
||||
});
|
||||
|
||||
function cleanPendingValidation(textDocument: TextDocument): void {
|
||||
const request = pendingValidationRequests[textDocument.uri];
|
||||
if (request) {
|
||||
request.dispose();
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerValidation(textDocument: TextDocument): void {
|
||||
cleanPendingValidation(textDocument);
|
||||
pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => {
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
validateTextDocument(textDocument);
|
||||
}, validationDelayMs);
|
||||
}
|
||||
|
||||
function isValidationEnabled(languageId: string, settings: Settings = globalSettings) {
|
||||
const validationSettings = settings && settings.html && settings.html.validate;
|
||||
if (validationSettings) {
|
||||
@@ -287,7 +272,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
return true;
|
||||
}
|
||||
|
||||
async function validateTextDocument(textDocument: TextDocument) {
|
||||
async function validateTextDocument(textDocument: TextDocument): Promise<Diagnostic[]> {
|
||||
try {
|
||||
const version = textDocument.version;
|
||||
const diagnostics: Diagnostic[] = [];
|
||||
@@ -301,12 +286,13 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
pushAll(diagnostics, await mode.doValidation(latestTextDocument, settings));
|
||||
}
|
||||
}
|
||||
connection.sendDiagnostics({ uri: latestTextDocument.uri, diagnostics });
|
||||
return diagnostics;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
connection.onCompletion(async (textDocumentPosition, token) => {
|
||||
@@ -321,15 +307,6 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
}
|
||||
const doComplete = mode.doComplete;
|
||||
|
||||
if (mode.getId() !== 'html') {
|
||||
/* __GDPR__
|
||||
"html.embbedded.complete" : {
|
||||
"languageId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } });
|
||||
}
|
||||
|
||||
const settings = await getDocumentSettings(document, () => doComplete.length > 2);
|
||||
const documentContext = getDocumentContext(document.uri, workspaceFolders);
|
||||
return doComplete(document, textDocumentPosition.position, documentContext, settings);
|
||||
@@ -340,7 +317,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
connection.onCompletionResolve((item, token) => {
|
||||
return runSafe(runtime, async () => {
|
||||
const data = item.data;
|
||||
if (data && data.languageId && data.uri) {
|
||||
if (isCompletionItemData(data)) {
|
||||
const mode = languageModes.getMode(data.languageId);
|
||||
const document = documents.get(data.uri);
|
||||
if (mode && mode.doResolve && document) {
|
||||
|
||||
@@ -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, Diagnostic } from './languageModes';
|
||||
import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport';
|
||||
|
||||
export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegions: LanguageModelCache<HTMLDocumentRegions>, workspace: Workspace): LanguageMode {
|
||||
@@ -18,7 +18,7 @@ export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegio
|
||||
},
|
||||
async doValidation(document: TextDocument, settings = workspace.settings) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded), settings && settings.css);
|
||||
return (cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded), settings && settings.css) as Diagnostic[]);
|
||||
},
|
||||
async doComplete(document: TextDocument, position: Position, documentContext: DocumentContext, _settings = workspace.settings) {
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
|
||||
@@ -24,7 +24,7 @@ export function loadLibrary(name: string) {
|
||||
try {
|
||||
content = readFileSync(libPath).toString();
|
||||
} catch (e) {
|
||||
console.log(`Unable to load library ${name} at ${libPath}: ${e.message}`);
|
||||
console.log(`Unable to load library ${name} at ${libPath}`);
|
||||
content = '';
|
||||
}
|
||||
contents[name] = content;
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation,
|
||||
Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover,
|
||||
DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind, SelectionRange,
|
||||
LanguageMode, Settings, SemanticTokenData, Workspace, DocumentContext
|
||||
LanguageMode, Settings, SemanticTokenData, Workspace, DocumentContext, CompletionItemData, isCompletionItemData
|
||||
} from './languageModes';
|
||||
import { getWordAtText, isWhitespaceOnly, repeat } from '../utils/strings';
|
||||
import { HTMLDocumentRegions } from './embeddedSupport';
|
||||
@@ -19,7 +19,7 @@ import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticT
|
||||
const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
|
||||
|
||||
function getLanguageServiceHost(scriptKind: ts.ScriptKind) {
|
||||
const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false };
|
||||
const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es2020.full.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false };
|
||||
|
||||
let currentTextDocument = TextDocument.create('init', 'javascript', 1, '');
|
||||
const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs').then(libs => {
|
||||
@@ -52,7 +52,7 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) {
|
||||
};
|
||||
},
|
||||
getCurrentDirectory: () => '',
|
||||
getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6',
|
||||
getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es2020.full',
|
||||
readFile: (path: string, _encoding?: string | undefined): string | undefined => {
|
||||
if (path === currentTextDocument.uri) {
|
||||
return currentTextDocument.getText();
|
||||
@@ -66,6 +66,15 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) {
|
||||
} else {
|
||||
return !!libs.loadLibrary(path);
|
||||
}
|
||||
},
|
||||
directoryExists: (path: string): boolean => {
|
||||
// typescript tries to first find libraries in node_modules/@types and node_modules/@typescript
|
||||
// there's no node_modules in our setup
|
||||
if (path.startsWith('node_modules')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
};
|
||||
return ts.createLanguageService(host);
|
||||
@@ -122,6 +131,11 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
return {
|
||||
isIncomplete: false,
|
||||
items: completions.entries.map(entry => {
|
||||
const data: CompletionItemData = { // data used for resolving item details (see 'doResolve')
|
||||
languageId,
|
||||
uri: document.uri,
|
||||
offset: offset
|
||||
};
|
||||
return {
|
||||
uri: document.uri,
|
||||
position: position,
|
||||
@@ -129,23 +143,21 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache<HTMLDocume
|
||||
sortText: entry.sortText,
|
||||
kind: convertKind(entry.kind),
|
||||
textEdit: TextEdit.replace(replaceRange, entry.name),
|
||||
data: { // data used for resolving item details (see 'doResolve')
|
||||
languageId,
|
||||
uri: document.uri,
|
||||
offset: offset
|
||||
}
|
||||
data
|
||||
};
|
||||
})
|
||||
};
|
||||
},
|
||||
async doResolve(document: TextDocument, item: CompletionItem): Promise<CompletionItem> {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined, undefined);
|
||||
if (details) {
|
||||
item.detail = ts.displayPartsToString(details.displayParts);
|
||||
item.documentation = ts.displayPartsToString(details.documentation);
|
||||
delete item.data;
|
||||
if (isCompletionItemData(item.data)) {
|
||||
const jsDocument = jsDocuments.get(document);
|
||||
const jsLanguageService = await host.getLanguageService(jsDocument);
|
||||
let details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined, undefined);
|
||||
if (details) {
|
||||
item.detail = ts.displayPartsToString(details.displayParts);
|
||||
item.documentation = ts.displayPartsToString(details.documentation);
|
||||
delete item.data;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
},
|
||||
|
||||
@@ -54,6 +54,16 @@ export interface SemanticTokenData {
|
||||
modifierSet: number;
|
||||
}
|
||||
|
||||
export type CompletionItemData = {
|
||||
languageId: string;
|
||||
uri: string;
|
||||
offset: number;
|
||||
};
|
||||
|
||||
export function isCompletionItemData(value: any): value is CompletionItemData {
|
||||
return value && typeof value.languageId === 'string' && typeof value.uri === 'string' && typeof value.offset === 'number';
|
||||
}
|
||||
|
||||
export interface LanguageMode {
|
||||
getId(): string;
|
||||
getSelectionRange?: (document: TextDocument, position: Position) => Promise<SelectionRange>;
|
||||
|
||||
@@ -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 { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver';
|
||||
import { TextDocument } from 'vscode-html-languageservice';
|
||||
import { formatError, runSafe } from './runner';
|
||||
import { RuntimeEnvironment } from '../htmlServer';
|
||||
|
||||
export type Validator = (textDocument: TextDocument) => Promise<Diagnostic[]>;
|
||||
export type DiagnosticsSupport = {
|
||||
dispose(): void;
|
||||
requestRefresh(): void;
|
||||
};
|
||||
|
||||
export function registerDiagnosticsPushSupport(documents: TextDocuments<TextDocument>, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport {
|
||||
|
||||
const pendingValidationRequests: { [uri: string]: Disposable } = {};
|
||||
const validationDelayMs = 500;
|
||||
|
||||
const disposables: Disposable[] = [];
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent(change => {
|
||||
triggerValidation(change.document);
|
||||
}, undefined, disposables);
|
||||
|
||||
// a document has closed: clear all diagnostics
|
||||
documents.onDidClose(event => {
|
||||
cleanPendingValidation(event.document);
|
||||
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
||||
}, undefined, disposables);
|
||||
|
||||
function cleanPendingValidation(textDocument: TextDocument): void {
|
||||
const request = pendingValidationRequests[textDocument.uri];
|
||||
if (request) {
|
||||
request.dispose();
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerValidation(textDocument: TextDocument): void {
|
||||
cleanPendingValidation(textDocument);
|
||||
const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => {
|
||||
if (request === pendingValidationRequests[textDocument.uri]) {
|
||||
try {
|
||||
const diagnostics = await validate(textDocument);
|
||||
if (request === pendingValidationRequests[textDocument.uri]) {
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||
}
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
} catch (e) {
|
||||
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
|
||||
}
|
||||
}
|
||||
}, validationDelayMs);
|
||||
}
|
||||
|
||||
return {
|
||||
requestRefresh: () => {
|
||||
documents.all().forEach(triggerValidation);
|
||||
},
|
||||
dispose: () => {
|
||||
disposables.forEach(d => d.dispose());
|
||||
disposables.length = 0;
|
||||
const keys = Object.keys(pendingValidationRequests);
|
||||
for (const key of keys) {
|
||||
pendingValidationRequests[key].dispose();
|
||||
delete pendingValidationRequests[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function registerDiagnosticsPullSupport(documents: TextDocuments<TextDocument>, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport {
|
||||
|
||||
function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport {
|
||||
return {
|
||||
kind: DocumentDiagnosticReportKind.Full,
|
||||
items: diagnostics
|
||||
};
|
||||
}
|
||||
|
||||
const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => {
|
||||
return runSafe(runtime, async () => {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return newDocumentDiagnosticReport(await validate(document));
|
||||
}
|
||||
return newDocumentDiagnosticReport([]);
|
||||
|
||||
}, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token);
|
||||
});
|
||||
|
||||
function requestRefresh(): void {
|
||||
connection.languages.diagnostics.refresh();
|
||||
}
|
||||
|
||||
return {
|
||||
requestRefresh,
|
||||
dispose: () => {
|
||||
registration.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -12,65 +12,60 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
vscode-css-languageservice@^5.4.2:
|
||||
version "5.4.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.4.2.tgz#69ea74c000bd653dfc8e458a1720d28b9ffa5cfb"
|
||||
integrity sha512-DT7+7vfdT2HDNjDoXWtYJ0lVDdeDEdbMNdK4PKqUl2MS8g7PWt7J5G9B6k9lYox8nOfhCEjLnoNC3UKHHCR1lg==
|
||||
vscode-css-languageservice@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.0.1.tgz#ccf94944e094dcc5833d1b4ac276994b698e9283"
|
||||
integrity sha512-81n/eeYuJwQdvpoy6IK1258PtPbO720fl13FcJ5YQECPyHMFkmld1qKHwPJkyLbLPfboqJPM53ys4xW8v+iBVw==
|
||||
dependencies:
|
||||
vscode-languageserver-textdocument "^1.0.4"
|
||||
vscode-languageserver-types "^3.16.0"
|
||||
vscode-nls "^5.0.0"
|
||||
vscode-languageserver-types "^3.17.1"
|
||||
vscode-nls "^5.0.1"
|
||||
vscode-uri "^3.0.3"
|
||||
|
||||
vscode-html-languageservice@^4.2.5:
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-4.2.5.tgz#c0cc8ff3d824d16388bbac187e1828749eccf006"
|
||||
integrity sha512-dbr10KHabB9EaK8lI0XZW7SqOsTfrNyT3Nuj0GoPi4LjGKUmMiLtsqzfedIzRTzqY+w0FiLdh0/kQrnQ0tLxrw==
|
||||
vscode-html-languageservice@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.0.0.tgz#c68613f836d7fcff125183d78e6f1f0ff326fa55"
|
||||
integrity sha512-KJG13z54aLszskp3ETf8b1EKDypr2Sf5RUsfR6OXmKqEl2ZUfyIxsWz4gbJWjPzoJZx/bGH0ZXVwxJ1rg8OKRQ==
|
||||
dependencies:
|
||||
vscode-languageserver-textdocument "^1.0.4"
|
||||
vscode-languageserver-types "^3.16.0"
|
||||
vscode-nls "^5.0.0"
|
||||
vscode-languageserver-types "^3.17.1"
|
||||
vscode-nls "^5.0.1"
|
||||
vscode-uri "^3.0.3"
|
||||
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
|
||||
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
|
||||
vscode-jsonrpc@8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e"
|
||||
integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==
|
||||
|
||||
vscode-languageserver-protocol@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
|
||||
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
|
||||
vscode-languageserver-protocol@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed"
|
||||
integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==
|
||||
dependencies:
|
||||
vscode-jsonrpc "6.0.0"
|
||||
vscode-languageserver-types "3.16.0"
|
||||
|
||||
vscode-languageserver-textdocument@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz#879f2649bfa5a6e07bc8b392c23ede2dfbf43eff"
|
||||
integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A==
|
||||
vscode-jsonrpc "8.0.1"
|
||||
vscode-languageserver-types "3.17.1"
|
||||
|
||||
vscode-languageserver-textdocument@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157"
|
||||
integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ==
|
||||
|
||||
vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
vscode-languageserver-types@3.17.1, vscode-languageserver-types@^3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
|
||||
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
|
||||
|
||||
vscode-languageserver@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0"
|
||||
integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==
|
||||
vscode-languageserver@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.1.tgz#56bd7a01f5c88af075a77f1d220edcb30fc4bdc7"
|
||||
integrity sha512-sn7SjBwWm3OlmLtgg7jbM0wBULppyL60rj8K5HF0ny/MzN+GzPBX1kCvYdybhl7UW63V5V5tRVnyB8iwC73lSQ==
|
||||
dependencies:
|
||||
vscode-languageserver-protocol "3.16.0"
|
||||
vscode-languageserver-protocol "3.17.1"
|
||||
|
||||
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==
|
||||
vscode-nls@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2"
|
||||
integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A==
|
||||
|
||||
vscode-uri@^3.0.3:
|
||||
version "3.0.3"
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
"@vscode/extension-telemetry@0.4.10":
|
||||
version "0.4.10"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910"
|
||||
integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w==
|
||||
"@vscode/extension-telemetry@0.5.1":
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.1.tgz#20150976629663b3d33799a4ad25944a1535f7db"
|
||||
integrity sha512-cvFq8drxdLRF8KN72WcV4lTEa9GqDiRwy9EbnYuoSCD9Jdk8zHFF49MmACC1qs4R9Ko/C1uMOmeLJmVi8EA0rQ==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
@@ -44,44 +44,44 @@ minimatch@^3.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
semver@^7.3.4:
|
||||
version "7.3.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
|
||||
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
|
||||
semver@^7.3.5:
|
||||
version "7.3.7"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
|
||||
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
|
||||
vscode-jsonrpc@8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e"
|
||||
integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==
|
||||
|
||||
vscode-languageclient@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2"
|
||||
integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==
|
||||
vscode-languageclient@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301"
|
||||
integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw==
|
||||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
semver "^7.3.4"
|
||||
vscode-languageserver-protocol "3.16.0"
|
||||
semver "^7.3.5"
|
||||
vscode-languageserver-protocol "3.17.1"
|
||||
|
||||
vscode-languageserver-protocol@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
|
||||
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
|
||||
vscode-languageserver-protocol@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed"
|
||||
integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==
|
||||
dependencies:
|
||||
vscode-jsonrpc "6.0.0"
|
||||
vscode-languageserver-types "3.16.0"
|
||||
vscode-jsonrpc "8.0.1"
|
||||
vscode-languageserver-types "3.17.1"
|
||||
|
||||
vscode-languageserver-types@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
vscode-languageserver-types@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
|
||||
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
|
||||
|
||||
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==
|
||||
vscode-nls@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2"
|
||||
integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A==
|
||||
|
||||
vscode-uri@^3.0.3:
|
||||
version "3.0.3"
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
"vscode": "^1.57.0"
|
||||
},
|
||||
"enabledApiProposals": [
|
||||
"notebookEditor",
|
||||
"notebookEditorEdit"
|
||||
"notebookWorkspaceEdit"
|
||||
],
|
||||
"activationEvents": [
|
||||
"*"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtensionContext, NotebookDocument, NotebookDocumentChangeEvent, workspace, WorkspaceEdit } from 'vscode';
|
||||
import { ExtensionContext, NotebookDocument, NotebookDocumentChangeEvent, NotebookEdit, workspace, WorkspaceEdit } from 'vscode';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { getCellMetadata } from './serializers';
|
||||
import { CellMetadata } from './common';
|
||||
@@ -34,7 +34,7 @@ function onDidChangeNotebookCells(e: NotebookDocumentChangeEvent) {
|
||||
// Don't edit the metadata directly, always get a clone (prevents accidental singletons and directly editing the objects).
|
||||
const updatedMetadata: CellMetadata = { ...JSON.parse(JSON.stringify(cellMetadata || {})) };
|
||||
updatedMetadata.id = id;
|
||||
edit.replaceNotebookCellMetadata(cell.notebook.uri, cell.index, { ...(cell.metadata), custom: updatedMetadata });
|
||||
edit.set(cell.notebook.uri, [NotebookEdit.updateCellMetadata(cell.index, { ...(cell.metadata), custom: updatedMetadata })]);
|
||||
workspace.applyEdit(edit);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -94,7 +94,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
edit.replaceNotebookMetadata(resource, {
|
||||
edit.set(resource, [vscode.NotebookEdit.updateNotebookMetadata({
|
||||
...document.metadata,
|
||||
custom: {
|
||||
...(document.metadata.custom ?? {}),
|
||||
@@ -103,7 +103,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
...metadata
|
||||
},
|
||||
}
|
||||
});
|
||||
})]);
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"../../src/vscode-dts/vscode.d.ts",
|
||||
"../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts",
|
||||
"../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts"
|
||||
"../../src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtensionContext, Uri } from 'vscode';
|
||||
import { LanguageClientOptions } from 'vscode-languageclient';
|
||||
import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient';
|
||||
import { startClient, LanguageClientConstructor, SchemaRequestService } from '../jsonClient';
|
||||
import { LanguageClient } from 'vscode-languageclient/browser';
|
||||
|
||||
@@ -14,8 +14,10 @@ declare const Worker: {
|
||||
|
||||
declare function fetch(uri: string, options: any): any;
|
||||
|
||||
let client: BaseLanguageClient | undefined;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: ExtensionContext) {
|
||||
export async function activate(context: ExtensionContext) {
|
||||
const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/jsonServerMain.js');
|
||||
try {
|
||||
const worker = new Worker(serverMain.toString());
|
||||
@@ -32,9 +34,16 @@ export function activate(context: ExtensionContext) {
|
||||
}
|
||||
};
|
||||
|
||||
startClient(context, newLanguageClient, { schemaRequests });
|
||||
client = await startClient(context, newLanguageClient, { schemaRequests });
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deactivate(): Promise<void> {
|
||||
if (client) {
|
||||
await client.stop();
|
||||
client = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@ import {
|
||||
import {
|
||||
LanguageClientOptions, RequestType, NotificationType,
|
||||
DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams,
|
||||
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient
|
||||
DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, BaseLanguageClient
|
||||
} from 'vscode-languageclient';
|
||||
|
||||
|
||||
import { hash } from './utils/hash';
|
||||
import { createLanguageStatusItem } from './languageStatus';
|
||||
|
||||
@@ -56,7 +57,7 @@ namespace ResultLimitReachedNotification {
|
||||
export const type: NotificationType<string> = new NotificationType('json/resultLimitReached');
|
||||
}
|
||||
|
||||
interface Settings {
|
||||
type Settings = {
|
||||
json?: {
|
||||
schemas?: JSONSchemaSettings[];
|
||||
format?: { enable?: boolean };
|
||||
@@ -67,13 +68,13 @@ interface Settings {
|
||||
proxy?: string;
|
||||
proxyStrictSSL?: boolean;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export interface JSONSchemaSettings {
|
||||
export type JSONSchemaSettings = {
|
||||
fileMatch?: string[];
|
||||
url?: string;
|
||||
schema?: any;
|
||||
}
|
||||
};
|
||||
|
||||
namespace SettingIds {
|
||||
export const enableFormatter = 'json.format.enable';
|
||||
@@ -94,7 +95,7 @@ export interface TelemetryReporter {
|
||||
}): void;
|
||||
}
|
||||
|
||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient;
|
||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;
|
||||
|
||||
export interface Runtime {
|
||||
schemaRequests: SchemaRequestService;
|
||||
@@ -108,7 +109,7 @@ export interface SchemaRequestService {
|
||||
|
||||
export const languageServerDescription = localize('jsonserver.name', 'JSON Language Server');
|
||||
|
||||
export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) {
|
||||
export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<BaseLanguageClient> {
|
||||
|
||||
const toDispose = context.subscriptions;
|
||||
|
||||
@@ -218,176 +219,177 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
|
||||
const client = newLanguageClient('json', languageServerDescription, clientOptions);
|
||||
client.registerProposedFeatures();
|
||||
|
||||
const disposable = client.start();
|
||||
toDispose.push(disposable);
|
||||
client.onReady().then(() => {
|
||||
isClientReady = true;
|
||||
const schemaDocuments: { [uri: string]: boolean } = {};
|
||||
|
||||
const schemaDocuments: { [uri: string]: boolean } = {};
|
||||
|
||||
// handle content request
|
||||
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
||||
const uri = Uri.parse(uriPath);
|
||||
if (uri.scheme === 'untitled') {
|
||||
return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString())));
|
||||
}
|
||||
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
|
||||
return workspace.openTextDocument(uri).then(doc => {
|
||||
schemaDocuments[uri.toString()] = true;
|
||||
return doc.getText();
|
||||
}, error => {
|
||||
return Promise.reject(new ResponseError(2, error.toString()));
|
||||
});
|
||||
} else if (schemaDownloadEnabled) {
|
||||
if (runtime.telemetry && uri.authority === 'schema.management.azure.com') {
|
||||
/* __GDPR__
|
||||
"json.schema" : {
|
||||
"schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath });
|
||||
}
|
||||
return runtime.schemaRequests.getContent(uriPath).catch(e => {
|
||||
return Promise.reject(new ResponseError(4, e.toString()));
|
||||
});
|
||||
} else {
|
||||
return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload)));
|
||||
}
|
||||
});
|
||||
|
||||
const handleContentChange = (uriString: string) => {
|
||||
if (schemaDocuments[uriString]) {
|
||||
client.sendNotification(SchemaContentChangeNotification.type, uriString);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const handleActiveEditorChange = (activeEditor?: TextEditor) => {
|
||||
if (!activeEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeDocUri = activeEditor.document.uri.toString();
|
||||
|
||||
if (activeDocUri && fileSchemaErrors.has(activeDocUri)) {
|
||||
schemaResolutionErrorStatusBarItem.show();
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.hide();
|
||||
}
|
||||
};
|
||||
|
||||
toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString())));
|
||||
toDispose.push(workspace.onDidCloseTextDocument(d => {
|
||||
const uriString = d.uri.toString();
|
||||
if (handleContentChange(uriString)) {
|
||||
delete schemaDocuments[uriString];
|
||||
}
|
||||
fileSchemaErrors.delete(uriString);
|
||||
}));
|
||||
toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange));
|
||||
|
||||
const handleRetryResolveSchemaCommand = () => {
|
||||
if (window.activeTextEditor) {
|
||||
schemaResolutionErrorStatusBarItem.text = '$(watch)';
|
||||
const activeDocUri = window.activeTextEditor.document.uri.toString();
|
||||
client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => {
|
||||
const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError);
|
||||
if (schemaErrorIndex !== -1) {
|
||||
// Show schema resolution errors in status bar only; ref: #51032
|
||||
const schemaResolveDiagnostic = diagnostics[schemaErrorIndex];
|
||||
fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message);
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.hide();
|
||||
}
|
||||
schemaResolutionErrorStatusBarItem.text = '$(alert)';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand));
|
||||
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
||||
|
||||
toDispose.push(extensions.onDidChange(_ => {
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
||||
}));
|
||||
|
||||
// manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652.
|
||||
updateFormatterRegistration();
|
||||
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
|
||||
|
||||
updateSchemaDownloadSetting();
|
||||
|
||||
toDispose.push(workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(SettingIds.enableFormatter)) {
|
||||
updateFormatterRegistration();
|
||||
} else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) {
|
||||
updateSchemaDownloadSetting();
|
||||
}
|
||||
}));
|
||||
|
||||
client.onNotification(ResultLimitReachedNotification.type, async message => {
|
||||
const shouldPrompt = context.globalState.get<boolean>(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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
toDispose.push(createLanguageStatusItem(documentSelector, (uri: string) => client.sendRequest(LanguageStatusRequest.type, uri)));
|
||||
|
||||
function updateFormatterRegistration() {
|
||||
const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter);
|
||||
if (!formatEnabled && rangeFormatting) {
|
||||
rangeFormatting.dispose();
|
||||
rangeFormatting = undefined;
|
||||
} else if (formatEnabled && !rangeFormatting) {
|
||||
rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, {
|
||||
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
|
||||
const filesConfig = workspace.getConfiguration('files', document);
|
||||
const fileFormattingOptions = {
|
||||
trimTrailingWhitespace: filesConfig.get<boolean>('trimTrailingWhitespace'),
|
||||
trimFinalNewlines: filesConfig.get<boolean>('trimFinalNewlines'),
|
||||
insertFinalNewline: filesConfig.get<boolean>('insertFinalNewline'),
|
||||
};
|
||||
const params: DocumentRangeFormattingParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
range: client.code2ProtocolConverter.asRange(range),
|
||||
options: client.code2ProtocolConverter.asFormattingOptions(options, fileFormattingOptions)
|
||||
};
|
||||
|
||||
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
||||
client.protocol2CodeConverter.asTextEdits,
|
||||
(error) => {
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
// handle content request
|
||||
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
||||
const uri = Uri.parse(uriPath);
|
||||
if (uri.scheme === 'untitled') {
|
||||
return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString())));
|
||||
}
|
||||
|
||||
function updateSchemaDownloadSetting() {
|
||||
schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false;
|
||||
if (schemaDownloadEnabled) {
|
||||
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.');
|
||||
schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema';
|
||||
handleRetryResolveSchemaCommand();
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.');
|
||||
schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' };
|
||||
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
|
||||
return workspace.openTextDocument(uri).then(doc => {
|
||||
schemaDocuments[uri.toString()] = true;
|
||||
return doc.getText();
|
||||
}, error => {
|
||||
return Promise.reject(new ResponseError(2, error.toString()));
|
||||
});
|
||||
} else if (schemaDownloadEnabled) {
|
||||
if (runtime.telemetry && uri.authority === 'schema.management.azure.com') {
|
||||
/* __GDPR__
|
||||
"json.schema" : {
|
||||
"owner": "aeschli",
|
||||
"comment": "Measure the use of the Azure resource manager schemas",
|
||||
"schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The azure schema URL that was requested." }
|
||||
}
|
||||
*/
|
||||
runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath });
|
||||
}
|
||||
return runtime.schemaRequests.getContent(uriPath).catch(e => {
|
||||
return Promise.reject(new ResponseError(4, e.toString()));
|
||||
});
|
||||
} else {
|
||||
return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload)));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
await client.start();
|
||||
|
||||
isClientReady = true;
|
||||
|
||||
const handleContentChange = (uriString: string) => {
|
||||
if (schemaDocuments[uriString]) {
|
||||
client.sendNotification(SchemaContentChangeNotification.type, uriString);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const handleActiveEditorChange = (activeEditor?: TextEditor) => {
|
||||
if (!activeEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeDocUri = activeEditor.document.uri.toString();
|
||||
|
||||
if (activeDocUri && fileSchemaErrors.has(activeDocUri)) {
|
||||
schemaResolutionErrorStatusBarItem.show();
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.hide();
|
||||
}
|
||||
};
|
||||
|
||||
toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString())));
|
||||
toDispose.push(workspace.onDidCloseTextDocument(d => {
|
||||
const uriString = d.uri.toString();
|
||||
if (handleContentChange(uriString)) {
|
||||
delete schemaDocuments[uriString];
|
||||
}
|
||||
fileSchemaErrors.delete(uriString);
|
||||
}));
|
||||
toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange));
|
||||
|
||||
const handleRetryResolveSchemaCommand = () => {
|
||||
if (window.activeTextEditor) {
|
||||
schemaResolutionErrorStatusBarItem.text = '$(watch)';
|
||||
const activeDocUri = window.activeTextEditor.document.uri.toString();
|
||||
client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => {
|
||||
const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError);
|
||||
if (schemaErrorIndex !== -1) {
|
||||
// Show schema resolution errors in status bar only; ref: #51032
|
||||
const schemaResolveDiagnostic = diagnostics[schemaErrorIndex];
|
||||
fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message);
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.hide();
|
||||
}
|
||||
schemaResolutionErrorStatusBarItem.text = '$(alert)';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand));
|
||||
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
||||
|
||||
toDispose.push(extensions.onDidChange(_ => {
|
||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
||||
}));
|
||||
|
||||
// manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652.
|
||||
updateFormatterRegistration();
|
||||
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
|
||||
|
||||
updateSchemaDownloadSetting();
|
||||
|
||||
toDispose.push(workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(SettingIds.enableFormatter)) {
|
||||
updateFormatterRegistration();
|
||||
} else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) {
|
||||
updateSchemaDownloadSetting();
|
||||
}
|
||||
}));
|
||||
|
||||
client.onNotification(ResultLimitReachedNotification.type, async message => {
|
||||
const shouldPrompt = context.globalState.get<boolean>(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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
toDispose.push(createLanguageStatusItem(documentSelector, (uri: string) => client.sendRequest(LanguageStatusRequest.type, uri)));
|
||||
|
||||
function updateFormatterRegistration() {
|
||||
const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter);
|
||||
if (!formatEnabled && rangeFormatting) {
|
||||
rangeFormatting.dispose();
|
||||
rangeFormatting = undefined;
|
||||
} else if (formatEnabled && !rangeFormatting) {
|
||||
rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, {
|
||||
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
|
||||
const filesConfig = workspace.getConfiguration('files', document);
|
||||
const fileFormattingOptions = {
|
||||
trimTrailingWhitespace: filesConfig.get<boolean>('trimTrailingWhitespace'),
|
||||
trimFinalNewlines: filesConfig.get<boolean>('trimFinalNewlines'),
|
||||
insertFinalNewline: filesConfig.get<boolean>('insertFinalNewline'),
|
||||
};
|
||||
const params: DocumentRangeFormattingParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
range: client.code2ProtocolConverter.asRange(range),
|
||||
options: client.code2ProtocolConverter.asFormattingOptions(options, fileFormattingOptions)
|
||||
};
|
||||
|
||||
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
||||
client.protocol2CodeConverter.asTextEdits,
|
||||
(error) => {
|
||||
client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateSchemaDownloadSetting() {
|
||||
schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false;
|
||||
if (schemaDownloadEnabled) {
|
||||
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.');
|
||||
schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema';
|
||||
handleRetryResolveSchemaCommand();
|
||||
} else {
|
||||
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.');
|
||||
schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' };
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ExtensionContext, OutputChannel, window, workspace } from 'vscode';
|
||||
import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node';
|
||||
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
@@ -15,6 +15,7 @@ import TelemetryReporter from '@vscode/extension-telemetry';
|
||||
import { JSONSchemaCache } from './schemaCache';
|
||||
|
||||
let telemetry: TelemetryReporter | undefined;
|
||||
let client: BaseLanguageClient | undefined;
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export async function activate(context: ExtensionContext) {
|
||||
@@ -45,11 +46,15 @@ export async function activate(context: ExtensionContext) {
|
||||
|
||||
const schemaRequests = await getSchemaRequestService(context, log);
|
||||
|
||||
startClient(context, newLanguageClient, { schemaRequests, telemetry });
|
||||
client = await startClient(context, newLanguageClient, { schemaRequests, telemetry });
|
||||
}
|
||||
|
||||
export function deactivate(): Promise<any> {
|
||||
return telemetry ? telemetry.dispose() : Promise.resolve(null);
|
||||
export async function deactivate(): Promise<any> {
|
||||
if (client) {
|
||||
await client.stop();
|
||||
client = undefined;
|
||||
}
|
||||
telemetry?.dispose();
|
||||
}
|
||||
|
||||
interface IPackageInfo {
|
||||
|
||||
@@ -147,10 +147,10 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vscode/extension-telemetry": "0.5.0",
|
||||
"@vscode/extension-telemetry": "0.5.1",
|
||||
"request-light": "^0.5.8",
|
||||
"vscode-languageclient": "^7.0.0",
|
||||
"vscode-nls": "^5.0.0"
|
||||
"vscode-languageclient": "^8.0.1",
|
||||
"vscode-nls": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "16.x"
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"request-light": "^0.5.8",
|
||||
"vscode-json-languageservice": "^4.2.1",
|
||||
"vscode-languageserver": "^7.0.0",
|
||||
"vscode-json-languageservice": "^5.0.0",
|
||||
"vscode-languageserver": "^8.0.1",
|
||||
"vscode-uri": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
import {
|
||||
Connection,
|
||||
TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType,
|
||||
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions
|
||||
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import { formatError, runSafe, runSafeAsync } from './utils/runner';
|
||||
import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Diagnostic, Range, Position } from 'vscode-json-languageservice';
|
||||
import { runSafe, runSafeAsync } from './utils/runner';
|
||||
import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation';
|
||||
import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position } from 'vscode-json-languageservice';
|
||||
import { getLanguageModelCache } from './languageModelCache';
|
||||
import { Utils, URI } from 'vscode-uri';
|
||||
|
||||
@@ -113,11 +114,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
let resultLimit = Number.MAX_VALUE;
|
||||
let formatterMaxNumberOfEdits = Number.MAX_VALUE;
|
||||
|
||||
let diagnosticsSupport: DiagnosticsSupport | undefined;
|
||||
|
||||
|
||||
// After the server has started the client sends an initialize request. The server receives
|
||||
// in the passed params the rootPath of the workspace plus the client capabilities.
|
||||
connection.onInitialize((params: InitializeParams): InitializeResult => {
|
||||
|
||||
const handledProtocols = params.initializationOptions?.handledSchemaProtocols;
|
||||
const initializationOptions = params.initializationOptions as any || {};
|
||||
|
||||
const handledProtocols = initializationOptions?.handledSchemaProtocols;
|
||||
|
||||
languageService = getLanguageService({
|
||||
schemaRequestService: getSchemaRequestService(handledProtocols),
|
||||
@@ -139,10 +145,18 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
}
|
||||
|
||||
clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
|
||||
dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean');
|
||||
dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof initializationOptions.provideFormatter !== 'boolean');
|
||||
foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
|
||||
hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false);
|
||||
formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
|
||||
formatterMaxNumberOfEdits = initializationOptions.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE;
|
||||
|
||||
const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined);
|
||||
if (supportsDiagnosticPull === undefined) {
|
||||
diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument);
|
||||
} else {
|
||||
diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument);
|
||||
}
|
||||
|
||||
const capabilities: ServerCapabilities = {
|
||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||
completionProvider: clientSnippetSupport ? {
|
||||
@@ -151,12 +165,17 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
} : undefined,
|
||||
hoverProvider: true,
|
||||
documentSymbolProvider: true,
|
||||
documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true,
|
||||
documentFormattingProvider: params.initializationOptions?.provideFormatter === true,
|
||||
documentRangeFormattingProvider: initializationOptions.provideFormatter === true,
|
||||
documentFormattingProvider: initializationOptions.provideFormatter === true,
|
||||
colorProvider: {},
|
||||
foldingRangeProvider: true,
|
||||
selectionRangeProvider: true,
|
||||
documentLinkProvider: {}
|
||||
documentLinkProvider: {},
|
||||
diagnosticProvider: {
|
||||
documentSelector: null,
|
||||
interFileDependencies: false,
|
||||
workspaceDiagnostics: false
|
||||
}
|
||||
};
|
||||
|
||||
return { capabilities };
|
||||
@@ -279,25 +298,18 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
needsRevalidation = languageService.resetSchema(uriOrUris);
|
||||
}
|
||||
if (needsRevalidation) {
|
||||
for (const doc of documents.all()) {
|
||||
triggerValidation(doc);
|
||||
}
|
||||
diagnosticsSupport?.requestRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
// Retry schema validation on all open documents
|
||||
connection.onRequest(ForceValidateRequest.type, uri => {
|
||||
return new Promise<Diagnostic[]>(resolve => {
|
||||
const document = documents.get(uri);
|
||||
if (document) {
|
||||
updateConfiguration();
|
||||
validateTextDocument(document, diagnostics => {
|
||||
resolve(diagnostics);
|
||||
});
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
connection.onRequest(ForceValidateRequest.type, async uri => {
|
||||
const document = documents.get(uri);
|
||||
if (document) {
|
||||
updateConfiguration();
|
||||
return await validateTextDocument(document);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
connection.onRequest(LanguageStatusRequest.type, async uri => {
|
||||
@@ -343,72 +355,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
}
|
||||
languageService.configure(languageSettings);
|
||||
|
||||
// Revalidate any open text documents
|
||||
documents.all().forEach(triggerValidation);
|
||||
diagnosticsSupport?.requestRefresh();
|
||||
}
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent((change) => {
|
||||
limitExceededWarnings.cancel(change.document.uri);
|
||||
triggerValidation(change.document);
|
||||
});
|
||||
|
||||
// a document has closed: clear all diagnostics
|
||||
documents.onDidClose(event => {
|
||||
limitExceededWarnings.cancel(event.document.uri);
|
||||
cleanPendingValidation(event.document);
|
||||
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
||||
});
|
||||
|
||||
const pendingValidationRequests: { [uri: string]: Disposable } = {};
|
||||
const validationDelayMs = 300;
|
||||
|
||||
function cleanPendingValidation(textDocument: TextDocument): void {
|
||||
const request = pendingValidationRequests[textDocument.uri];
|
||||
if (request) {
|
||||
request.dispose();
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerValidation(textDocument: TextDocument): void {
|
||||
cleanPendingValidation(textDocument);
|
||||
if (validateEnabled) {
|
||||
pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => {
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
validateTextDocument(textDocument);
|
||||
}, validationDelayMs);
|
||||
} else {
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] });
|
||||
}
|
||||
}
|
||||
|
||||
function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void {
|
||||
const respond = (diagnostics: Diagnostic[]) => {
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||
if (callback) {
|
||||
callback(diagnostics);
|
||||
}
|
||||
};
|
||||
async function validateTextDocument(textDocument: TextDocument): Promise<Diagnostic[]> {
|
||||
if (textDocument.getText().length === 0) {
|
||||
respond([]); // ignore empty documents
|
||||
return;
|
||||
return []; // ignore empty documents
|
||||
}
|
||||
const jsonDocument = getJSONDocument(textDocument);
|
||||
const version = textDocument.version;
|
||||
|
||||
const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' };
|
||||
languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => {
|
||||
runtime.timer.setImmediate(() => {
|
||||
const currDocument = documents.get(textDocument.uri);
|
||||
if (currDocument && currDocument.version === version) {
|
||||
respond(diagnostics); // Send the computed diagnostics to VSCode.
|
||||
}
|
||||
});
|
||||
}, error => {
|
||||
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error));
|
||||
});
|
||||
return await languageService.doValidation(textDocument, jsonDocument, documentSettings);
|
||||
}
|
||||
|
||||
connection.onDidChangeWatchedFiles((change) => {
|
||||
@@ -420,7 +376,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
|
||||
}
|
||||
});
|
||||
if (hasChanges) {
|
||||
documents.all().forEach(triggerValidation);
|
||||
diagnosticsSupport?.requestRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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 { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver';
|
||||
import { TextDocument } from 'vscode-json-languageservice';
|
||||
import { formatError, runSafeAsync } from './runner';
|
||||
import { RuntimeEnvironment } from '../jsonServer';
|
||||
|
||||
export type Validator = (textDocument: TextDocument) => Promise<Diagnostic[]>;
|
||||
export type DiagnosticsSupport = {
|
||||
dispose(): void;
|
||||
requestRefresh(): void;
|
||||
};
|
||||
|
||||
export function registerDiagnosticsPushSupport(documents: TextDocuments<TextDocument>, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport {
|
||||
|
||||
const pendingValidationRequests: { [uri: string]: Disposable } = {};
|
||||
const validationDelayMs = 500;
|
||||
|
||||
const disposables: Disposable[] = [];
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent(change => {
|
||||
triggerValidation(change.document);
|
||||
}, undefined, disposables);
|
||||
|
||||
// a document has closed: clear all diagnostics
|
||||
documents.onDidClose(event => {
|
||||
cleanPendingValidation(event.document);
|
||||
connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
||||
}, undefined, disposables);
|
||||
|
||||
function cleanPendingValidation(textDocument: TextDocument): void {
|
||||
const request = pendingValidationRequests[textDocument.uri];
|
||||
if (request) {
|
||||
request.dispose();
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
}
|
||||
}
|
||||
|
||||
function triggerValidation(textDocument: TextDocument): void {
|
||||
cleanPendingValidation(textDocument);
|
||||
const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => {
|
||||
if (request === pendingValidationRequests[textDocument.uri]) {
|
||||
try {
|
||||
const diagnostics = await validate(textDocument);
|
||||
if (request === pendingValidationRequests[textDocument.uri]) {
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||
}
|
||||
delete pendingValidationRequests[textDocument.uri];
|
||||
} catch (e) {
|
||||
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
|
||||
}
|
||||
}
|
||||
}, validationDelayMs);
|
||||
}
|
||||
|
||||
return {
|
||||
requestRefresh: () => {
|
||||
documents.all().forEach(triggerValidation);
|
||||
},
|
||||
dispose: () => {
|
||||
disposables.forEach(d => d.dispose());
|
||||
disposables.length = 0;
|
||||
const keys = Object.keys(pendingValidationRequests);
|
||||
for (const key of keys) {
|
||||
pendingValidationRequests[key].dispose();
|
||||
delete pendingValidationRequests[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function registerDiagnosticsPullSupport(documents: TextDocuments<TextDocument>, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport {
|
||||
|
||||
function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport {
|
||||
return {
|
||||
kind: DocumentDiagnosticReportKind.Full,
|
||||
items: diagnostics
|
||||
};
|
||||
}
|
||||
|
||||
const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => {
|
||||
return runSafeAsync(runtime, async () => {
|
||||
const document = documents.get(params.textDocument.uri);
|
||||
if (document) {
|
||||
return newDocumentDiagnosticReport(await validate(document));
|
||||
}
|
||||
return newDocumentDiagnosticReport([]);
|
||||
|
||||
}, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token);
|
||||
});
|
||||
|
||||
function requestRefresh(): void {
|
||||
connection.languages.diagnostics.refresh();
|
||||
}
|
||||
|
||||
return {
|
||||
requestRefresh,
|
||||
dispose: () => {
|
||||
registration.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -22,51 +22,51 @@ request-light@^0.5.8:
|
||||
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27"
|
||||
integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==
|
||||
|
||||
vscode-json-languageservice@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz#94b6f471ece193bf4a1ef37f6ab5cce86d50a8b4"
|
||||
integrity sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==
|
||||
vscode-json-languageservice@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.0.0.tgz#465d76cfe5dfeed4c3d5a2123b50e3f115bb7f78"
|
||||
integrity sha512-1/+1TJBRFrfCNizmrW0fbIvguKzzO+4ehlqWCCnF7ioSACUGHrYop4ANb+eRnFaCP6fi3+i+llJC5Y5yAvmL6w==
|
||||
dependencies:
|
||||
jsonc-parser "^3.0.0"
|
||||
vscode-languageserver-textdocument "^1.0.3"
|
||||
vscode-languageserver-types "^3.16.0"
|
||||
vscode-nls "^5.0.0"
|
||||
vscode-languageserver-textdocument "^1.0.4"
|
||||
vscode-languageserver-types "^3.17.1"
|
||||
vscode-nls "^5.0.1"
|
||||
vscode-uri "^3.0.3"
|
||||
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
|
||||
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
|
||||
vscode-jsonrpc@8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e"
|
||||
integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==
|
||||
|
||||
vscode-languageserver-protocol@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
|
||||
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
|
||||
vscode-languageserver-protocol@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed"
|
||||
integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==
|
||||
dependencies:
|
||||
vscode-jsonrpc "6.0.0"
|
||||
vscode-languageserver-types "3.16.0"
|
||||
vscode-jsonrpc "8.0.1"
|
||||
vscode-languageserver-types "3.17.1"
|
||||
|
||||
vscode-languageserver-textdocument@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz#879f2649bfa5a6e07bc8b392c23ede2dfbf43eff"
|
||||
integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A==
|
||||
vscode-languageserver-textdocument@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157"
|
||||
integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ==
|
||||
|
||||
vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
vscode-languageserver-types@3.17.1, vscode-languageserver-types@^3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
|
||||
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
|
||||
|
||||
vscode-languageserver@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0"
|
||||
integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==
|
||||
vscode-languageserver@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.1.tgz#56bd7a01f5c88af075a77f1d220edcb30fc4bdc7"
|
||||
integrity sha512-sn7SjBwWm3OlmLtgg7jbM0wBULppyL60rj8K5HF0ny/MzN+GzPBX1kCvYdybhl7UW63V5V5tRVnyB8iwC73lSQ==
|
||||
dependencies:
|
||||
vscode-languageserver-protocol "3.16.0"
|
||||
vscode-languageserver-protocol "3.17.1"
|
||||
|
||||
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==
|
||||
vscode-nls@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2"
|
||||
integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A==
|
||||
|
||||
vscode-uri@^3.0.3:
|
||||
version "3.0.3"
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
"@vscode/extension-telemetry@0.5.0":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.0.tgz#8214171e550393d577fc56326fa986c6800b831b"
|
||||
integrity sha512-27FsgeVJvC4zVw7Ar3Ub+7vJswDt8RoBFpbgBwf8Xq/B2gaT8G6a+gkw3s2pQmjWGIqyu7TRA8e9rS8/vxv6NQ==
|
||||
"@vscode/extension-telemetry@0.5.1":
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.1.tgz#20150976629663b3d33799a4ad25944a1535f7db"
|
||||
integrity sha512-cvFq8drxdLRF8KN72WcV4lTEa9GqDiRwy9EbnYuoSCD9Jdk8zHFF49MmACC1qs4R9Ko/C1uMOmeLJmVi8EA0rQ==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
@@ -49,44 +49,44 @@ request-light@^0.5.8:
|
||||
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27"
|
||||
integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==
|
||||
|
||||
semver@^7.3.4:
|
||||
version "7.3.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
|
||||
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
|
||||
semver@^7.3.5:
|
||||
version "7.3.7"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
|
||||
integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
|
||||
vscode-jsonrpc@8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e"
|
||||
integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==
|
||||
|
||||
vscode-languageclient@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2"
|
||||
integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==
|
||||
vscode-languageclient@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301"
|
||||
integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw==
|
||||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
semver "^7.3.4"
|
||||
vscode-languageserver-protocol "3.16.0"
|
||||
semver "^7.3.5"
|
||||
vscode-languageserver-protocol "3.17.1"
|
||||
|
||||
vscode-languageserver-protocol@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
|
||||
integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
|
||||
vscode-languageserver-protocol@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed"
|
||||
integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==
|
||||
dependencies:
|
||||
vscode-jsonrpc "6.0.0"
|
||||
vscode-languageserver-types "3.16.0"
|
||||
vscode-jsonrpc "8.0.1"
|
||||
vscode-languageserver-types "3.17.1"
|
||||
|
||||
vscode-languageserver-types@3.16.0:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
|
||||
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
|
||||
vscode-languageserver-types@3.17.1:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
|
||||
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
|
||||
|
||||
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==
|
||||
vscode-nls@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2"
|
||||
integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A==
|
||||
|
||||
yallist@^4.0.0:
|
||||
version "4.0.0"
|
||||
|
||||
@@ -9,7 +9,57 @@ import type * as MarkdownItToken from 'markdown-it/lib/token';
|
||||
import type { ActivationFunction } from 'vscode-notebook-renderer';
|
||||
|
||||
const sanitizerOptions: DOMPurify.Config = {
|
||||
ALLOWED_TAGS: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'],
|
||||
ALLOWED_TAGS: [
|
||||
'a',
|
||||
'b',
|
||||
'blockquote',
|
||||
'br',
|
||||
'button',
|
||||
'caption',
|
||||
'center',
|
||||
'code',
|
||||
'col',
|
||||
'colgroup',
|
||||
'details',
|
||||
'div',
|
||||
'em',
|
||||
'font',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'hr',
|
||||
'i',
|
||||
'img',
|
||||
'input',
|
||||
'kbd',
|
||||
'label',
|
||||
'li',
|
||||
'ol',
|
||||
'p',
|
||||
'pre',
|
||||
'select',
|
||||
'small',
|
||||
'span',
|
||||
'strong',
|
||||
'sub',
|
||||
'summary',
|
||||
'sup',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'tr',
|
||||
'tt',
|
||||
'u',
|
||||
'ul',
|
||||
'video',
|
||||
],
|
||||
};
|
||||
|
||||
export const activate: ActivationFunction<void> = (ctx) => {
|
||||
@@ -207,7 +257,9 @@ export const activate: ActivationFunction<void> = (ctx) => {
|
||||
previewNode.classList.remove('emptyMarkdownCell');
|
||||
const markdownText = outputInfo.mime.startsWith('text/x-') ? `\`\`\`${outputInfo.mime.substr(7)}\n${text}\n\`\`\``
|
||||
: (outputInfo.mime.startsWith('application/') ? `\`\`\`${outputInfo.mime.substr(12)}\n${text}\n\`\`\`` : text);
|
||||
const unsanitizedRenderedMarkdown = markdownIt.render(markdownText);
|
||||
const unsanitizedRenderedMarkdown = markdownIt.render(markdownText, {
|
||||
outputItem: outputInfo,
|
||||
});
|
||||
previewNode.innerHTML = (ctx.workspace.isTrusted
|
||||
? unsanitizedRenderedMarkdown
|
||||
: DOMPurify.sanitize(unsanitizedRenderedMarkdown, sanitizerOptions)) as string;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"Programming Languages"
|
||||
],
|
||||
"enabledApiProposals": [
|
||||
"textEditorDrop"
|
||||
"textEditorDrop",
|
||||
"documentPaste"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:markdown",
|
||||
@@ -414,16 +415,22 @@
|
||||
"markdownDescription": "%configuration.markdown.editor.drop.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"markdown.experimental.editor.pasteLinks.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.markdown.editor.pasteLinks.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"markdown.experimental.validate.enabled": {
|
||||
"type": "boolean",
|
||||
"scope": "resource",
|
||||
"description": "%configuration.markdown.experimental.validate.enabled.description%",
|
||||
"default": false
|
||||
},
|
||||
"markdown.experimental.validate.referenceLinks": {
|
||||
"markdown.experimental.validate.referenceLinks.enabled": {
|
||||
"type": "string",
|
||||
"scope": "resource",
|
||||
"markdownDescription": "%configuration.markdown.experimental.validate.referenceLinks.description%",
|
||||
"markdownDescription": "%configuration.markdown.experimental.validate.referenceLinks.enabled.description%",
|
||||
"default": "warning",
|
||||
"enum": [
|
||||
"ignore",
|
||||
@@ -431,10 +438,10 @@
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"markdown.experimental.validate.headerLinks": {
|
||||
"markdown.experimental.validate.headerLinks.enabled": {
|
||||
"type": "string",
|
||||
"scope": "resource",
|
||||
"markdownDescription": "%configuration.markdown.experimental.validate.headerLinks.description%",
|
||||
"markdownDescription": "%configuration.markdown.experimental.validate.headerLinks.enabled.description%",
|
||||
"default": "warning",
|
||||
"enum": [
|
||||
"ignore",
|
||||
@@ -442,16 +449,24 @@
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"markdown.experimental.validate.fileLinks": {
|
||||
"markdown.experimental.validate.fileLinks.enabled": {
|
||||
"type": "string",
|
||||
"scope": "resource",
|
||||
"markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.description%",
|
||||
"markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.enabled.description%",
|
||||
"default": "warning",
|
||||
"enum": [
|
||||
"ignore",
|
||||
"warning",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"markdown.experimental.validate.ignoreLinks": {
|
||||
"type": "array",
|
||||
"scope": "resource",
|
||||
"markdownDescription": "%configuration.markdown.experimental.validate.ignoreLinks.description%",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -504,6 +519,7 @@
|
||||
"markdown-it": "^12.3.2",
|
||||
"markdown-it-front-matter": "^0.2.1",
|
||||
"morphdom": "^2.6.1",
|
||||
"picomatch": "^2.3.1",
|
||||
"vscode-languageserver-textdocument": "^1.0.4",
|
||||
"vscode-nls": "^5.0.0",
|
||||
"vscode-uri": "^3.0.3"
|
||||
@@ -512,6 +528,7 @@
|
||||
"@types/dompurify": "^2.3.1",
|
||||
"@types/lodash.throttle": "^4.1.3",
|
||||
"@types/markdown-it": "12.2.3",
|
||||
"@types/picomatch": "^2.3.0",
|
||||
"@types/vscode-notebook-renderer": "^1.60.0",
|
||||
"@types/vscode-webview": "^1.57.0",
|
||||
"lodash.throttle": "^4.1.1"
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
"configuration.markdown.links.openLocation.beside": "Open links beside the active editor.",
|
||||
"configuration.markdown.suggest.paths.enabled.description": "Enable/disable path suggestions for markdown links",
|
||||
"configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbenck.experimental.editor.dropIntoEditor.enabled#`.",
|
||||
"configuration.markdown.editor.pasteLinks.enabled": "Enable/disable pasting files into a Markdown editor inserts Markdown links.",
|
||||
"configuration.markdown.experimental.validate.enabled.description": "Enable/disable all error reporting in Markdown files.",
|
||||
"configuration.markdown.experimental.validate.referenceLinks.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.",
|
||||
"configuration.markdown.experimental.validate.headerLinks.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.",
|
||||
"configuration.markdown.experimental.validate.fileLinks.description": "Validate links to other files in Markdown files, e.g. `[link](/path/to/file.md)`. This checks that the target files exists. Requires enabling `#markdown.experimental.validate.enabled#`.",
|
||||
"configuration.markdown.experimental.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.",
|
||||
"configuration.markdown.experimental.validate.headerLinks.enabled.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.",
|
||||
"configuration.markdown.experimental.validate.fileLinks.enabled.description": "Validate links to other files in Markdown files, e.g. `[link](/path/to/file.md)`. This checks that the target files exists. Requires enabling `#markdown.experimental.validate.enabled#`.",
|
||||
"configuration.markdown.experimental.validate.ignoreLinks.description": "Configure links that should not be validated. For example `/about` would not validate the link `[about](/about)`, while the glob `/assets/**/*.svg` would let you skip validation for any link to `.svg` files under the `assets` directory.",
|
||||
"workspaceTrust": "Required for loading styles configured in the workspace."
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { CommandManager } from './commandManager';
|
||||
import * as commands from './commands/index';
|
||||
import { register as registerDiagnostics } from './languageFeatures/diagnostics';
|
||||
import { registerPasteProvider } from './languageFeatures/copyPaste';
|
||||
import { MdDefinitionProvider } from './languageFeatures/definitionProvider';
|
||||
import { register as registerDiagnostics } from './languageFeatures/diagnostics';
|
||||
import { MdLinkProvider } from './languageFeatures/documentLinkProvider';
|
||||
import { MdDocumentSymbolProvider } from './languageFeatures/documentSymbolProvider';
|
||||
import { registerDropIntoEditor } from './languageFeatures/dropIntoEditor';
|
||||
@@ -76,8 +77,9 @@ function registerMarkdownLanguageFeatures(
|
||||
vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier)),
|
||||
vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)),
|
||||
MdPathCompletionProvider.register(selector, engine, linkProvider),
|
||||
registerDiagnostics(engine, workspaceContents, linkProvider),
|
||||
registerDiagnostics(selector, engine, workspaceContents, linkProvider, commandManager),
|
||||
registerDropIntoEditor(selector),
|
||||
registerPasteProvider(selector),
|
||||
registerFindFileReferences(commandManager, referencesProvider),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { tryInsertUriList } from './dropIntoEditor';
|
||||
|
||||
export function registerPasteProvider(selector: vscode.DocumentSelector) {
|
||||
return vscode.languages.registerDocumentPasteEditProvider(selector, new class implements vscode.DocumentPasteEditProvider {
|
||||
|
||||
async provideDocumentPasteEdits(
|
||||
document: vscode.TextDocument,
|
||||
range: vscode.Range,
|
||||
dataTransfer: vscode.DataTransfer,
|
||||
token: vscode.CancellationToken,
|
||||
): Promise<vscode.SnippetTextEdit | undefined> {
|
||||
const enabled = vscode.workspace.getConfiguration('markdown', document).get('experimental.editor.pasteLinks.enabled', false);
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
return tryInsertUriList(document, range, dataTransfer, token);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as picomatch from 'picomatch';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
import { TableOfContents } from '../tableOfContents';
|
||||
import { Delayer } from '../util/async';
|
||||
@@ -12,8 +13,9 @@ import { Disposable } from '../util/dispose';
|
||||
import { isMarkdownFile } from '../util/file';
|
||||
import { Limiter } from '../util/limiter';
|
||||
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
|
||||
import { LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider';
|
||||
import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider';
|
||||
import { tryFindMdDocumentForLink } from './references';
|
||||
import { CommandManager } from '../commandManager';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -37,6 +39,7 @@ export interface DiagnosticOptions {
|
||||
readonly validateReferences: DiagnosticLevel;
|
||||
readonly validateOwnHeaders: DiagnosticLevel;
|
||||
readonly validateFilePaths: DiagnosticLevel;
|
||||
readonly ignoreLinks: readonly string[];
|
||||
}
|
||||
|
||||
function toSeverity(level: DiagnosticLevel): vscode.DiagnosticSeverity | undefined {
|
||||
@@ -56,7 +59,13 @@ class VSCodeDiagnosticConfiguration extends Disposable implements DiagnosticConf
|
||||
super();
|
||||
|
||||
this._register(vscode.workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('markdown.experimental.validate.enabled')) {
|
||||
if (
|
||||
e.affectsConfiguration('markdown.experimental.validate.enabled')
|
||||
|| e.affectsConfiguration('markdown.experimental.validate.referenceLinks.enabled')
|
||||
|| e.affectsConfiguration('markdown.experimental.validate.headerLinks.enabled')
|
||||
|| e.affectsConfiguration('markdown.experimental.validate.fileLinks.enabled')
|
||||
|| e.affectsConfiguration('markdown.experimental.validate.ignoreLinks')
|
||||
) {
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
}));
|
||||
@@ -66,9 +75,10 @@ class VSCodeDiagnosticConfiguration extends Disposable implements DiagnosticConf
|
||||
const config = vscode.workspace.getConfiguration('markdown', resource);
|
||||
return {
|
||||
enabled: config.get<boolean>('experimental.validate.enabled', false),
|
||||
validateReferences: config.get<DiagnosticLevel>('experimental.validate.referenceLinks', DiagnosticLevel.ignore),
|
||||
validateOwnHeaders: config.get<DiagnosticLevel>('experimental.validate.headerLinks', DiagnosticLevel.ignore),
|
||||
validateFilePaths: config.get<DiagnosticLevel>('experimental.validate.fileLinks', DiagnosticLevel.ignore),
|
||||
validateReferences: config.get<DiagnosticLevel>('experimental.validate.referenceLinks.enabled', DiagnosticLevel.ignore),
|
||||
validateOwnHeaders: config.get<DiagnosticLevel>('experimental.validate.headerLinks.enabled', DiagnosticLevel.ignore),
|
||||
validateFilePaths: config.get<DiagnosticLevel>('experimental.validate.fileLinks.enabled', DiagnosticLevel.ignore),
|
||||
ignoreLinks: config.get('experimental.validate.ignoreLinks', []),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -118,6 +128,104 @@ class InflightDiagnosticRequests {
|
||||
}
|
||||
}
|
||||
|
||||
class LinkWatcher extends Disposable {
|
||||
|
||||
private readonly _onDidChangeLinkedToFile = this._register(new vscode.EventEmitter<Iterable<vscode.Uri>>);
|
||||
/**
|
||||
* Event fired with a list of document uri when one of the links in the document changes
|
||||
*/
|
||||
public readonly onDidChangeLinkedToFile = this._onDidChangeLinkedToFile.event;
|
||||
|
||||
private readonly _watchers = new Map</* link path */ string, {
|
||||
/**
|
||||
* Watcher for this link path
|
||||
*/
|
||||
readonly watcher: vscode.Disposable;
|
||||
|
||||
/**
|
||||
* List of documents that reference the link
|
||||
*/
|
||||
readonly documents: Map</* document resource as string */ string, /* document resource*/ vscode.Uri>;
|
||||
}>();
|
||||
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
|
||||
for (const entry of this._watchers.values()) {
|
||||
entry.watcher.dispose();
|
||||
}
|
||||
this._watchers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the known links in a markdown document, adding and removing file watchers as needed
|
||||
*/
|
||||
updateLinksForDocument(document: vscode.Uri, links: readonly MdLink[]) {
|
||||
const linkedToResource = new Set<vscode.Uri>(
|
||||
links
|
||||
.filter(link => link.href.kind === 'internal')
|
||||
.map(link => (link.href as InternalHref).path));
|
||||
|
||||
// First decrement watcher counter for previous document state
|
||||
for (const entry of this._watchers.values()) {
|
||||
entry.documents.delete(document.toString());
|
||||
}
|
||||
|
||||
// Then create/update watchers for new document state
|
||||
for (const path of linkedToResource) {
|
||||
let entry = this._watchers.get(path.toString());
|
||||
if (!entry) {
|
||||
entry = {
|
||||
watcher: this.startWatching(path),
|
||||
documents: new Map(),
|
||||
};
|
||||
this._watchers.set(path.toString(), entry);
|
||||
}
|
||||
|
||||
entry.documents.set(document.toString(), document);
|
||||
}
|
||||
|
||||
// Finally clean up watchers for links that are no longer are referenced anywhere
|
||||
for (const [key, value] of this._watchers) {
|
||||
if (value.documents.size === 0) {
|
||||
value.watcher.dispose();
|
||||
this._watchers.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleteDocument(resource: vscode.Uri) {
|
||||
this.updateLinksForDocument(resource, []);
|
||||
}
|
||||
|
||||
private startWatching(path: vscode.Uri): vscode.Disposable {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(path, '*'), false, true, false);
|
||||
const handler = (resource: vscode.Uri) => this.onLinkedResourceChanged(resource);
|
||||
return vscode.Disposable.from(
|
||||
watcher,
|
||||
watcher.onDidDelete(handler),
|
||||
watcher.onDidCreate(handler),
|
||||
);
|
||||
}
|
||||
|
||||
private onLinkedResourceChanged(resource: vscode.Uri) {
|
||||
const entry = this._watchers.get(resource.toString());
|
||||
if (entry) {
|
||||
this._onDidChangeLinkedToFile.fire(entry.documents.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LinkDoesNotExistDiagnostic extends vscode.Diagnostic {
|
||||
|
||||
public readonly link: string;
|
||||
|
||||
constructor(range: vscode.Range, message: string, severity: vscode.DiagnosticSeverity, link: string) {
|
||||
super(range, message, severity);
|
||||
this.link = link;
|
||||
}
|
||||
}
|
||||
|
||||
export class DiagnosticManager extends Disposable {
|
||||
|
||||
private readonly collection: vscode.DiagnosticCollection;
|
||||
@@ -126,6 +234,8 @@ export class DiagnosticManager extends Disposable {
|
||||
private readonly pendingDiagnostics = new Set<vscode.Uri>();
|
||||
private readonly inFlightDiagnostics = this._register(new InflightDiagnosticRequests());
|
||||
|
||||
private readonly linkWatcher = this._register(new LinkWatcher());
|
||||
|
||||
constructor(
|
||||
private readonly computer: DiagnosticComputer,
|
||||
private readonly configuration: DiagnosticConfiguration,
|
||||
@@ -148,10 +258,20 @@ export class DiagnosticManager extends Disposable {
|
||||
this.triggerDiagnostics(e.document);
|
||||
}));
|
||||
|
||||
this._register(vscode.workspace.onDidCloseTextDocument(doc => {
|
||||
this.pendingDiagnostics.delete(doc.uri);
|
||||
this.inFlightDiagnostics.cancel(doc.uri);
|
||||
this.collection.delete(doc.uri);
|
||||
this._register(vscode.workspace.onDidCloseTextDocument(({ uri }) => {
|
||||
this.pendingDiagnostics.delete(uri);
|
||||
this.inFlightDiagnostics.cancel(uri);
|
||||
this.linkWatcher.deleteDocument(uri);
|
||||
this.collection.delete(uri);
|
||||
}));
|
||||
|
||||
this._register(this.linkWatcher.onDidChangeLinkedToFile(changedDocuments => {
|
||||
for (const resource of changedDocuments) {
|
||||
const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === resource.toString());
|
||||
if (doc) {
|
||||
this.triggerDiagnostics(doc);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.rebuild();
|
||||
@@ -162,12 +282,12 @@ export class DiagnosticManager extends Disposable {
|
||||
this.pendingDiagnostics.clear();
|
||||
}
|
||||
|
||||
public async getDiagnostics(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise<vscode.Diagnostic[]> {
|
||||
public async recomputeDiagnosticState(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise<{ diagnostics: readonly vscode.Diagnostic[]; links: readonly MdLink[]; config: DiagnosticOptions }> {
|
||||
const config = this.configuration.getOptions(doc.uri);
|
||||
if (!config.enabled) {
|
||||
return [];
|
||||
return { diagnostics: [], links: [], config };
|
||||
}
|
||||
return this.computer.getDiagnostics(doc, config, token);
|
||||
return { ...await this.computer.getDiagnostics(doc, config, token), config };
|
||||
}
|
||||
|
||||
private async recomputePendingDiagnostics(): Promise<void> {
|
||||
@@ -178,8 +298,9 @@ export class DiagnosticManager extends Disposable {
|
||||
const doc = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === resource.fsPath);
|
||||
if (doc) {
|
||||
this.inFlightDiagnostics.trigger(doc.uri, async (token) => {
|
||||
const diagnostics = await this.getDiagnostics(doc, token);
|
||||
this.collection.set(doc.uri, diagnostics);
|
||||
const state = await this.recomputeDiagnosticState(doc, token);
|
||||
this.linkWatcher.updateLinksForDocument(doc.uri, state.config.enabled && state.config.validateFilePaths ? state.links : []);
|
||||
this.collection.set(doc.uri, state.diagnostics);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -269,17 +390,20 @@ export class DiagnosticComputer {
|
||||
private readonly linkProvider: MdLinkProvider,
|
||||
) { }
|
||||
|
||||
public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<vscode.Diagnostic[]> {
|
||||
public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: MdLink[] }> {
|
||||
const links = await this.linkProvider.getAllLinks(doc, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
return { links, diagnostics: [] };
|
||||
}
|
||||
|
||||
return (await Promise.all([
|
||||
this.validateFileLinks(doc, options, links, token),
|
||||
Array.from(this.validateReferenceLinks(options, links)),
|
||||
this.validateOwnHeaderLinks(doc, options, links, token),
|
||||
])).flat();
|
||||
return {
|
||||
links,
|
||||
diagnostics: (await Promise.all([
|
||||
this.validateFileLinks(doc, options, links, token),
|
||||
Array.from(this.validateReferenceLinks(options, links)),
|
||||
this.validateOwnHeaderLinks(doc, options, links, token),
|
||||
])).flat()
|
||||
};
|
||||
}
|
||||
|
||||
private async validateOwnHeaderLinks(doc: SkinnyTextDocument, options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise<vscode.Diagnostic[]> {
|
||||
@@ -300,10 +424,13 @@ export class DiagnosticComputer {
|
||||
&& link.href.fragment
|
||||
&& !toc.lookup(link.href.fragment)
|
||||
) {
|
||||
diagnostics.push(new vscode.Diagnostic(
|
||||
link.source.hrefRange,
|
||||
localize('invalidHeaderLink', 'No header found: \'{0}\'', link.href.fragment),
|
||||
severity));
|
||||
if (!this.isIgnoredLink(options, link.source.text)) {
|
||||
diagnostics.push(new LinkDoesNotExistDiagnostic(
|
||||
link.source.hrefRange,
|
||||
localize('invalidHeaderLink', 'No header found: \'{0}\'', link.href.fragment),
|
||||
severity,
|
||||
link.source.text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,9 +482,11 @@ export class DiagnosticComputer {
|
||||
}
|
||||
|
||||
if (!hrefDoc && !await this.workspaceContents.pathExists(path)) {
|
||||
const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.toString(true));
|
||||
const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.fsPath);
|
||||
for (const link of links) {
|
||||
diagnostics.push(new vscode.Diagnostic(link.source.hrefRange, msg, severity));
|
||||
if (!this.isIgnoredLink(options, link.source.pathText)) {
|
||||
diagnostics.push(new LinkDoesNotExistDiagnostic(link.source.hrefRange, msg, severity, link.source.pathText));
|
||||
}
|
||||
}
|
||||
} else if (hrefDoc) {
|
||||
// Validate each of the links to headers in the file
|
||||
@@ -365,9 +494,9 @@ export class DiagnosticComputer {
|
||||
if (fragmentLinks.length) {
|
||||
const toc = await TableOfContents.create(this.engine, hrefDoc);
|
||||
for (const link of fragmentLinks) {
|
||||
if (!toc.lookup(link.fragment)) {
|
||||
if (!toc.lookup(link.fragment) && !this.isIgnoredLink(options, link.source.pathText) && !this.isIgnoredLink(options, link.source.text)) {
|
||||
const msg = localize('invalidLinkToHeaderInOtherFile', 'Header does not exist in file: {0}', link.fragment);
|
||||
diagnostics.push(new vscode.Diagnostic(link.source.hrefRange, msg, severity));
|
||||
diagnostics.push(new LinkDoesNotExistDiagnostic(link.source.hrefRange, msg, severity, link.source.text));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,14 +505,70 @@ export class DiagnosticComputer {
|
||||
}));
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
private isIgnoredLink(options: DiagnosticOptions, link: string): boolean {
|
||||
return options.ignoreLinks.some(glob => picomatch.isMatch(link, glob));
|
||||
}
|
||||
}
|
||||
|
||||
class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider {
|
||||
|
||||
private static readonly _addToIgnoreLinksCommandId = '_markdown.addToIgnoreLinks';
|
||||
|
||||
private static readonly metadata: vscode.CodeActionProviderMetadata = {
|
||||
providedCodeActionKinds: [
|
||||
vscode.CodeActionKind.QuickFix
|
||||
],
|
||||
};
|
||||
|
||||
public static register(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable {
|
||||
const reg = vscode.languages.registerCodeActionsProvider(selector, new AddToIgnoreLinksQuickFixProvider(), AddToIgnoreLinksQuickFixProvider.metadata);
|
||||
const commandReg = commandManager.register({
|
||||
id: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId,
|
||||
execute(resource: vscode.Uri, path: string) {
|
||||
const settingId = 'experimental.validate.ignoreLinks';
|
||||
const config = vscode.workspace.getConfiguration('markdown', resource);
|
||||
const paths = new Set(config.get<string[]>(settingId, []));
|
||||
paths.add(path);
|
||||
config.update(settingId, [...paths], vscode.ConfigurationTarget.WorkspaceFolder);
|
||||
}
|
||||
});
|
||||
return vscode.Disposable.from(reg, commandReg);
|
||||
}
|
||||
|
||||
provideCodeActions(document: vscode.TextDocument, _range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, _token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
|
||||
const fixes: vscode.CodeAction[] = [];
|
||||
|
||||
for (const diagnostic of context.diagnostics) {
|
||||
if (diagnostic instanceof LinkDoesNotExistDiagnostic) {
|
||||
const fix = new vscode.CodeAction(
|
||||
localize('ignoreLinksQuickFix.title', "Exclude '{0}' from link validation.", diagnostic.link),
|
||||
vscode.CodeActionKind.QuickFix);
|
||||
|
||||
fix.command = {
|
||||
command: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId,
|
||||
title: '',
|
||||
arguments: [document.uri, diagnostic.link]
|
||||
};
|
||||
fixes.push(fix);
|
||||
}
|
||||
}
|
||||
|
||||
return fixes;
|
||||
}
|
||||
}
|
||||
|
||||
export function register(
|
||||
selector: vscode.DocumentSelector,
|
||||
engine: MarkdownEngine,
|
||||
workspaceContents: MdWorkspaceContents,
|
||||
linkProvider: MdLinkProvider,
|
||||
commandManager: CommandManager,
|
||||
): vscode.Disposable {
|
||||
const configuration = new VSCodeDiagnosticConfiguration();
|
||||
const manager = new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkProvider), configuration);
|
||||
return vscode.Disposable.from(configuration, manager);
|
||||
return vscode.Disposable.from(
|
||||
configuration,
|
||||
manager,
|
||||
AddToIgnoreLinksQuickFixProvider.register(selector, commandManager));
|
||||
}
|
||||
|
||||
+66
-45
@@ -93,7 +93,16 @@ function getWorkspaceFolder(document: SkinnyTextDocument) {
|
||||
}
|
||||
|
||||
export interface MdLinkSource {
|
||||
/**
|
||||
* The original text of the link destination in code.
|
||||
*/
|
||||
readonly text: string;
|
||||
|
||||
/**
|
||||
* The original text of just the link's path in code.
|
||||
*/
|
||||
readonly pathText: string;
|
||||
|
||||
readonly resource: vscode.Uri;
|
||||
readonly hrefRange: vscode.Range;
|
||||
readonly fragmentRange: vscode.Range | undefined;
|
||||
@@ -138,7 +147,7 @@ function extractDocumentLink(
|
||||
text: link,
|
||||
resource: document.uri,
|
||||
hrefRange: new vscode.Range(linkStart, linkEnd),
|
||||
fragmentRange: getFragmentRange(link, linkStart, linkEnd),
|
||||
...getLinkSourceFragmentInfo(document, link, linkStart, linkEnd),
|
||||
}
|
||||
};
|
||||
} catch {
|
||||
@@ -154,6 +163,14 @@ function getFragmentRange(text: string, start: vscode.Position, end: vscode.Posi
|
||||
return new vscode.Range(start.translate({ characterDelta: index + 1 }), end);
|
||||
}
|
||||
|
||||
function getLinkSourceFragmentInfo(document: SkinnyTextDocument, link: string, linkStart: vscode.Position, linkEnd: vscode.Position): { fragmentRange: vscode.Range | undefined; pathText: string } {
|
||||
const fragmentRange = getFragmentRange(link, linkStart, linkEnd);
|
||||
return {
|
||||
pathText: document.getText(new vscode.Range(linkStart, fragmentRange ? fragmentRange.start.translate(0, -1) : linkEnd)),
|
||||
fragmentRange,
|
||||
};
|
||||
}
|
||||
|
||||
const angleBracketLinkRe = /^<(.*)>$/;
|
||||
|
||||
/**
|
||||
@@ -187,35 +204,38 @@ const definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)([^<]\S*|<[^>
|
||||
|
||||
const inlineCodePattern = /(?:^|[^`])(`+)(?:.+?|.*?(?:(?:\r?\n).+?)*?)(?:\r?\n)?\1(?:$|[^`])/gm;
|
||||
|
||||
interface CodeInDocument {
|
||||
/**
|
||||
* code blocks and fences each represented by [line_start,line_end).
|
||||
*/
|
||||
readonly multiline: ReadonlyArray<[number, number]>;
|
||||
class NoLinkRanges {
|
||||
public static async compute(document: SkinnyTextDocument, engine: MarkdownEngine): Promise<NoLinkRanges> {
|
||||
const tokens = await engine.parse(document);
|
||||
const multiline = tokens.filter(t => (t.type === 'code_block' || t.type === 'fence' || t.type === 'html_block') && !!t.map).map(t => t.map) as [number, number][];
|
||||
|
||||
/**
|
||||
* inline code spans each represented by {@link vscode.Range}.
|
||||
*/
|
||||
readonly inline: readonly vscode.Range[];
|
||||
const text = document.getText();
|
||||
const inline = [...text.matchAll(inlineCodePattern)].map(match => {
|
||||
const start = match.index || 0;
|
||||
return new vscode.Range(document.positionAt(start), document.positionAt(start + match[0].length));
|
||||
});
|
||||
|
||||
return new NoLinkRanges(multiline, inline);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
/**
|
||||
* code blocks and fences each represented by [line_start,line_end).
|
||||
*/
|
||||
public readonly multiline: ReadonlyArray<[number, number]>,
|
||||
|
||||
/**
|
||||
* Inline code spans where links should not be detected
|
||||
*/
|
||||
public readonly inline: readonly vscode.Range[]
|
||||
) { }
|
||||
|
||||
contains(range: vscode.Range): boolean {
|
||||
return this.multiline.some(interval => range.start.line >= interval[0] && range.start.line < interval[1]) ||
|
||||
this.inline.some(position => position.intersection(range));
|
||||
}
|
||||
}
|
||||
|
||||
async function findCode(document: SkinnyTextDocument, engine: MarkdownEngine): Promise<CodeInDocument> {
|
||||
const tokens = await engine.parse(document);
|
||||
const multiline = tokens.filter(t => (t.type === 'code_block' || t.type === 'fence') && !!t.map).map(t => t.map) as [number, number][];
|
||||
|
||||
const text = document.getText();
|
||||
const inline = [...text.matchAll(inlineCodePattern)].map(match => {
|
||||
const start = match.index || 0;
|
||||
return new vscode.Range(document.positionAt(start), document.positionAt(start + match[0].length));
|
||||
});
|
||||
|
||||
return { multiline, inline };
|
||||
}
|
||||
|
||||
function isLinkInsideCode(code: CodeInDocument, linkHrefRange: vscode.Range) {
|
||||
return code.multiline.some(interval => linkHrefRange.start.line >= interval[0] && linkHrefRange.start.line < interval[1]) ||
|
||||
code.inline.some(position => position.intersection(linkHrefRange));
|
||||
}
|
||||
|
||||
export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
|
||||
@@ -262,35 +282,35 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
}
|
||||
|
||||
public async getAllLinks(document: SkinnyTextDocument, token: vscode.CancellationToken): Promise<MdLink[]> {
|
||||
const codeInDocument = await findCode(document, this.engine);
|
||||
const noLinkRanges = await NoLinkRanges.compute(document, this.engine);
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from([
|
||||
...this.getInlineLinks(document, codeInDocument),
|
||||
...this.getReferenceLinks(document, codeInDocument),
|
||||
...this.getLinkDefinitions2(document, codeInDocument),
|
||||
...this.getAutoLinks(document, codeInDocument),
|
||||
...this.getInlineLinks(document, noLinkRanges),
|
||||
...this.getReferenceLinks(document, noLinkRanges),
|
||||
...this.getLinkDefinitions2(document, noLinkRanges),
|
||||
...this.getAutoLinks(document, noLinkRanges),
|
||||
]);
|
||||
}
|
||||
|
||||
private *getInlineLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLink> {
|
||||
private *getInlineLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable<MdLink> {
|
||||
const text = document.getText();
|
||||
|
||||
for (const match of text.matchAll(linkPattern)) {
|
||||
const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index);
|
||||
if (matchImageData && !isLinkInsideCode(codeInDocument, matchImageData.source.hrefRange)) {
|
||||
if (matchImageData && !noLinkRanges.contains(matchImageData.source.hrefRange)) {
|
||||
yield matchImageData;
|
||||
}
|
||||
const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index);
|
||||
if (matchLinkData && !isLinkInsideCode(codeInDocument, matchLinkData.source.hrefRange)) {
|
||||
if (matchLinkData && !noLinkRanges.contains(matchLinkData.source.hrefRange)) {
|
||||
yield matchLinkData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private *getAutoLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLink> {
|
||||
private *getAutoLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable<MdLink> {
|
||||
const text = document.getText();
|
||||
|
||||
for (const match of text.matchAll(autoLinkPattern)) {
|
||||
@@ -301,7 +321,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
const linkStart = document.positionAt(offset);
|
||||
const linkEnd = document.positionAt(offset + link.length);
|
||||
const hrefRange = new vscode.Range(linkStart, linkEnd);
|
||||
if (isLinkInsideCode(codeInDocument, hrefRange)) {
|
||||
if (noLinkRanges.contains(hrefRange)) {
|
||||
continue;
|
||||
}
|
||||
yield {
|
||||
@@ -311,14 +331,14 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
text: link,
|
||||
resource: document.uri,
|
||||
hrefRange: new vscode.Range(linkStart, linkEnd),
|
||||
fragmentRange: getFragmentRange(link, linkStart, linkEnd),
|
||||
...getLinkSourceFragmentInfo(document, link, linkStart, linkEnd),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private *getReferenceLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLink> {
|
||||
private *getReferenceLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable<MdLink> {
|
||||
const text = document.getText();
|
||||
for (const match of text.matchAll(referenceLinkPattern)) {
|
||||
let linkStart: vscode.Position;
|
||||
@@ -339,7 +359,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
}
|
||||
|
||||
const hrefRange = new vscode.Range(linkStart, linkEnd);
|
||||
if (isLinkInsideCode(codeInDocument, hrefRange)) {
|
||||
if (noLinkRanges.contains(hrefRange)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -347,6 +367,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
kind: 'link',
|
||||
source: {
|
||||
text: reference,
|
||||
pathText: reference,
|
||||
resource: document.uri,
|
||||
hrefRange,
|
||||
fragmentRange: undefined,
|
||||
@@ -360,11 +381,11 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
}
|
||||
|
||||
public async getLinkDefinitions(document: SkinnyTextDocument): Promise<Iterable<MdLinkDefinition>> {
|
||||
const codeInDocument = await findCode(document, this.engine);
|
||||
return this.getLinkDefinitions2(document, codeInDocument);
|
||||
const noLinkRanges = await NoLinkRanges.compute(document, this.engine);
|
||||
return this.getLinkDefinitions2(document, noLinkRanges);
|
||||
}
|
||||
|
||||
private *getLinkDefinitions2(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable<MdLinkDefinition> {
|
||||
private *getLinkDefinitions2(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable<MdLinkDefinition> {
|
||||
const text = document.getText();
|
||||
for (const match of text.matchAll(definitionPattern)) {
|
||||
const pre = match[1];
|
||||
@@ -388,7 +409,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
text = link;
|
||||
}
|
||||
const hrefRange = new vscode.Range(linkStart, linkEnd);
|
||||
if (isLinkInsideCode(codeInDocument, hrefRange)) {
|
||||
if (noLinkRanges.contains(hrefRange)) {
|
||||
continue;
|
||||
}
|
||||
const target = parseLink(document, text);
|
||||
@@ -399,7 +420,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
text: link,
|
||||
resource: document.uri,
|
||||
hrefRange,
|
||||
fragmentRange: getFragmentRange(link, linkStart, linkEnd),
|
||||
...getLinkSourceFragmentInfo(document, link, linkStart, linkEnd),
|
||||
},
|
||||
ref: { text: reference, range: refRange },
|
||||
href: target,
|
||||
|
||||
@@ -24,7 +24,7 @@ const imageFileExtensions = new Set<string>([
|
||||
]);
|
||||
|
||||
export function registerDropIntoEditor(selector: vscode.DocumentSelector) {
|
||||
return vscode.languages.registerDocumentOnDropProvider(selector, new class implements vscode.DocumentOnDropProvider {
|
||||
return vscode.languages.registerDocumentOnDropEditProvider(selector, new class implements vscode.DocumentOnDropEditProvider {
|
||||
async provideDocumentOnDropEdits(document: vscode.TextDocument, position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.SnippetTextEdit | undefined> {
|
||||
const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true);
|
||||
if (!enabled) {
|
||||
@@ -32,45 +32,45 @@ export function registerDropIntoEditor(selector: vscode.DocumentSelector) {
|
||||
}
|
||||
|
||||
const replacementRange = new vscode.Range(position, position);
|
||||
return this.tryInsertUriList(document, replacementRange, dataTransfer, token);
|
||||
}
|
||||
|
||||
private async tryInsertUriList(document: vscode.TextDocument, replacementRange: vscode.Range, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.SnippetTextEdit | undefined> {
|
||||
const urlList = await dataTransfer.get('text/uri-list')?.asString();
|
||||
if (!urlList || token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const uris: vscode.Uri[] = [];
|
||||
for (const resource of urlList.split('\n')) {
|
||||
try {
|
||||
uris.push(vscode.Uri.parse(resource));
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
if (!uris.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const snippet = new vscode.SnippetString();
|
||||
uris.forEach((uri, i) => {
|
||||
const mdPath = document.uri.scheme === uri.scheme
|
||||
? encodeURI(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath).replace(/\\/g, '/'))
|
||||
: uri.toString(false);
|
||||
|
||||
const ext = URI.Utils.extname(uri).toLowerCase();
|
||||
snippet.appendText(imageFileExtensions.has(ext) ? '`);
|
||||
|
||||
if (i <= uris.length - 1 && uris.length > 1) {
|
||||
snippet.appendText(' ');
|
||||
}
|
||||
});
|
||||
|
||||
return new vscode.SnippetTextEdit(replacementRange, snippet);
|
||||
return tryInsertUriList(document, replacementRange, dataTransfer, token);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function tryInsertUriList(document: vscode.TextDocument, replacementRange: vscode.Range, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.SnippetTextEdit | undefined> {
|
||||
const urlList = await dataTransfer.get('text/uri-list')?.asString();
|
||||
if (!urlList || token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const uris: vscode.Uri[] = [];
|
||||
for (const resource of urlList.split('\n')) {
|
||||
try {
|
||||
uris.push(vscode.Uri.parse(resource));
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
if (!uris.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const snippet = new vscode.SnippetString();
|
||||
uris.forEach((uri, i) => {
|
||||
const mdPath = document.uri.scheme === uri.scheme
|
||||
? encodeURI(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath).replace(/\\/g, '/'))
|
||||
: uri.toString(false);
|
||||
|
||||
const ext = URI.Utils.extname(uri).toLowerCase();
|
||||
snippet.appendText(imageFileExtensions.has(ext) ? '`);
|
||||
|
||||
if (i <= uris.length - 1 && uris.length > 1) {
|
||||
snippet.appendText(' ');
|
||||
}
|
||||
});
|
||||
|
||||
return new vscode.SnippetTextEdit(replacementRange, snippet);
|
||||
}
|
||||
|
||||
@@ -109,6 +109,8 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
||||
private readonly _onScrollEmitter = this._register(new vscode.EventEmitter<LastScrollLocation>());
|
||||
public readonly onScroll = this._onScrollEmitter.event;
|
||||
|
||||
private readonly _disposeCts = this._register(new vscode.CancellationTokenSource());
|
||||
|
||||
constructor(
|
||||
webview: vscode.WebviewPanel,
|
||||
resource: vscode.Uri,
|
||||
@@ -202,6 +204,8 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
this._disposeCts.cancel();
|
||||
|
||||
super.dispose();
|
||||
|
||||
this._disposed = true;
|
||||
@@ -286,7 +290,9 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
||||
try {
|
||||
document = await vscode.workspace.openTextDocument(this._resource);
|
||||
} catch {
|
||||
await this.showFileNotFoundError();
|
||||
if (!this._disposed) {
|
||||
await this.showFileNotFoundError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -306,7 +312,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider {
|
||||
this.currentVersion = pendingVersion;
|
||||
|
||||
const content = await (shouldReloadPage
|
||||
? this._contentProvider.provideTextDocumentContent(document, this, this._previewConfigurations, this.line, this.state)
|
||||
? this._contentProvider.provideTextDocumentContent(document, this, this._previewConfigurations, this.line, this.state, this._disposeCts.token)
|
||||
: this._contentProvider.markdownBody(document, this));
|
||||
|
||||
// Another call to `doUpdate` may have happened.
|
||||
|
||||
@@ -66,7 +66,8 @@ export class MarkdownContentProvider {
|
||||
resourceProvider: WebviewResourceProvider,
|
||||
previewConfigurations: MarkdownPreviewConfigurationManager,
|
||||
initialLine: number | undefined = undefined,
|
||||
state?: any
|
||||
state: any | undefined,
|
||||
token: vscode.CancellationToken
|
||||
): Promise<MarkdownContentProviderOutput> {
|
||||
const sourceUri = markdownDocument.uri;
|
||||
const config = previewConfigurations.loadAndCacheConfiguration(sourceUri);
|
||||
@@ -89,6 +90,10 @@ export class MarkdownContentProvider {
|
||||
const csp = this.getCsp(resourceProvider, sourceUri, nonce);
|
||||
|
||||
const body = await this.markdownBody(markdownDocument, resourceProvider);
|
||||
if (token.isCancellationRequested) {
|
||||
return { html: '', containingImages: [] };
|
||||
}
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html style="${escapeAttribute(this.getSettingsOverrideStyles(config))}">
|
||||
<head>
|
||||
|
||||
@@ -16,16 +16,19 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace';
|
||||
import { assertRangeEqual, joinLines, workspacePath } from './util';
|
||||
|
||||
|
||||
function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents) {
|
||||
async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents): Promise<vscode.Diagnostic[]> {
|
||||
const engine = createNewMarkdownEngine();
|
||||
const linkProvider = new MdLinkProvider(engine);
|
||||
const computer = new DiagnosticComputer(engine, workspaceContents, linkProvider);
|
||||
return computer.getDiagnostics(doc, {
|
||||
enabled: true,
|
||||
validateFilePaths: DiagnosticLevel.warning,
|
||||
validateOwnHeaders: DiagnosticLevel.warning,
|
||||
validateReferences: DiagnosticLevel.warning,
|
||||
}, noopToken);
|
||||
return (
|
||||
await computer.getDiagnostics(doc, {
|
||||
enabled: true,
|
||||
validateFilePaths: DiagnosticLevel.warning,
|
||||
validateOwnHeaders: DiagnosticLevel.warning,
|
||||
validateReferences: DiagnosticLevel.warning,
|
||||
ignoreLinks: [],
|
||||
}, noopToken)
|
||||
).diagnostics;
|
||||
}
|
||||
|
||||
function createDiagnosticsManager(workspaceContents: MdWorkspaceContents, configuration = new MemoryDiagnosticConfiguration()) {
|
||||
@@ -41,6 +44,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration {
|
||||
|
||||
constructor(
|
||||
private readonly enabled: boolean = true,
|
||||
private readonly ignoreLinks: string[] = [],
|
||||
) { }
|
||||
|
||||
getOptions(_resource: vscode.Uri): DiagnosticOptions {
|
||||
@@ -50,6 +54,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration {
|
||||
validateFilePaths: DiagnosticLevel.ignore,
|
||||
validateOwnHeaders: DiagnosticLevel.ignore,
|
||||
validateReferences: DiagnosticLevel.ignore,
|
||||
ignoreLinks: this.ignoreLinks,
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -57,6 +62,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration {
|
||||
validateFilePaths: DiagnosticLevel.warning,
|
||||
validateOwnHeaders: DiagnosticLevel.warning,
|
||||
validateReferences: DiagnosticLevel.warning,
|
||||
ignoreLinks: this.ignoreLinks,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -155,7 +161,7 @@ suite('markdown: Diagnostics', () => {
|
||||
));
|
||||
|
||||
const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(false));
|
||||
const diagnostics = await manager.getDiagnostics(doc1, noopToken);
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
|
||||
@@ -177,4 +183,89 @@ suite('markdown: Diagnostics', () => {
|
||||
const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1]));
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
|
||||
test('Should allow ignoring invalid file link using glob', async () => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[text](/no-such-file)`,
|
||||
``,
|
||||
`[text]: /no-such-file`,
|
||||
));
|
||||
|
||||
const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/no-such-file']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
|
||||
test('ignoreLinks should allow skipping link to non-existent file', async () => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[text](/no-such-file#header)`,
|
||||
));
|
||||
|
||||
const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/no-such-file']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
|
||||
test('ignoreLinks should not consider link fragment', async () => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
`[text](/no-such-file#header)`,
|
||||
));
|
||||
|
||||
const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/no-such-file']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
|
||||
test('ignoreLinks should support globs', async () => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
``,
|
||||
``,
|
||||
``,
|
||||
));
|
||||
|
||||
const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/images/**/*.png']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
|
||||
test('ignoreLinks should support ignoring header', async () => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
``,
|
||||
));
|
||||
|
||||
const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['#no-such']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
|
||||
test('ignoreLinks should support ignoring header in file', async () => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
``,
|
||||
));
|
||||
const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines(''));
|
||||
|
||||
const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]);
|
||||
{
|
||||
const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration(true, ['/doc2.md#no-such']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
}
|
||||
{
|
||||
const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration(true, ['/doc2.md#*']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
}
|
||||
});
|
||||
|
||||
test('ignoreLinks should support ignore header links if file is ignored', async () => {
|
||||
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
|
||||
``,
|
||||
));
|
||||
const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines(''));
|
||||
|
||||
const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]);
|
||||
const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration(true, ['/doc2.md']));
|
||||
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
|
||||
assert.deepStrictEqual(diagnostics.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -270,4 +270,47 @@ suite('markdown.DocumentLinkProvider', () => {
|
||||
const link = links[0];
|
||||
assertRangeEqual(link.range, new vscode.Range(0, 5, 0, 23));
|
||||
});
|
||||
|
||||
test('Should not detect links inside html comment blocks', async () => {
|
||||
const links = await getLinksForFile(joinLines(
|
||||
`<!-- <http://example.com> -->`,
|
||||
`<!-- [text](./foo.md) -->`,
|
||||
`<!-- [text]: ./foo.md -->`,
|
||||
``,
|
||||
`<!--`,
|
||||
`<http://example.com>`,
|
||||
`-->`,
|
||||
``,
|
||||
`<!--`,
|
||||
`[text](./foo.md)`,
|
||||
`-->`,
|
||||
``,
|
||||
`<!--`,
|
||||
`[text]: ./foo.md`,
|
||||
`-->`,
|
||||
));
|
||||
assert.strictEqual(links.length, 0);
|
||||
});
|
||||
|
||||
test.skip('Should not detect links inside inline html comments', async () => {
|
||||
// See #149678
|
||||
const links = await getLinksForFile(joinLines(
|
||||
`text <!-- <http://example.com> --> text`,
|
||||
`text <!-- [text](./foo.md) --> text`,
|
||||
`text <!-- [text]: ./foo.md --> text`,
|
||||
``,
|
||||
`text <!--`,
|
||||
`<http://example.com>`,
|
||||
`--> text`,
|
||||
``,
|
||||
`text <!--`,
|
||||
`[text](./foo.md)`,
|
||||
`--> text`,
|
||||
``,
|
||||
`text <!--`,
|
||||
`[text]: ./foo.md`,
|
||||
`--> text`,
|
||||
));
|
||||
assert.strictEqual(links.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"src/**/*",
|
||||
"../../src/vscode-dts/vscode.d.ts",
|
||||
"../../src/vscode-dts/vscode.proposed.textEditorDrop.d.ts",
|
||||
"../../src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts"
|
||||
"../../src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts",
|
||||
"../../src/vscode-dts/vscode.proposed.documentPaste.d.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
|
||||
integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
|
||||
|
||||
"@types/picomatch@^2.3.0":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003"
|
||||
integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==
|
||||
|
||||
"@types/trusted-types@*":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
|
||||
@@ -117,6 +122,11 @@ morphdom@^2.6.1:
|
||||
resolved "https://registry.yarnpkg.com/morphdom/-/morphdom-2.6.1.tgz#e868e24f989fa3183004b159aed643e628b4306e"
|
||||
integrity sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA==
|
||||
|
||||
picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"build-notebook": "node ./esbuild"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iktakahiro/markdown-it-katex": "https://github.com/mjbvz/markdown-it-katex.git"
|
||||
"@iktakahiro/markdown-it-katex": "mjbvz/markdown-it-katex"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/markdown-it": "^0.0.0",
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@iktakahiro/markdown-it-katex@https://github.com/mjbvz/markdown-it-katex.git":
|
||||
"@iktakahiro/markdown-it-katex@mjbvz/markdown-it-katex":
|
||||
version "4.0.1"
|
||||
resolved "https://github.com/mjbvz/markdown-it-katex.git#2e3736e4b916ee64ed92ebfabeaa94643612665a"
|
||||
resolved "https://codeload.github.com/mjbvz/markdown-it-katex/tar.gz/1e0d09f9174b3ee1537de2586ce8d8a460284ce4"
|
||||
dependencies:
|
||||
katex "^0.13.0"
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ function generateViewMoreElement(outputId: string) {
|
||||
}
|
||||
|
||||
export function truncatedArrayOfString(id: string, outputs: string[], linesLimit: number, container: HTMLElement) {
|
||||
let buffer = outputs.join('\n').split(/\r|\n|\r\n/g);
|
||||
let buffer = outputs.join('\n').split(/\r\n|\r|\n/g);
|
||||
let lineCount = buffer.length;
|
||||
|
||||
if (lineCount < linesLimit) {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"../../src/vscode-dts/vscode.d.ts",
|
||||
"../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts",
|
||||
"../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "MIT",
|
||||
"description": "Dependencies shared by all extensions",
|
||||
"dependencies": {
|
||||
"typescript": "4.7.1-rc"
|
||||
"typescript": "4.7"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node ./postinstall.mjs"
|
||||
@@ -12,6 +12,6 @@
|
||||
"devDependencies": {
|
||||
"@parcel/watcher": "2.0.5",
|
||||
"esbuild": "^0.11.12",
|
||||
"vscode-grammar-updater": "^1.0.4"
|
||||
"vscode-grammar-updater": "^1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"git": {
|
||||
"name": "language-php",
|
||||
"repositoryUrl": "https://github.com/atom/language-php",
|
||||
"commitHash": "ff64523c94c014d68f5dec189b05557649c5872a"
|
||||
"commitHash": "eb28b8aea1214dcbc732f3d9b9ed20c089c648bd"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
"version": "0.48.0"
|
||||
"version": "0.48.1"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
|
||||
@@ -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/atom/language-php/commit/ff64523c94c014d68f5dec189b05557649c5872a",
|
||||
"version": "https://github.com/atom/language-php/commit/eb28b8aea1214dcbc732f3d9b9ed20c089c648bd",
|
||||
"scopeName": "source.php",
|
||||
"patterns": [
|
||||
{
|
||||
@@ -13,62 +13,6 @@
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"begin": "(?i)^\\s*(interface)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\\s*(extends)?\\s*",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.type.interface.php"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.interface.php"
|
||||
},
|
||||
"3": {
|
||||
"name": "storage.modifier.extends.php"
|
||||
}
|
||||
},
|
||||
"end": "(?i)((?:[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*\\s*,\\s*)*)([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)?\\s*(?:(?={)|$)",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "(?i)[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*",
|
||||
"name": "entity.other.inherited-class.php"
|
||||
},
|
||||
{
|
||||
"match": ",",
|
||||
"name": "punctuation.separator.classes.php"
|
||||
}
|
||||
]
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.other.inherited-class.php"
|
||||
}
|
||||
},
|
||||
"name": "meta.interface.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#namespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?i)^\\s*(trait)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.type.trait.php"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.trait.php"
|
||||
}
|
||||
},
|
||||
"end": "(?={)",
|
||||
"name": "meta.trait.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"match": "(?i)(?:^|(?<=<\\?php))\\s*(namespace)\\s+([a-z0-9_\\x{7f}-\\x{10ffff}\\\\]+)(?=\\s*;)",
|
||||
"name": "meta.namespace.php",
|
||||
@@ -232,6 +176,149 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?ix)\n\\b(trait)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.type.trait.php"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.trait.php"
|
||||
}
|
||||
},
|
||||
"end": "}|(?=\\?>)",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.trait.end.bracket.curly.php"
|
||||
}
|
||||
},
|
||||
"name": "meta.trait.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.trait.begin.bracket.curly.php"
|
||||
}
|
||||
},
|
||||
"end": "(?=}|\\?>)",
|
||||
"contentName": "meta.trait.body.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "$self"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?ix)\n\\b(interface)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.type.interface.php"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.interface.php"
|
||||
}
|
||||
},
|
||||
"end": "}|(?=\\?>)",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.interface.end.bracket.curly.php"
|
||||
}
|
||||
},
|
||||
"name": "meta.interface.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"include": "#interface-extends"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.interface.begin.bracket.curly.php"
|
||||
}
|
||||
},
|
||||
"end": "(?=}|\\?>)",
|
||||
"contentName": "meta.interface.body.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#class-constant"
|
||||
},
|
||||
{
|
||||
"include": "$self"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?ix)\n\\b(enum)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\n(?: \\s* (:) \\s* (int | string) \\b )?",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.type.enum.php"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.name.type.enum.php"
|
||||
},
|
||||
"3": {
|
||||
"name": "keyword.operator.return-value.php"
|
||||
},
|
||||
"4": {
|
||||
"name": "keyword.other.type.php"
|
||||
}
|
||||
},
|
||||
"end": "}|(?=\\?>)",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.enum.end.bracket.curly.php"
|
||||
}
|
||||
},
|
||||
"name": "meta.enum.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"include": "#class-implements"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.enum.begin.bracket.curly.php"
|
||||
}
|
||||
},
|
||||
"end": "(?=}|\\?>)",
|
||||
"contentName": "meta.enum.body.php",
|
||||
"patterns": [
|
||||
{
|
||||
"match": "(?i)\\b(case)\\s*([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "storage.modifier.php"
|
||||
},
|
||||
"2": {
|
||||
"name": "constant.enum.php"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": "#class-constant"
|
||||
},
|
||||
{
|
||||
"include": "$self"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?ix)\n(?:\n \\b(?:(abstract|final)\\s+)?(class)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\n |\\b(new)\\b\\s*(\\#\\[.*\\])?\\s*\\b(class)\\b # anonymous class\n)",
|
||||
"beginCaptures": {
|
||||
@@ -266,89 +353,37 @@
|
||||
},
|
||||
"name": "meta.class.php",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?<=class)\\s*(\\()",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.arguments.begin.bracket.round.php"
|
||||
}
|
||||
},
|
||||
"end": "\\)|(?=\\?>)",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.arguments.end.bracket.round.php"
|
||||
}
|
||||
},
|
||||
"name": "meta.function-call.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#named-arguments"
|
||||
},
|
||||
{
|
||||
"include": "$self"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"begin": "(?i)(extends)\\s+",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.modifier.extends.php"
|
||||
}
|
||||
},
|
||||
"contentName": "meta.other.inherited-class.php",
|
||||
"end": "(?i)(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\])",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)(?=\\\\?[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*\\\\)",
|
||||
"end": "(?i)([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)?(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\])",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "entity.other.inherited-class.php"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#namespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"include": "#class-builtin"
|
||||
},
|
||||
{
|
||||
"include": "#namespace"
|
||||
},
|
||||
{
|
||||
"match": "(?i)[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*",
|
||||
"name": "entity.other.inherited-class.php"
|
||||
}
|
||||
]
|
||||
"include": "#class-extends"
|
||||
},
|
||||
{
|
||||
"begin": "(?i)(implements)\\s+",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.modifier.implements.php"
|
||||
}
|
||||
},
|
||||
"end": "(?i)(?=[;{])",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"begin": "(?i)(?=[a-z0-9_\\x{7f}-\\x{10ffff}\\\\]+)",
|
||||
"contentName": "meta.other.inherited-class.php",
|
||||
"end": "(?i)(?:\\s*(?:,|(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\\\s]))\\s*)",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)(?=\\\\?[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*\\\\)",
|
||||
"end": "(?i)([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)?(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\])",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "entity.other.inherited-class.php"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#namespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"include": "#class-builtin"
|
||||
},
|
||||
{
|
||||
"include": "#namespace"
|
||||
},
|
||||
{
|
||||
"match": "(?i)[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*",
|
||||
"name": "entity.other.inherited-class.php"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"include": "#class-implements"
|
||||
},
|
||||
{
|
||||
"begin": "{",
|
||||
@@ -360,6 +395,9 @@
|
||||
"end": "(?=}|\\?>)",
|
||||
"contentName": "meta.class.body.php",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#class-constant"
|
||||
},
|
||||
{
|
||||
"include": "$self"
|
||||
}
|
||||
@@ -382,7 +420,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": "(?x)\n\\s* # FIXME: Removing this causes specs to fail. Investigate.\n\\b(\n break|case|continue|declare|default|die|do|\n else(if)?|end(declare|for(each)?|if|switch|while)|exit|\n for(each)?|if|return|switch|use|while|yield\n)\\b",
|
||||
"match": "(?x)\n\\b(\n break|case|continue|declare|default|die|do|\n else(if)?|end(declare|for(each)?|if|switch|while)|exit|\n for(each)?|if|return|switch|use|while|yield\n)\\b",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.control.${1:/downcase}.php"
|
||||
@@ -853,7 +891,7 @@
|
||||
"name": "storage.type.php"
|
||||
},
|
||||
{
|
||||
"match": "(?i)\\b(global|abstract|const|extends|implements|final|private|protected|public|static)\\b",
|
||||
"match": "(?i)\\b(global|abstract|const|final|private|protected|public|static)\\b",
|
||||
"name": "storage.modifier.php"
|
||||
},
|
||||
{
|
||||
@@ -975,7 +1013,7 @@
|
||||
"name": "entity.name.goto-label.php"
|
||||
}
|
||||
},
|
||||
"match": "(?i)^\\s*([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\\s*:(?!:)"
|
||||
"match": "(?i)^\\s*([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*(?<!default))\\s*:(?!:)"
|
||||
},
|
||||
{
|
||||
"include": "#string-backtick"
|
||||
@@ -1187,6 +1225,120 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"inheritance-single": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)(?=\\\\?[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*\\\\)",
|
||||
"end": "(?i)([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)?(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\])",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "entity.other.inherited-class.php"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#namespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"include": "#class-builtin"
|
||||
},
|
||||
{
|
||||
"include": "#namespace"
|
||||
},
|
||||
{
|
||||
"match": "(?i)[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*",
|
||||
"name": "entity.other.inherited-class.php"
|
||||
}
|
||||
]
|
||||
},
|
||||
"class-extends": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)(extends)\\s+",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.modifier.extends.php"
|
||||
}
|
||||
},
|
||||
"end": "(?i)(?=[^A-Za-z0-9_\\x{7f}-\\x{10ffff}\\\\])",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"include": "#inheritance-single"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface-extends": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)(extends)\\s+",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.modifier.extends.php"
|
||||
}
|
||||
},
|
||||
"end": "(?i)(?={)",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"match": ",",
|
||||
"name": "punctuation.separator.classes.php"
|
||||
},
|
||||
{
|
||||
"include": "#inheritance-single"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"class-implements": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)(implements)\\s+",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "storage.modifier.implements.php"
|
||||
}
|
||||
},
|
||||
"end": "(?i)(?={)",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"match": ",",
|
||||
"name": "punctuation.separator.classes.php"
|
||||
},
|
||||
{
|
||||
"include": "#inheritance-single"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"class-constant": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "(?i)\\b(const)\\s*([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "storage.modifier.php"
|
||||
},
|
||||
"2": {
|
||||
"name": "constant.other.php"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"comments": {
|
||||
"patterns": [
|
||||
{
|
||||
@@ -1621,7 +1773,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(<<<)\\s*(\"?)(SQL)(\\2)(\\s*)$",
|
||||
"begin": "(<<<)\\s*(\"?)([DS]QL)(\\2)(\\s*)$",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.section.embedded.begin.php"
|
||||
@@ -1976,7 +2128,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(<<<)\\s*'(SQL)'(\\s*)$",
|
||||
"begin": "(<<<)\\s*'([DS]QL)'(\\s*)$",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.section.embedded.begin.php"
|
||||
|
||||
@@ -151,6 +151,7 @@ const browserPlugins = [
|
||||
]
|
||||
}),
|
||||
new DefinePlugin({
|
||||
'process.platform': JSON.stringify('web'),
|
||||
'process.env': JSON.stringify({}),
|
||||
'process.env.BROWSER_ENV': JSON.stringify('true')
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"activityBarBadge.background": "#007ACC",
|
||||
"sideBarTitle.foreground": "#BBBBBB",
|
||||
"input.placeholderForeground": "#A6A6A6",
|
||||
"menu.background": "#252526",
|
||||
"menu.background": "#303031",
|
||||
"menu.foreground": "#CCCCCC",
|
||||
"statusBarItem.remoteForeground": "#FFF",
|
||||
"statusBarItem.remoteBackground": "#16825D",
|
||||
|
||||
@@ -301,6 +301,12 @@
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.variableTypes.suppressWhenTypeMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
@@ -353,6 +359,12 @@
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"javascript.inlayHints.variableTypes.suppressWhenTypeMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"javascript.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
@@ -686,7 +698,7 @@
|
||||
},
|
||||
"js/ts.implicitProjectConfig.checkJs": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.implicitProjectConfig.checkJs%",
|
||||
"scope": "window"
|
||||
},
|
||||
@@ -705,7 +717,7 @@
|
||||
},
|
||||
"js/ts.implicitProjectConfig.strictNullChecks": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.implicitProjectConfig.strictNullChecks%",
|
||||
"scope": "window"
|
||||
},
|
||||
|
||||
@@ -88,10 +88,7 @@
|
||||
"message": "Enable/disable inlay hints for parameter names:\n```typescript\n\nparseInt(/* str: */ '123', /* radix: */ 8)\n \n```\nRequires using TypeScript 4.4+ in the workspace.",
|
||||
"comment": "The text inside the ``` block is code and should not be localized."
|
||||
},
|
||||
"configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName": {
|
||||
"message": "Suppress parameter name hints on arguments whose text is identical to the parameter name.",
|
||||
"comment": "The text inside the ``` block is code and should not be localized."
|
||||
},
|
||||
"configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName": "Suppress parameter name hints on arguments whose text is identical to the parameter name.",
|
||||
"configuration.inlayHints.parameterTypes.enabled": {
|
||||
"message": "Enable/disable inlay hints for implicit parameter types:\n```typescript\n\nel.addEventListener('click', e /* :MouseEvent */ => ...)\n \n```\nRequires using TypeScript 4.4+ in the workspace.",
|
||||
"comment": "The text inside the ``` block is code and should not be localized."
|
||||
@@ -100,6 +97,7 @@
|
||||
"message": "Enable/disable inlay hints for implicit variable types:\n```typescript\n\nconst foo /* :number */ = Date.now();\n \n```\nRequires using TypeScript 4.4+ in the workspace.",
|
||||
"comment": "The text inside the ``` block is code and should not be localized."
|
||||
},
|
||||
"configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName": "Suppress type hints on variables whose name is identical to the type name. Requires using TypeScript 4.8+ in the workspace.",
|
||||
"configuration.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"message": "Enable/disable inlay hints for implicit types on property declarations:\n```typescript\n\nclass Foo {\n\tprop /* :number */ = Date.now();\n}\n \n```\nRequires using TypeScript 4.4+ in the workspace.",
|
||||
"comment": "The text inside the ``` block is code and should not be localized."
|
||||
|
||||
@@ -554,6 +554,7 @@ class CompletionAcceptedCommand implements Command {
|
||||
if (item instanceof MyCompletionItem) {
|
||||
/* __GDPR__
|
||||
"completions.accept" : {
|
||||
"owner": "mjbvz",
|
||||
"isPackageJsonImport" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"isImportStatementCompletion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
@@ -820,6 +821,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider<
|
||||
) {
|
||||
/* __GDPR__
|
||||
"completions.execute" : {
|
||||
"owner": "mjbvz",
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
|
||||
+2
@@ -210,6 +210,7 @@ export class InlayHintSettingNames {
|
||||
static readonly parameterNamesSuppressWhenArgumentMatchesName = 'inlayHints.parameterNames.suppressWhenArgumentMatchesName';
|
||||
static readonly parameterNamesEnabled = 'inlayHints.parameterTypes.enabled';
|
||||
static readonly variableTypesEnabled = 'inlayHints.variableTypes.enabled';
|
||||
static readonly variableTypesSuppressWhenTypeMatchesName = 'inlayHints.variableTypes.suppressWhenTypeMatchesName';
|
||||
static readonly propertyDeclarationTypesEnabled = 'inlayHints.propertyDeclarationTypes.enabled';
|
||||
static readonly functionLikeReturnTypesEnabled = 'inlayHints.functionLikeReturnTypes.enabled';
|
||||
static readonly enumMemberValuesEnabled = 'inlayHints.enumMemberValues.enabled';
|
||||
@@ -221,6 +222,7 @@ export function getInlayHintsPreferences(config: vscode.WorkspaceConfiguration)
|
||||
includeInlayParameterNameHintsWhenArgumentMatchesName: !config.get<boolean>(InlayHintSettingNames.parameterNamesSuppressWhenArgumentMatchesName, true),
|
||||
includeInlayFunctionParameterTypeHints: config.get<boolean>(InlayHintSettingNames.parameterNamesEnabled, false),
|
||||
includeInlayVariableTypeHints: config.get<boolean>(InlayHintSettingNames.variableTypesEnabled, false),
|
||||
includeInlayVariableTypeHintsWhenTypeMatchesName: !config.get<boolean>(InlayHintSettingNames.variableTypesSuppressWhenTypeMatchesName, true),
|
||||
includeInlayPropertyDeclarationTypeHints: config.get<boolean>(InlayHintSettingNames.propertyDeclarationTypesEnabled, false),
|
||||
includeInlayFunctionLikeReturnTypeHints: config.get<boolean>(InlayHintSettingNames.functionLikeReturnTypesEnabled, false),
|
||||
includeInlayEnumMemberValueHints: config.get<boolean>(InlayHintSettingNames.enumMemberValuesEnabled, false),
|
||||
|
||||
@@ -15,14 +15,15 @@ import { Position } from '../utils/typeConverters';
|
||||
import FileConfigurationManager, { getInlayHintsPreferences, InlayHintSettingNames } from './fileConfigurationManager';
|
||||
|
||||
|
||||
const inlayHintSettingNames = [
|
||||
const inlayHintSettingNames = Object.freeze([
|
||||
InlayHintSettingNames.parameterNamesSuppressWhenArgumentMatchesName,
|
||||
InlayHintSettingNames.parameterNamesEnabled,
|
||||
InlayHintSettingNames.variableTypesEnabled,
|
||||
InlayHintSettingNames.variableTypesSuppressWhenTypeMatchesName,
|
||||
InlayHintSettingNames.propertyDeclarationTypesEnabled,
|
||||
InlayHintSettingNames.functionLikeReturnTypesEnabled,
|
||||
InlayHintSettingNames.enumMemberValuesEnabled,
|
||||
];
|
||||
]);
|
||||
|
||||
class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHintsProvider {
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class OrganizeImportsCommand implements Command {
|
||||
public async execute(file: string, sortOnly = false): Promise<any> {
|
||||
/* __GDPR__
|
||||
"organizeImports.execute" : {
|
||||
"owner": "mjbvz",
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}"
|
||||
]
|
||||
|
||||
@@ -37,6 +37,7 @@ class ApplyCodeActionCommand implements Command {
|
||||
): Promise<boolean> {
|
||||
/* __GDPR__
|
||||
"quickFix.execute" : {
|
||||
"owner": "mjbvz",
|
||||
"fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}"
|
||||
@@ -67,6 +68,7 @@ class ApplyFixAllCodeAction implements Command {
|
||||
public async execute(args: ApplyFixAllCodeAction_args): Promise<void> {
|
||||
/* __GDPR__
|
||||
"quickFixAll.execute" : {
|
||||
"owner": "mjbvz",
|
||||
"fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}"
|
||||
|
||||
@@ -36,6 +36,7 @@ class DidApplyRefactoringCommand implements Command {
|
||||
public async execute(args: DidApplyRefactoringCommand_Args): Promise<void> {
|
||||
/* __GDPR__
|
||||
"refactor.execute" : {
|
||||
"owner": "mjbvz",
|
||||
"action" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}"
|
||||
|
||||
@@ -230,6 +230,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
|
||||
if (!executeInfo.token || !executeInfo.token.isCancellationRequested) {
|
||||
/* __GDPR__
|
||||
"languageServiceErrorResponse" : {
|
||||
"owner": "mjbvz",
|
||||
"${include}": [
|
||||
"${TypeScriptCommonProperties}",
|
||||
"${TypeScriptRequestErrorProperties}"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user