diff --git a/.eslintrc.json b/.eslintrc.json
index 1c420f93df5..65054e9b4a4 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -63,13 +63,18 @@
"browser": [
"common"
],
- "electron-main": [
+ "electron-sandbox": [
"common",
- "node"
+ "browser"
],
"electron-browser": [
"common",
"browser",
+ "node",
+ "electron-sandbox"
+ ],
+ "electron-main": [
+ "common",
"node"
]
}
@@ -104,6 +109,14 @@
"**/vs/base/{common,browser}/**"
]
},
+ {
+ "target": "**/vs/base/electron-sandbox/**",
+ "restrictions": [
+ "vs/nls",
+ "vs/css!./**/*",
+ "**/vs/base/{common,browser,electron-sandbox}/**"
+ ]
+ },
{
"target": "**/vs/base/node/**",
"restrictions": [
@@ -149,13 +162,22 @@
"*" // node modules
]
},
+ {
+ "target": "**/vs/base/parts/*/electron-sandbox/**",
+ "restrictions": [
+ "vs/nls",
+ "vs/css!./**/*",
+ "**/vs/base/{common,browser,electron-sandbox}/**",
+ "**/vs/base/parts/*/{common,browser,electron-sandbox}/**"
+ ]
+ },
{
"target": "**/vs/base/parts/*/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
- "**/vs/base/{common,browser,node,electron-browser}/**",
- "**/vs/base/parts/*/{common,browser,node,electron-browser}/**",
+ "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@@ -185,6 +207,7 @@
"vs/nls",
"**/vs/base/common/**",
"**/vs/base/parts/*/common/**",
+ "**/vs/base/test/common/**",
"**/vs/platform/*/common/**",
"**/vs/platform/*/test/common/**"
]
@@ -209,14 +232,24 @@
"*" // node modules
]
},
+ {
+ "target": "**/vs/platform/*/electron-sandbox/**",
+ "restrictions": [
+ "vs/nls",
+ "vs/css!./**/*",
+ "**/vs/base/{common,browser,electron-sandbox}/**",
+ "**/vs/base/parts/*/{common,browser,electron-sandbox}/**",
+ "**/vs/platform/*/{common,browser,electron-sandbox}/**"
+ ]
+ },
{
"target": "**/vs/platform/*/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
- "**/vs/base/{common,browser,node}/**",
- "**/vs/base/parts/*/{common,browser,node,electron-browser}/**",
- "**/vs/platform/*/{common,browser,node,electron-browser}/**",
+ "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@@ -420,18 +453,34 @@
"**/vs/**/{common,worker}/**"
]
},
+ {
+ "target": "**/vs/workbench/electron-sandbox/**",
+ "restrictions": [
+ "vs/nls",
+ "vs/css!./**/*",
+ "**/vs/base/{common,browser,electron-sandbox}/**",
+ "**/vs/base/parts/*/{common,browser,electron-sandbox}/**",
+ "**/vs/platform/*/{common,browser,electron-sandbox}/**",
+ "**/vs/editor/{common,browser,electron-sandbox}/**",
+ "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention
+ "**/vs/workbench/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/api/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/services/*/{common,browser,electron-sandbox}/**"
+ ]
+ },
{
"target": "**/vs/workbench/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
- "**/vs/base/{common,browser,node,electron-browser}/**",
- "**/vs/base/parts/*/{common,browser,node,electron-browser}/**",
- "**/vs/platform/*/{common,browser,node,electron-browser}/**",
- "**/vs/editor/{common,browser,node,electron-browser}/**",
+ "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/editor/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention
- "**/vs/workbench/{common,browser,node,electron-browser,api}/**",
- "**/vs/workbench/services/*/{common,browser,node,electron-browser}/**",
+ "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/services/*/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@@ -443,7 +492,7 @@
"**/vs/base/**",
"**/vs/platform/**",
"**/vs/editor/**",
- "**/vs/workbench/{common,browser,node,electron-browser}/**",
+ "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
"vs/workbench/contrib/files/common/editors/fileEditorInput",
"**/vs/workbench/services/**",
"**/vs/workbench/test/**",
@@ -507,16 +556,30 @@
"*" // node modules
]
},
+ {
+ "target": "**/vs/workbench/services/**/electron-sandbox/**",
+ "restrictions": [
+ "vs/nls",
+ "vs/css!./**/*",
+ "**/vs/base/**/{common,browser,worker,electron-sandbox}/**",
+ "**/vs/platform/**/{common,browser,electron-sandbox}/**",
+ "**/vs/editor/**",
+ "**/vs/workbench/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/api/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/services/**/{common,browser,electron-sandbox}/**"
+ ]
+ },
{
"target": "**/vs/workbench/services/**/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
- "**/vs/base/**/{common,browser,worker,node,electron-browser}/**",
- "**/vs/platform/**/{common,browser,node,electron-browser}/**",
+ "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**",
+ "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/**",
- "**/vs/workbench/{common,browser,node,electron-browser,api}/**",
- "**/vs/workbench/services/**/{common,browser,node,electron-browser}/**",
+ "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@@ -529,7 +592,7 @@
"**/vs/base/**",
"**/vs/platform/**",
"**/vs/editor/**",
- "**/vs/workbench/{common,browser,node,electron-browser}/**",
+ "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/workbench/services/**",
"**/vs/workbench/contrib/**",
"**/vs/workbench/test/**",
@@ -623,17 +686,32 @@
"*" // node modules
]
},
+ {
+ "target": "**/vs/workbench/contrib/**/electron-sandbox/**",
+ "restrictions": [
+ "vs/nls",
+ "vs/css!./**/*",
+ "**/vs/base/**/{common,browser,worker,electron-sandbox}/**",
+ "**/vs/platform/**/{common,browser,electron-sandbox}/**",
+ "**/vs/editor/**",
+ "**/vs/workbench/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/api/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/services/**/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/contrib/**/{common,browser,electron-sandbox}/**"
+ ]
+ },
{
"target": "**/vs/workbench/contrib/**/electron-browser/**",
"restrictions": [
"vs/nls",
"vs/css!./**/*",
- "**/vs/base/**/{common,browser,worker,node,electron-browser}/**",
- "**/vs/platform/**/{common,browser,node,electron-browser}/**",
+ "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**",
+ "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"**/vs/editor/**",
- "**/vs/workbench/{common,browser,node,electron-browser,api}/**",
- "**/vs/workbench/services/**/{common,browser,node,electron-browser}/**",
- "**/vs/workbench/contrib/**/{common,browser,node,electron-browser}/**",
+ "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/contrib/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@@ -653,10 +731,10 @@
"restrictions": [
"vs/nls",
"vs/css!./**/*",
- "**/vs/base/**/{common,browser,node,electron-browser}/**",
- "**/vs/base/parts/**/{common,browser,node,electron-browser}/**",
- "**/vs/platform/**/{common,browser,node,electron-browser}/**",
- "**/vs/code/**/{common,browser,node,electron-browser}/**",
+ "**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/code/**/{common,browser,node,electron-sandbox,electron-browser}/**",
"*" // node modules
]
},
@@ -684,6 +762,54 @@
"*" // node modules
]
},
+ {
+ "target": "**/src/vs/workbench/workbench.common.main.ts",
+ "restrictions": [
+ "vs/nls",
+ "**/vs/base/**/{common,browser}/**",
+ "**/vs/base/parts/**/{common,browser}/**",
+ "**/vs/platform/**/{common,browser}/**",
+ "**/vs/editor/**",
+ "**/vs/workbench/**/{common,browser}/**"
+ ]
+ },
+ {
+ "target": "**/src/vs/workbench/workbench.web.main.ts",
+ "restrictions": [
+ "vs/nls",
+ "**/vs/base/**/{common,browser}/**",
+ "**/vs/base/parts/**/{common,browser}/**",
+ "**/vs/platform/**/{common,browser}/**",
+ "**/vs/editor/**",
+ "**/vs/workbench/**/{common,browser}/**",
+ "**/vs/workbench/workbench.common.main"
+ ]
+ },
+ {
+ "target": "**/src/vs/workbench/workbench.sandbox.main.ts",
+ "restrictions": [
+ "vs/nls",
+ "**/vs/base/**/{common,browser,electron-sandbox}/**",
+ "**/vs/base/parts/**/{common,browser,electron-sandbox}/**",
+ "**/vs/platform/**/{common,browser,electron-sandbox}/**",
+ "**/vs/editor/**",
+ "**/vs/workbench/**/{common,browser,electron-sandbox}/**",
+ "**/vs/workbench/workbench.common.main"
+ ]
+ },
+ {
+ "target": "**/src/vs/workbench/workbench.desktop.main.ts",
+ "restrictions": [
+ "vs/nls",
+ "**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/editor/**",
+ "**/vs/workbench/**/{common,browser,node,electron-sandbox,electron-browser}/**",
+ "**/vs/workbench/workbench.common.main",
+ "**/vs/workbench/workbench.sandbox.main"
+ ]
+ },
{
"target": "**/extensions/**",
"restrictions": "**/*"
diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml
index e78602ddd11..3e0e8b6425b 100644
--- a/.github/workflows/author-verified.yml
+++ b/.github/workflows/author-verified.yml
@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
- ref: v16
+ ref: v18
path: ./actions
- name: Install Actions
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested')
@@ -31,6 +31,7 @@ jobs:
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested')
uses: ./actions/author-verified
with:
+ token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
requestVerificationComment: "This bug has been fixed in to the latest release of [VS Code Insiders](https://code.visualstudio.com/insiders/)!\n\n@${author}, you can help us out by commenting `/verified` if things are now working as expected.\n\nIf things still don't seem right, please ensure you're on version ${commit} of Insiders (today's or later - you can use `Help: About` in the command pallette to check), and leave a comment letting us know what isn't working as expected.\n\nHappy Coding!"
pendingReleaseLabel: awaiting-insiders-release
authorVerificationRequestedLabel: author-verification-requested
diff --git a/.github/workflows/classifier-apply.yml b/.github/workflows/classifier-apply.yml
index 38e0aabad7f..a881de9e501 100644
--- a/.github/workflows/classifier-apply.yml
+++ b/.github/workflows/classifier-apply.yml
@@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
- ref: v16
+ ref: v18
path: ./actions
- name: Install Actions
run: npm install --production --prefix ./actions
diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml
index 127af00a002..e11044ee792 100644
--- a/.github/workflows/commands.yml
+++ b/.github/workflows/commands.yml
@@ -13,7 +13,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
- ref: v16
+ ref: v18
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run Commands
diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml
index 241fe7b413d..1e18239a5cf 100644
--- a/.github/workflows/english-please.yml
+++ b/.github/workflows/english-please.yml
@@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
- ref: v16
+ ref: v18
path: ./actions
- name: Install Actions
if: contains(github.event.issue.labels.*.name, '*english-please')
diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml
index 3beed1f0e28..d14c721c0f3 100644
--- a/.github/workflows/feature-request.yml
+++ b/.github/workflows/feature-request.yml
@@ -17,7 +17,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
- ref: v16
+ ref: v18
- name: Install Actions
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request')
run: npm install --production --prefix ./actions
diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml
index e870f6bcb17..b7ad214939e 100644
--- a/.github/workflows/locker.yml
+++ b/.github/workflows/locker.yml
@@ -13,7 +13,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
- ref: v16
+ ref: v18
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run Locker
diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml
index d82efcf97de..9693bab593a 100644
--- a/.github/workflows/needs-more-info-closer.yml
+++ b/.github/workflows/needs-more-info-closer.yml
@@ -13,7 +13,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
- ref: v16
+ ref: v18
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run Needs More Info Closer
diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml
index 57fe3b3b988..241e75e670d 100644
--- a/.github/workflows/on-label.yml
+++ b/.github/workflows/on-label.yml
@@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
- ref: v16
+ ref: v18
path: ./actions
- name: Install Actions
run: npm install --production --prefix ./actions
diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml
index a8f501c4656..9d1be512929 100644
--- a/.github/workflows/on-open.yml
+++ b/.github/workflows/on-open.yml
@@ -11,7 +11,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: 'microsoft/vscode-github-triage-actions'
- ref: v16
+ ref: v18
path: ./actions
- name: Install Actions
run: npm install --production --prefix ./actions
diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml
new file mode 100644
index 00000000000..83eb1657edf
--- /dev/null
+++ b/.github/workflows/release-pipeline-labeler.yml
@@ -0,0 +1,31 @@
+name: "Release Pipeline Labeler"
+on:
+ issues:
+ types: [closed]
+ schedule:
+ - cron: 20 14 * * * # 4:20pm Zurich
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Actions
+ uses: actions/checkout@v2
+ with:
+ repository: 'microsoft/vscode-github-triage-actions'
+ ref: v18
+ path: ./actions
+ - name: Checkout Repo
+ if: github.event_name != 'issues'
+ uses: actions/checkout@v2
+ with:
+ path: ./repo
+ fetch-depth: 0
+ - name: Install Actions
+ run: npm install --production --prefix ./actions
+ - name: "Run Release Pipeline Labeler"
+ uses: ./actions/release-pipeline
+ with:
+ token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
+ notYetReleasedLabel: unreleased
+ insidersReleasedLabel: insiders-released
diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml
index be7d119fa3b..154189c4d6c 100644
--- a/.github/workflows/test-plan-item-validator.yml
+++ b/.github/workflows/test-plan-item-validator.yml
@@ -14,7 +14,7 @@ jobs:
with:
repository: 'microsoft/vscode-github-triage-actions'
path: ./actions
- ref: v16
+ ref: v18
- name: Install Actions
if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item')
run: npm install --production --prefix ./actions
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 496b3cbf6e3..cfd41590211 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -20,10 +20,7 @@
"port": 5870,
"outFiles": [
"${workspaceFolder}/out/**/*.js"
- ],
- "presentation": {
- "hidden": true
- }
+ ]
},
{
"type": "pwa-chrome",
@@ -173,7 +170,7 @@
"${workspaceFolder}/out/**/*.js"
],
"presentation": {
- "group": "6_tests",
+ "group": "5_tests",
"order": 6
}
},
@@ -220,10 +217,7 @@
"type": "node",
"request": "launch",
"name": "VS Code (Web)",
- "runtimeExecutable": "yarn",
- "runtimeArgs": [
- "web"
- ],
+ "program": "${workspaceFolder}/scripts/code-web.js",
"presentation": {
"group": "0_vscode",
"order": 2
@@ -277,7 +271,7 @@
{
"type": "node",
"request": "launch",
- "name": "HTML Unit Tests",
+ "name": "HTML Server Unit Tests",
"program": "${workspaceFolder}/extensions/html-language-features/server/test/index.js",
"stopOnEntry": false,
"cwd": "${workspaceFolder}/extensions/html-language-features/server",
@@ -289,6 +283,21 @@
"order": 10
}
},
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "CSS Server Unit Tests",
+ "program": "${workspaceFolder}/extensions/css-language-features/server/test/index.js",
+ "stopOnEntry": false,
+ "cwd": "${workspaceFolder}/extensions/css-language-features/server",
+ "outFiles": [
+ "${workspaceFolder}/extensions/css-language-features/server/out/**/*.js"
+ ],
+ "presentation": {
+ "group": "5_tests",
+ "order": 10
+ }
+ },
{
"type": "extensionHost",
"request": "launch",
@@ -313,7 +322,7 @@
"name": "TypeScript Extension Tests",
"runtimeExecutable": "${execPath}",
"args": [
- "${workspaceFolder}/extensions/typescript-language-features/test-fixtures",
+ "${workspaceFolder}/extensions/typescript-language-features/test-workspace",
"--extensionDevelopmentPath=${workspaceFolder}/extensions/typescript-language-features",
"--extensionTestsPath=${workspaceFolder}/extensions/typescript-language-features/out/test"
],
diff --git a/.yarnrc b/.yarnrc
index d86b284e83e..00b2ebda693 100644
--- a/.yarnrc
+++ b/.yarnrc
@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
-target "7.2.4"
+target "7.3.0"
runtime "electron"
diff --git a/build/azure-pipelines/darwin/entitlements.plist b/build/azure-pipelines/darwin/app-entitlements.plist
similarity index 84%
rename from build/azure-pipelines/darwin/entitlements.plist
rename to build/azure-pipelines/darwin/app-entitlements.plist
index be8b7163da7..90031d937be 100644
--- a/build/azure-pipelines/darwin/entitlements.plist
+++ b/build/azure-pipelines/darwin/app-entitlements.plist
@@ -6,8 +6,6 @@
com.apple.security.cs.allow-unsigned-executable-memory
- com.apple.security.cs.disable-library-validation
-
com.apple.security.cs.allow-dyld-environment-variables
diff --git a/build/azure-pipelines/darwin/helper-entitlements.plist b/build/azure-pipelines/darwin/helper-entitlements.plist
deleted file mode 100644
index 123d12a53e9..00000000000
--- a/build/azure-pipelines/darwin/helper-entitlements.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- com.apple.security.cs.disable-library-validation
-
-
-
diff --git a/build/azure-pipelines/darwin/helper-gpu-entitlements.plist b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist
index 777b3abd95e..4efe1ce508f 100644
--- a/build/azure-pipelines/darwin/helper-gpu-entitlements.plist
+++ b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist
@@ -4,7 +4,5 @@
com.apple.security.cs.allow-jit
- com.apple.security.cs.disable-library-validation
-
diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml
index 8419e3388ad..ea286ef1418 100644
--- a/build/azure-pipelines/darwin/product-build-darwin.yml
+++ b/build/azure-pipelines/darwin/product-build-darwin.yml
@@ -162,21 +162,13 @@ steps:
- script: |
set -e
- APP_ROOT=$(agent.builddirectory)/VSCode-darwin
- APP_NAME="`ls $APP_ROOT | head -n 1`"
- HELPER_APP_NAME="`echo $APP_NAME | sed -e 's/^Visual Studio //;s/\.app$//'`"
- APP_FRAMEWORK_PATH="$APP_ROOT/$APP_NAME/Contents/Frameworks"
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
- codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist "$APP_ROOT"/*.app
- codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper.app"
- codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-gpu-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (GPU).app"
- codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-plugin-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Plugin).app"
- codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-renderer-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Renderer).app"
+ DEBUG=electron-osx-sign* node build/darwin/sign.js
displayName: Set Hardened Entitlements
- script: |
diff --git a/build/azure-pipelines/mixin.js b/build/azure-pipelines/mixin.js
index efb7d4d1ca9..8d441e783a9 100644
--- a/build/azure-pipelines/mixin.js
+++ b/build/azure-pipelines/mixin.js
@@ -12,6 +12,8 @@ const es = require('event-stream');
const vfs = require('vinyl-fs');
const fancyLog = require('fancy-log');
const ansiColors = require('ansi-colors');
+const fs = require('fs');
+const path = require('path');
function main() {
const quality = process.env['VSCODE_QUALITY'];
@@ -21,7 +23,7 @@ function main() {
return;
}
- const productJsonFilter = filter('product.json', { restore: true });
+ const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true });
fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`);
return vfs
@@ -29,7 +31,32 @@ function main() {
.pipe(filter(f => !f.isDirectory()))
.pipe(productJsonFilter)
.pipe(buffer())
- .pipe(json(o => Object.assign({}, require('../product.json'), o)))
+ .pipe(json(o => {
+ const ossProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8'));
+ let builtInExtensions = ossProduct.builtInExtensions;
+
+ if (Array.isArray(o.builtInExtensions)) {
+ fancyLog(ansiColors.blue('[mixin]'), 'Overwriting built-in extensions:', o.builtInExtensions.map(e => e.name));
+
+ builtInExtensions = o.builtInExtensions;
+ } else if (o.builtInExtensions) {
+ const include = o.builtInExtensions['include'] || [];
+ const exclude = o.builtInExtensions['exclude'] || [];
+
+ fancyLog(ansiColors.blue('[mixin]'), 'OSS built-in extensions:', builtInExtensions.map(e => e.name));
+ fancyLog(ansiColors.blue('[mixin]'), 'Including built-in extensions:', include.map(e => e.name));
+ fancyLog(ansiColors.blue('[mixin]'), 'Excluding built-in extensions:', exclude);
+
+ builtInExtensions = builtInExtensions.filter(ext => !include.find(e => e.name === ext.name) && !exclude.find(name => name === ext.name));
+ builtInExtensions = [...builtInExtensions, ...include];
+
+ fancyLog(ansiColors.blue('[mixin]'), 'Final built-in extensions:', builtInExtensions.map(e => e.name));
+ } else {
+ fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name));
+ }
+
+ return { ...o, builtInExtensions };
+ }))
.pipe(productJsonFilter.restore)
.pipe(es.mapSync(function (f) {
fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎'));
@@ -38,4 +65,4 @@ function main() {
.pipe(vfs.dest('.'));
}
-main();
\ No newline at end of file
+main();
diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml
index a98b5f4f77e..7b6d2bcbbde 100644
--- a/build/azure-pipelines/product-build.yml
+++ b/build/azure-pipelines/product-build.yml
@@ -36,6 +36,17 @@ jobs:
steps:
- template: win32/product-build-win32.yml
+- job: WindowsARM64
+ condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true'))
+ pool:
+ vmImage: VS2017-Win2016
+ variables:
+ VSCODE_ARCH: arm64
+ dependsOn:
+ - Compile
+ steps:
+ - template: win32/product-build-win32-arm64.yml
+
- job: Linux
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true'))
pool:
diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml
index c3db41e80d5..db6524be03b 100644
--- a/build/azure-pipelines/product-compile.yml
+++ b/build/azure-pipelines/product-compile.yml
@@ -72,29 +72,6 @@ steps:
vstsFeed: 'npm-vscode'
condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
-- script: |
- set -e
- yarn generate-github-config
- displayName: Generate GitHub config
- env:
- OSS_GITHUB_ID: "a5d3c261b032765a78de"
- OSS_GITHUB_SECRET: $(oss-github-client-secret)
- INSIDERS_GITHUB_ID: "31f02627809389d9f111"
- INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret)
- STABLE_GITHUB_ID: "baa8a44b5e861d918709"
- STABLE_GITHUB_SECRET: $(stable-github-client-secret)
- EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea"
- EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret)
- VSO_GITHUB_ID: "3d4be8f37a0325b5817d"
- VSO_GITHUB_SECRET: $(vso-github-client-secret)
- VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492"
- VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret)
- VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c"
- VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret)
- GITHUB_APP_ID: "Iv1.ae51e546bef24ff1"
- GITHUB_APP_SECRET: $(github-app-client-secret)
- condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
-
- script: |
set -e
yarn postinstall
diff --git a/build/azure-pipelines/win32/product-build-win32-arm64.yml b/build/azure-pipelines/win32/product-build-win32-arm64.yml
new file mode 100644
index 00000000000..01be34aa9a8
--- /dev/null
+++ b/build/azure-pipelines/win32/product-build-win32-arm64.yml
@@ -0,0 +1,190 @@
+steps:
+- powershell: |
+ mkdir .build -ea 0
+ "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit
+ "$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality
+ displayName: Prepare cache flag
+
+- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
+ inputs:
+ keyfile: 'build/.cachesalt, .build/commit, .build/quality'
+ targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min'
+ vstsFeed: 'npm-vscode'
+ platformIndependent: true
+ alias: 'Compilation'
+
+- powershell: |
+ $ErrorActionPreference = "Stop"
+ exit 1
+ displayName: Check RestoreCache
+ condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'))
+
+- task: NodeTool@0
+ inputs:
+ versionSpec: "12.13.0"
+
+- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
+ inputs:
+ versionSpec: "1.x"
+
+- task: UsePythonVersion@0
+ inputs:
+ versionSpec: '2.x'
+ addToPath: true
+
+- task: AzureKeyVault@1
+ displayName: 'Azure Key Vault: Get Secrets'
+ inputs:
+ azureSubscription: 'vscode-builds-subscription'
+ KeyVaultName: vscode
+
+- powershell: |
+ . build/azure-pipelines/win32/exec.ps1
+ $ErrorActionPreference = "Stop"
+ "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII
+
+ exec { git config user.email "vscode@microsoft.com" }
+ exec { git config user.name "VSCode" }
+
+ mkdir .build -ea 0
+ "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch
+ displayName: Prepare tooling
+
+- powershell: |
+ . build/azure-pipelines/win32/exec.ps1
+ $ErrorActionPreference = "Stop"
+ exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" }
+ exec { git fetch distro }
+ exec { git merge $(node -p "require('./package.json').distro") }
+ displayName: Merge distro
+
+- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
+ inputs:
+ keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
+ targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
+ vstsFeed: 'npm-vscode'
+
+- powershell: |
+ . build/azure-pipelines/win32/exec.ps1
+ $ErrorActionPreference = "Stop"
+ $env:npm_config_arch="$(VSCODE_ARCH)"
+ $env:CHILD_CONCURRENCY="1"
+ exec { yarn --frozen-lockfile }
+ displayName: Install dependencies
+ condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
+
+- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
+ inputs:
+ keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
+ targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
+ vstsFeed: 'npm-vscode'
+ condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
+
+- powershell: |
+ . build/azure-pipelines/win32/exec.ps1
+ $ErrorActionPreference = "Stop"
+ exec { yarn postinstall }
+ displayName: Run postinstall scripts
+ condition: and(succeeded(), eq(variables['CacheRestored'], 'true'))
+
+- powershell: |
+ . build/azure-pipelines/win32/exec.ps1
+ $ErrorActionPreference = "Stop"
+ exec { node build/azure-pipelines/mixin }
+ displayName: Mix in quality
+
+- powershell: |
+ . build/azure-pipelines/win32/exec.ps1
+ $ErrorActionPreference = "Stop"
+ $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
+ exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" }
+ exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" }
+ exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" }
+ displayName: Build
+
+- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
+ inputs:
+ ConnectedServiceName: 'ESRP CodeSign'
+ FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)'
+ Pattern: '*.dll,*.exe,*.node'
+ signConfigType: inlineSignParams
+ inlineOperation: |
+ [
+ {
+ "keyCode": "CP-230012",
+ "operationSetCode": "SigntoolSign",
+ "parameters": [
+ {
+ "parameterName": "OpusName",
+ "parameterValue": "VS Code"
+ },
+ {
+ "parameterName": "OpusInfo",
+ "parameterValue": "https://code.visualstudio.com/"
+ },
+ {
+ "parameterName": "Append",
+ "parameterValue": "/as"
+ },
+ {
+ "parameterName": "FileDigest",
+ "parameterValue": "/fd \"SHA256\""
+ },
+ {
+ "parameterName": "PageHash",
+ "parameterValue": "/NPH"
+ },
+ {
+ "parameterName": "TimeStamp",
+ "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+ }
+ ],
+ "toolName": "sign",
+ "toolVersion": "1.0"
+ },
+ {
+ "keyCode": "CP-230012",
+ "operationSetCode": "SigntoolVerify",
+ "parameters": [
+ {
+ "parameterName": "VerifyAll",
+ "parameterValue": "/all"
+ }
+ ],
+ "toolName": "sign",
+ "toolVersion": "1.0"
+ }
+ ]
+ SessionTimeout: 120
+
+- task: NuGetCommand@2
+ displayName: Install ESRPClient.exe
+ inputs:
+ restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config'
+ feedsToUse: config
+ nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config'
+ externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b
+ restoreDirectory: packages
+
+- task: ESRPImportCertTask@1
+ displayName: Import ESRP Request Signing Certificate
+ inputs:
+ ESRP: 'ESRP CodeSign'
+
+- powershell: |
+ $ErrorActionPreference = "Stop"
+ .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key)
+ displayName: Import ESRP Auth Certificate
+
+- powershell: |
+ . build/azure-pipelines/win32/exec.ps1
+ $ErrorActionPreference = "Stop"
+ $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)"
+ $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)"
+ $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
+ .\build\azure-pipelines\win32\publish.ps1
+ displayName: Publish
+
+- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
+ displayName: 'Component Detection'
+ continueOnError: true
diff --git a/build/azure-pipelines/win32/publish.ps1 b/build/azure-pipelines/win32/publish.ps1
index ee98fc946ec..a225f9d5fdf 100644
--- a/build/azure-pipelines/win32/publish.ps1
+++ b/build/azure-pipelines/win32/publish.ps1
@@ -16,16 +16,21 @@ $ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip"
$Build = "$Root\VSCode-win32-$Arch"
# Create server archive
-exec { xcopy $LegacyServer $Server /H /E /I }
-exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
+if ("$Arch" -ne "arm64") {
+ exec { xcopy $LegacyServer $Server /H /E /I }
+ exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
+}
# get version
$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json
$Version = $PackageJson.version
-$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" }
+$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-$Arch" }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Zip }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $SystemExe }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $UserExe }
-exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip }
+
+if ("$Arch" -ne "arm64") {
+ exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip }
+}
diff --git a/build/builtin/main.js b/build/builtin/main.js
index b094a67cac5..93cef8cbdad 100644
--- a/build/builtin/main.js
+++ b/build/builtin/main.js
@@ -10,11 +10,11 @@ const path = require('path');
let window = null;
app.once('ready', () => {
- window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true } });
+ window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true, enableWebSQL: false } });
window.setMenuBarVisibility(false);
window.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true }));
// window.webContents.openDevTools();
window.once('closed', () => window = null);
});
-app.on('window-all-closed', () => app.quit());
\ No newline at end of file
+app.on('window-all-closed', () => app.quit());
diff --git a/build/darwin/sign.js b/build/darwin/sign.js
new file mode 100644
index 00000000000..b8eb9fc7525
--- /dev/null
+++ b/build/darwin/sign.js
@@ -0,0 +1,61 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+'use strict';
+Object.defineProperty(exports, "__esModule", { value: true });
+const codesign = require("electron-osx-sign");
+const path = require("path");
+const util = require("../lib/util");
+const product = require("../../product.json");
+async function main() {
+ const buildDir = process.env['AGENT_BUILDDIRECTORY'];
+ const tempDir = process.env['AGENT_TEMPDIRECTORY'];
+ if (!buildDir) {
+ throw new Error('$AGENT_BUILDDIRECTORY not set');
+ }
+ if (!tempDir) {
+ throw new Error('$AGENT_TEMPDIRECTORY not set');
+ }
+ const baseDir = path.dirname(__dirname);
+ const appRoot = path.join(buildDir, 'VSCode-darwin');
+ const appName = product.nameLong + '.app';
+ const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks');
+ const helperAppBaseName = product.nameShort;
+ const gpuHelperAppName = helperAppBaseName + ' Helper (GPU).app';
+ const pluginHelperAppName = helperAppBaseName + ' Helper (Plugin).app';
+ const rendererHelperAppName = helperAppBaseName + ' Helper (Renderer).app';
+ const defaultOpts = {
+ app: path.join(appRoot, appName),
+ platform: 'darwin',
+ entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
+ 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
+ hardenedRuntime: true,
+ 'pre-auto-entitlements': false,
+ 'pre-embed-provisioning-profile': false,
+ keychain: path.join(tempDir, 'buildagent.keychain'),
+ version: util.getElectronVersion(),
+ identity: '99FM488X57',
+ 'gatekeeper-assess': false
+ };
+ const appOpts = Object.assign(Object.assign({}, defaultOpts), {
+ // TODO(deepak1556): Incorrectly declared type in electron-osx-sign
+ ignore: (filePath) => {
+ return filePath.includes(gpuHelperAppName) ||
+ filePath.includes(pluginHelperAppName) ||
+ filePath.includes(rendererHelperAppName);
+ } });
+ const gpuHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, gpuHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist') });
+ const pluginHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, pluginHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist') });
+ const rendererHelperOpts = Object.assign(Object.assign({}, defaultOpts), { app: path.join(appFrameworkPath, rendererHelperAppName), entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist') });
+ await codesign.signAsync(gpuHelperOpts);
+ await codesign.signAsync(pluginHelperOpts);
+ await codesign.signAsync(rendererHelperOpts);
+ await codesign.signAsync(appOpts);
+}
+if (require.main === module) {
+ main().catch(err => {
+ console.error(err);
+ process.exit(1);
+ });
+}
diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts
new file mode 100644
index 00000000000..ee5d2eeb17b
--- /dev/null
+++ b/build/darwin/sign.ts
@@ -0,0 +1,90 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import * as codesign from 'electron-osx-sign';
+import * as path from 'path';
+import * as util from '../lib/util';
+import * as product from '../../product.json';
+
+async function main(): Promise {
+ const buildDir = process.env['AGENT_BUILDDIRECTORY'];
+ const tempDir = process.env['AGENT_TEMPDIRECTORY'];
+
+ if (!buildDir) {
+ throw new Error('$AGENT_BUILDDIRECTORY not set');
+ }
+
+ if (!tempDir) {
+ throw new Error('$AGENT_TEMPDIRECTORY not set');
+ }
+
+ const baseDir = path.dirname(__dirname);
+ const appRoot = path.join(buildDir, 'VSCode-darwin');
+ const appName = product.nameLong + '.app';
+ const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks');
+ const helperAppBaseName = product.nameShort;
+ const gpuHelperAppName = helperAppBaseName + ' Helper (GPU).app';
+ const pluginHelperAppName = helperAppBaseName + ' Helper (Plugin).app';
+ const rendererHelperAppName = helperAppBaseName + ' Helper (Renderer).app';
+
+ const defaultOpts: codesign.SignOptions = {
+ app: path.join(appRoot, appName),
+ platform: 'darwin',
+ entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
+ 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'),
+ hardenedRuntime: true,
+ 'pre-auto-entitlements': false,
+ 'pre-embed-provisioning-profile': false,
+ keychain: path.join(tempDir, 'buildagent.keychain'),
+ version: util.getElectronVersion(),
+ identity: '99FM488X57',
+ 'gatekeeper-assess': false
+ };
+
+ const appOpts = {
+ ...defaultOpts,
+ // TODO(deepak1556): Incorrectly declared type in electron-osx-sign
+ ignore: (filePath: string) => {
+ return filePath.includes(gpuHelperAppName) ||
+ filePath.includes(pluginHelperAppName) ||
+ filePath.includes(rendererHelperAppName);
+ }
+ };
+
+ const gpuHelperOpts: codesign.SignOptions = {
+ ...defaultOpts,
+ app: path.join(appFrameworkPath, gpuHelperAppName),
+ entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'),
+ 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'),
+ };
+
+ const pluginHelperOpts: codesign.SignOptions = {
+ ...defaultOpts,
+ app: path.join(appFrameworkPath, pluginHelperAppName),
+ entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'),
+ 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'),
+ };
+
+ const rendererHelperOpts: codesign.SignOptions = {
+ ...defaultOpts,
+ app: path.join(appFrameworkPath, rendererHelperAppName),
+ entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'),
+ 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'),
+ };
+
+ await codesign.signAsync(gpuHelperOpts);
+ await codesign.signAsync(pluginHelperOpts);
+ await codesign.signAsync(rendererHelperOpts);
+ await codesign.signAsync(appOpts as any);
+}
+
+if (require.main === module) {
+ main().catch(err => {
+ console.error(err);
+ process.exit(1);
+ });
+}
diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js
index 75c8413ae5d..0e2285bad00 100644
--- a/build/gulpfile.hygiene.js
+++ b/build/gulpfile.hygiene.js
@@ -59,6 +59,7 @@ const indentationFilter = [
// except specific folders
'!test/automation/out/**',
'!test/smoke/out/**',
+ '!extensions/typescript-language-features/test-workspace/**',
'!extensions/vscode-api-tests/testWorkspace/**',
'!extensions/vscode-api-tests/testWorkspace2/**',
'!build/monaco/**',
@@ -84,7 +85,7 @@ const indentationFilter = [
'!src/typings/**/*.d.ts',
'!extensions/**/*.d.ts',
'!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}',
- '!build/{lib,download}/**/*.js',
+ '!build/{lib,download,darwin}/**/*.js',
'!build/**/*.sh',
'!build/azure-pipelines/**/*.js',
'!build/azure-pipelines/**/*.config',
diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js
index 25b41bd3a91..5881bc66e91 100644
--- a/build/gulpfile.vscode.js
+++ b/build/gulpfile.vscode.js
@@ -66,6 +66,7 @@ const vscodeResources = [
'out-build/vs/base/node/languagePacks.js',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}',
'out-build/vs/base/browser/ui/codicons/codicon/**',
+ 'out-build/vs/base/parts/sandbox/electron-browser/preload.js',
'out-build/vs/workbench/browser/media/*-theme.css',
'out-build/vs/workbench/contrib/debug/**/*.json',
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
@@ -154,6 +155,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
const out = sourceFolderName;
const checksums = computeChecksums(out, [
+ 'vs/base/parts/sandbox/electron-browser/preload.js',
'vs/workbench/workbench.desktop.main.js',
'vs/workbench/workbench.desktop.main.css',
'vs/workbench/services/extensions/node/extensionHostProcess.js',
diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js
index b0956371451..d659b15d94b 100644
--- a/build/gulpfile.vscode.win32.js
+++ b/build/gulpfile.vscode.win32.js
@@ -65,6 +65,7 @@ function buildWin32Setup(arch, target) {
return cb => {
const ia32AppId = target === 'system' ? product.win32AppId : product.win32UserAppId;
const x64AppId = target === 'system' ? product.win32x64AppId : product.win32x64UserAppId;
+ const arm64AppId = target === 'system' ? product.win32arm64AppId : product.win32arm64UserAppId;
const sourcePath = buildPath(arch);
const outputPath = setupDir(arch, target);
@@ -88,12 +89,12 @@ function buildWin32Setup(arch, target) {
ShellNameShort: product.win32ShellNameShort,
AppMutex: product.win32MutexName,
Arch: arch,
- AppId: arch === 'ia32' ? ia32AppId : x64AppId,
- IncompatibleTargetAppId: arch === 'ia32' ? product.win32AppId : product.win32x64AppId,
- IncompatibleArchAppId: arch === 'ia32' ? x64AppId : ia32AppId,
+ AppId: { 'ia32': ia32AppId, 'x64': x64AppId, 'arm64': arm64AppId }[arch],
+ IncompatibleTargetAppId: { 'ia32': product.win32AppId, 'x64': product.win32x64AppId, 'arm64': product.win32arm64AppId }[arch],
+ IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch],
AppUserId: product.win32AppUserModelId,
- ArchitecturesAllowed: arch === 'ia32' ? '' : 'x64',
- ArchitecturesInstallIn64BitMode: arch === 'ia32' ? '' : 'x64',
+ ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch],
+ ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch],
SourceDir: sourcePath,
RepoDir: repoPath,
OutputDir: outputPath,
@@ -112,8 +113,10 @@ function defineWin32SetupTasks(arch, target) {
defineWin32SetupTasks('ia32', 'system');
defineWin32SetupTasks('x64', 'system');
+defineWin32SetupTasks('arm64', 'system');
defineWin32SetupTasks('ia32', 'user');
defineWin32SetupTasks('x64', 'user');
+defineWin32SetupTasks('arm64', 'user');
function archiveWin32Setup(arch) {
return cb => {
@@ -145,6 +148,7 @@ function updateIcon(executablePath) {
gulp.task(task.define('vscode-win32-ia32-inno-updater', task.series(copyInnoUpdater('ia32'), updateIcon(path.join(buildPath('ia32'), 'tools', 'inno_updater.exe')))));
gulp.task(task.define('vscode-win32-x64-inno-updater', task.series(copyInnoUpdater('x64'), updateIcon(path.join(buildPath('x64'), 'tools', 'inno_updater.exe')))));
+gulp.task(task.define('vscode-win32-arm64-inno-updater', task.series(copyInnoUpdater('arm64'), updateIcon(path.join(buildPath('arm64'), 'tools', 'inno_updater.exe')))));
// CodeHelper.exe icon
diff --git a/build/lib/electron.js b/build/lib/electron.js
index abf6baab419..bb71f14c12d 100644
--- a/build/lib/electron.js
+++ b/build/lib/electron.js
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
-exports.config = exports.getElectronVersion = void 0;
+exports.config = void 0;
const fs = require("fs");
const path = require("path");
const vfs = require("vinyl-fs");
@@ -16,12 +16,6 @@ const electron = require('gulp-atom-electron');
const root = path.dirname(path.dirname(__dirname));
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
const commit = util.getVersion(root);
-function getElectronVersion() {
- const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
- const target = /^target "(.*)"$/m.exec(yarnrc)[1];
- return target;
-}
-exports.getElectronVersion = getElectronVersion;
const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8'));
function darwinBundleDocumentType(extensions, icon) {
return {
@@ -33,7 +27,7 @@ function darwinBundleDocumentType(extensions, icon) {
};
}
exports.config = {
- version: getElectronVersion(),
+ version: util.getElectronVersion(),
productAppName: product.nameLong,
companyName: 'Microsoft Corporation',
copyright: 'Copyright (C) 2019 Microsoft. All rights reserved',
@@ -70,7 +64,7 @@ exports.config = {
darwinBundleDocumentType(["vue"], 'resources/darwin/vue.icns'),
darwinBundleDocumentType(["ascx", "csproj", "dtd", "wxi", "wxl", "wxs", "xml", "xaml"], 'resources/darwin/xml.icns'),
darwinBundleDocumentType(["eyaml", "eyml", "yaml", "yml"], 'resources/darwin/yaml.icns'),
- darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
+ darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "containerfile", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
],
darwinBundleURLTypes: [{
role: 'Viewer',
@@ -100,7 +94,7 @@ function getElectron(arch) {
};
}
async function main(arch = process.arch) {
- const version = getElectronVersion();
+ const version = util.getElectronVersion();
const electronPath = path.join(root, '.build', 'electron');
const versionFile = path.join(electronPath, 'version');
const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`;
diff --git a/build/lib/electron.ts b/build/lib/electron.ts
index 86c7afcf312..e0beca78079 100644
--- a/build/lib/electron.ts
+++ b/build/lib/electron.ts
@@ -19,12 +19,6 @@ const root = path.dirname(path.dirname(__dirname));
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
const commit = util.getVersion(root);
-export function getElectronVersion(): string {
- const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
- const target = /^target "(.*)"$/m.exec(yarnrc)![1];
- return target;
-}
-
const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8'));
function darwinBundleDocumentType(extensions: string[], icon: string) {
@@ -38,7 +32,7 @@ function darwinBundleDocumentType(extensions: string[], icon: string) {
}
export const config = {
- version: getElectronVersion(),
+ version: util.getElectronVersion(),
productAppName: product.nameLong,
companyName: 'Microsoft Corporation',
copyright: 'Copyright (C) 2019 Microsoft. All rights reserved',
@@ -75,7 +69,7 @@ export const config = {
darwinBundleDocumentType(["vue"], 'resources/darwin/vue.icns'),
darwinBundleDocumentType(["ascx", "csproj", "dtd", "wxi", "wxl", "wxs", "xml", "xaml"], 'resources/darwin/xml.icns'),
darwinBundleDocumentType(["eyaml", "eyml", "yaml", "yml"], 'resources/darwin/yaml.icns'),
- darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
+ darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "containerfile", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
],
darwinBundleURLTypes: [{
role: 'Viewer',
@@ -108,7 +102,7 @@ function getElectron(arch: string): () => NodeJS.ReadWriteStream {
}
async function main(arch = process.arch): Promise {
- const version = getElectronVersion();
+ const version = util.getElectronVersion();
const electronPath = path.join(root, '.build', 'electron');
const versionFile = path.join(electronPath, 'version');
const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`;
diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json
index 930b13edabe..93a6992e411 100644
--- a/build/lib/i18n.resources.json
+++ b/build/lib/i18n.resources.json
@@ -342,6 +342,10 @@
"name": "vs/workbench/services/userDataSync",
"project": "vscode-workbench"
},
+ {
+ "name": "vs/workbench/services/views",
+ "project": "vscode-workbench"
+ },
{
"name": "vs/workbench/contrib/timeline",
"project": "vscode-workbench"
diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js
index 663769dacea..f3cc2252b5e 100644
--- a/build/lib/layersChecker.js
+++ b/build/lib/layersChecker.js
@@ -130,6 +130,14 @@ const RULES = [
'lib.dom.d.ts' // no DOM
]
},
+ // Electron (sandbox)
+ {
+ target: '**/vs/**/electron-sandbox/**',
+ allowedTypes: CORE_TYPES,
+ disallowedDefinitions: [
+ '@types/node' // no node.js
+ ]
+ },
// Electron (renderer): skip
{
target: '**/vs/**/electron-browser/**',
diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts
index 0dcedb8d4e3..508d2b986e9 100644
--- a/build/lib/layersChecker.ts
+++ b/build/lib/layersChecker.ts
@@ -143,6 +143,15 @@ const RULES = [
]
},
+ // Electron (sandbox)
+ {
+ target: '**/vs/**/electron-sandbox/**',
+ allowedTypes: CORE_TYPES,
+ disallowedDefinitions: [
+ '@types/node' // no node.js
+ ]
+ },
+
// Electron (renderer): skip
{
target: '**/vs/**/electron-browser/**',
diff --git a/build/lib/util.js b/build/lib/util.js
index d42670e67a5..8b70b534b20 100644
--- a/build/lib/util.js
+++ b/build/lib/util.js
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
-exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.incremental = void 0;
+exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.incremental = void 0;
const es = require("event-stream");
const debounce = require("debounce");
const _filter = require("gulp-filter");
@@ -14,6 +14,7 @@ const fs = require("fs");
const _rimraf = require("rimraf");
const git = require("./git");
const VinylFile = require("vinyl");
+const root = path.dirname(path.dirname(__dirname));
const NoCancellationToken = { isCancellationRequested: () => false };
function incremental(streamProvider, initial, supportsCancellation) {
const input = es.through();
@@ -255,3 +256,9 @@ function streamToPromise(stream) {
});
}
exports.streamToPromise = streamToPromise;
+function getElectronVersion() {
+ const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
+ const target = /^target "(.*)"$/m.exec(yarnrc)[1];
+ return target;
+}
+exports.getElectronVersion = getElectronVersion;
diff --git a/build/lib/util.ts b/build/lib/util.ts
index 45b6c9e1b82..e379b47d8ed 100644
--- a/build/lib/util.ts
+++ b/build/lib/util.ts
@@ -18,6 +18,8 @@ import * as VinylFile from 'vinyl';
import { ThroughStream } from 'through';
import * as sm from 'source-map';
+const root = path.dirname(path.dirname(__dirname));
+
export interface ICancellationToken {
isCancellationRequested(): boolean;
}
@@ -318,3 +320,9 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise {
stream.on('end', () => c());
});
}
+
+export function getElectronVersion(): string {
+ const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
+ const target = /^target "(.*)"$/m.exec(yarnrc)![1];
+ return target;
+}
diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js
index 7a2320d8289..7b8b710636a 100644
--- a/build/npm/postinstall.js
+++ b/build/npm/postinstall.js
@@ -33,9 +33,10 @@ function yarnInstall(location, opts) {
yarnInstall('extensions'); // node modules shared by all extensions
-yarnInstall('remote'); // node modules used by vscode server
-
-yarnInstall('remote/web'); // node modules used by vscode web
+if (!(process.platform === 'win32' && process.env['npm_config_arch'] === 'arm64')) {
+ yarnInstall('remote'); // node modules used by vscode server
+ yarnInstall('remote/web'); // node modules used by vscode web
+}
const allExtensionFolders = fs.readdirSync('extensions');
const extensions = allExtensionFolders.filter(e => {
diff --git a/build/package.json b/build/package.json
index ae605126424..ecff6c7a182 100644
--- a/build/package.json
+++ b/build/package.json
@@ -33,6 +33,7 @@
"@typescript-eslint/parser": "^2.12.0",
"applicationinsights": "1.0.8",
"azure-storage": "^2.1.0",
+ "electron-osx-sign": "^0.4.16",
"github-releases": "^0.4.1",
"gulp-bom": "^1.0.0",
"gulp-sourcemaps": "^1.11.0",
@@ -43,7 +44,7 @@
"minimist": "^1.2.3",
"request": "^2.85.0",
"terser": "4.3.8",
- "typescript": "^3.9.1-rc",
+ "typescript": "^3.9.3",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.5.4",
"xml2js": "^0.4.17"
diff --git a/build/win32/code.iss b/build/win32/code.iss
index 15293a0c5cf..49b2f255ce3 100644
--- a/build/win32/code.iss
+++ b/build/win32/code.iss
@@ -238,6 +238,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.confi
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\config.ico"; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.config\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
+Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
+Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.containerfile\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.containerfile"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
+Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Containerfile}"; Flags: uninsdeletekey; Tasks: associatewithfiles
+Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles
+Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles
+Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.containerfile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles
+
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cpp\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.cpp"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cpp"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,C++}"; Flags: uninsdeletekey; Tasks: associatewithfiles
@@ -1011,7 +1018,7 @@ begin
#endif
#if "user" == InstallTarget
- #if "ia32" == Arch
+ #if "ia32" == Arch || "arm64" == Arch
#define IncompatibleArchRootKey "HKLM32"
#else
#define IncompatibleArchRootKey "HKLM64"
diff --git a/build/yarn.lock b/build/yarn.lock
index 62326638698..8f615cb4e6c 100644
--- a/build/yarn.lock
+++ b/build/yarn.lock
@@ -557,6 +557,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+base64-js@^1.2.3:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+ integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+
bcrypt-pbkdf@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
@@ -569,6 +574,11 @@ beeper@^1.0.0:
resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809"
integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=
+bluebird@^3.5.0:
+ version "3.7.2"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
+ integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
+
boolbase@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
@@ -615,11 +625,29 @@ browserify-mime@~1.2.9:
resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f"
integrity sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8=
+buffer-alloc-unsafe@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
+ integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
+
+buffer-alloc@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
+ integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
+ dependencies:
+ buffer-alloc-unsafe "^1.1.0"
+ buffer-fill "^1.0.0"
+
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
+buffer-fill@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
+ integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
+
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@@ -717,6 +745,11 @@ commander@^2.8.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
+compare-version@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080"
+ integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=
+
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -805,7 +838,7 @@ debug-fabulous@0.0.X:
lazy-debug-legacy "0.0.X"
object-assign "4.1.0"
-debug@2.X:
+debug@2.X, debug@^2.6.8:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -918,6 +951,18 @@ ecc-jsbn@~0.1.1:
dependencies:
jsbn "~0.1.0"
+electron-osx-sign@^0.4.16:
+ version "0.4.16"
+ resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.16.tgz#0be8e579b2d9fa4c12d2a21f063898294b3434aa"
+ integrity sha512-ziMWfc3NmQlwnWLW6EaZq8nH2BWVng/atX5GWsGwhexJYpdW6hsg//MkAfRTRx1kR3Veiqkeiog1ibkbA4x0rg==
+ dependencies:
+ bluebird "^3.5.0"
+ compare-version "^0.1.2"
+ debug "^2.6.8"
+ isbinaryfile "^3.0.2"
+ minimist "^1.2.0"
+ plist "^3.0.1"
+
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -1488,6 +1533,13 @@ isarray@~1.0.0:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+isbinaryfile@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80"
+ integrity sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==
+ dependencies:
+ buffer-alloc "^1.2.0"
+
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -1969,6 +2021,15 @@ picomatch@^2.0.5:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6"
integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==
+plist@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c"
+ integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==
+ dependencies:
+ base64-js "^1.2.3"
+ xmlbuilder "^9.0.7"
+ xmldom "0.1.x"
+
prettyjson@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289"
@@ -2458,10 +2519,10 @@ typescript@^3.0.1:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
-typescript@^3.9.1-rc:
- version "3.9.1-rc"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.1-rc.tgz#81d5a5a0a597e224b6e2af8dffb46524b2eaf5f3"
- integrity sha512-+cPv8L2Vd4KidCotqi2wjegBZ5n47CDRUu/QiLVu2YbeXAz78hIfcai9ziBiNI6JTGTVwUqXRug2UZxDcxhvFw==
+typescript@^3.9.3:
+ version "3.9.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a"
+ integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==
typical@^4.0.0:
version "4.0.0"
@@ -2661,11 +2722,21 @@ xmlbuilder@0.4.3:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.4.3.tgz#c4614ba74e0ad196e609c9272cd9e1ddb28a8a58"
integrity sha1-xGFLp04K0ZbmCcknLNnh3bKKilg=
+xmlbuilder@^9.0.7:
+ version "9.0.7"
+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
+ integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
+
xmlbuilder@~9.0.1:
version "9.0.4"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f"
integrity sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=
+xmldom@0.1.x:
+ version "0.1.31"
+ resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
+ integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
+
xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
diff --git a/cgmanifest.json b/cgmanifest.json
index 09478ca403e..f1ce13c7205 100644
--- a/cgmanifest.json
+++ b/cgmanifest.json
@@ -60,12 +60,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
- "commitHash": "0552e0d5de46ffa3b481d741f1db5c779e201565"
+ "commitHash": "8f502de1dc5b6df4218a900d0857de7a40301d98"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
- "version": "7.2.4"
+ "version": "7.3.0"
},
{
"component": {
diff --git a/extensions/bat/test/colorize-results/test_bat.json b/extensions/bat/test/colorize-results/test_bat.json
index 97155fafc8b..eb76b0e1d04 100644
--- a/extensions/bat/test/colorize-results/test_bat.json
+++ b/extensions/bat/test/colorize-results/test_bat.json
@@ -488,7 +488,7 @@
"t": "source.batchfile constant.character.escape.batchfile",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "constant.character: #569CD6"
@@ -510,7 +510,7 @@
"t": "source.batchfile constant.character.escape.batchfile",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "constant.character: #569CD6"
diff --git a/extensions/coffeescript/test/colorize-results/test_coffee.json b/extensions/coffeescript/test/colorize-results/test_coffee.json
index 7c72176431d..9647307f073 100644
--- a/extensions/coffeescript/test/colorize-results/test_coffee.json
+++ b/extensions/coffeescript/test/colorize-results/test_coffee.json
@@ -1555,7 +1555,7 @@
"t": "source.coffee string.regexp.multiline.coffee keyword.control.anchor.regexp",
"r": {
"dark_plus": "keyword.control.anchor.regexp: #DCDCAA",
- "light_plus": "keyword.control.anchor.regexp: #FF0000",
+ "light_plus": "keyword.control.anchor.regexp: #EE0000",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
@@ -1605,4 +1605,4 @@
"hc_black": "string.regexp: #D16969"
}
}
-]
+]
\ No newline at end of file
diff --git a/extensions/cpp/test/colorize-results/test-78769_cpp.json b/extensions/cpp/test/colorize-results/test-78769_cpp.json
index eb114a4f007..82846880603 100644
--- a/extensions/cpp/test/colorize-results/test-78769_cpp.json
+++ b/extensions/cpp/test/colorize-results/test-78769_cpp.json
@@ -191,7 +191,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -257,7 +257,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -389,7 +389,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -433,7 +433,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -532,7 +532,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -587,7 +587,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -719,7 +719,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -763,7 +763,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -862,7 +862,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -906,7 +906,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -1027,7 +1027,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -1071,7 +1071,7 @@
"t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "meta.preprocessor: #569CD6",
"light_vs": "meta.preprocessor: #0000FF",
"hc_black": "constant.character: #569CD6"
@@ -1187,4 +1187,4 @@
"hc_black": "meta.preprocessor: #569CD6"
}
}
-]
+]
\ No newline at end of file
diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json
index 108895f8f65..10f6041bb00 100644
--- a/extensions/cpp/test/colorize-results/test_cpp.json
+++ b/extensions/cpp/test/colorize-results/test_cpp.json
@@ -2314,7 +2314,7 @@
"t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.character.escape.cpp",
"r": {
"dark_plus": "constant.character.escape: #D7BA7D",
- "light_plus": "constant.character.escape: #FF0000",
+ "light_plus": "constant.character.escape: #EE0000",
"dark_vs": "string: #CE9178",
"light_vs": "string: #A31515",
"hc_black": "constant.character: #569CD6"
@@ -2430,4 +2430,4 @@
"hc_black": "default: #FFFFFF"
}
}
-]
+]
\ No newline at end of file
diff --git a/extensions/css-language-features/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts
new file mode 100644
index 00000000000..8b1d7205fcd
--- /dev/null
+++ b/extensions/css-language-features/client/src/browser/cssClientMain.ts
@@ -0,0 +1,32 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ExtensionContext } from 'vscode';
+import { LanguageClientOptions } from 'vscode-languageclient';
+import { startClient, LanguageClientConstructor } from '../cssClient';
+import { LanguageClient } from 'vscode-languageclient/browser';
+
+declare const Worker: {
+ new(stringUrl: string): any;
+};
+declare const TextDecoder: {
+ new(encoding?: string): { decode(buffer: ArrayBuffer): string; };
+};
+
+// this method is called when vs code is activated
+export function activate(context: ExtensionContext) {
+ const serverMain = context.asAbsolutePath('server/dist/browser/cssServerMain.js');
+ try {
+ const worker = new Worker(serverMain);
+ const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
+ return new LanguageClient(id, name, clientOptions, worker);
+ };
+
+ startClient(context, newLanguageClient, { TextDecoder });
+
+ } catch (e) {
+ console.log(e);
+ }
+}
diff --git a/extensions/css-language-features/client/src/browser/vscodeNlsShim.ts b/extensions/css-language-features/client/src/browser/vscodeNlsShim.ts
new file mode 100644
index 00000000000..b4943fe0ee1
--- /dev/null
+++ b/extensions/css-language-features/client/src/browser/vscodeNlsShim.ts
@@ -0,0 +1,46 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+export interface Options {
+ locale?: string;
+ cacheLanguageResolution?: boolean;
+}
+export interface LocalizeInfo {
+ key: string;
+ comment: string[];
+}
+export interface LocalizeFunc {
+ (info: LocalizeInfo, message: string, ...args: any[]): string;
+ (key: string, message: string, ...args: any[]): string;
+}
+export interface LoadFunc {
+ (file?: string): LocalizeFunc;
+}
+
+function format(message: string, args: any[]): string {
+ let result: string;
+
+ if (args.length === 0) {
+ result = message;
+ } else {
+ result = message.replace(/\{(\d+)\}/g, (match, rest) => {
+ let index = rest[0];
+ return typeof args[index] !== 'undefined' ? args[index] : match;
+ });
+ }
+ return result;
+}
+
+function localize(_key: string | LocalizeInfo, message: string, ...args: any[]): string {
+ return format(message, args);
+}
+
+export function loadMessageBundle(_file?: string): LocalizeFunc {
+ return localize;
+}
+
+export function config(_opt?: Options | string): LoadFunc {
+ return loadMessageBundle;
+}
diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssClient.ts
similarity index 76%
rename from extensions/css-language-features/client/src/cssMain.ts
rename to extensions/css-language-features/client/src/cssClient.ts
index beabfc68cad..4bfa95c8439 100644
--- a/extensions/css-language-features/client/src/cssMain.ts
+++ b/extensions/css-language-features/client/src/cssClient.ts
@@ -3,38 +3,31 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import * as fs from 'fs';
-import * as path from 'path';
-import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, workspace, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode';
-import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, ProvideCompletionItemsSignature } from 'vscode-languageclient';
+import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList } from 'vscode';
+import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, CommonLanguageClient } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
-import { getCustomDataPathsFromAllExtensions, getCustomDataPathsInAllWorkspaces } from './customData';
+import { getCustomDataSource } from './customData';
+import { RequestService, serveFileSystemRequests } from './requests';
+
+namespace CustomDataChangedNotification {
+ export const type: NotificationType = new NotificationType('css/customDataChanged');
+}
const localize = nls.loadMessageBundle();
-// this method is called when vs code is activated
-export function activate(context: ExtensionContext) {
+export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient;
- let serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main;
- let serverModule = context.asAbsolutePath(path.join('server', serverMain));
+export interface Runtime {
+ TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string; } };
+ fs?: RequestService;
+}
- // The debug options for the server
- let debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] };
+export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) {
- // If the extension is launch in debug mode the debug server options are use
- // Otherwise the run options are used
- let serverOptions: ServerOptions = {
- run: { module: serverModule, transport: TransportKind.ipc },
- debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
- };
+ const customDataSource = getCustomDataSource(context.subscriptions);
let documentSelector = ['css', 'scss', 'less'];
- let dataPaths = [
- ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders),
- ...getCustomDataPathsFromAllExtensions()
- ];
-
// Options to control the language client
let clientOptions: LanguageClientOptions = {
documentSelector,
@@ -42,7 +35,7 @@ export function activate(context: ExtensionContext) {
configurationSection: ['css', 'scss', 'less']
},
initializationOptions: {
- dataPaths
+ handledSchemas: ['file']
},
middleware: {
provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult {
@@ -82,8 +75,17 @@ export function activate(context: ExtensionContext) {
};
// Create the language client and start the client.
- let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions);
+ let client = newLanguageClient('css', localize('cssserver.name', 'CSS Language Server'), clientOptions);
client.registerProposedFeatures();
+ client.onReady().then(() => {
+
+ client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
+ customDataSource.onDidChange(() => {
+ client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
+ });
+
+ serveFileSystemRequests(client, runtime);
+ });
let disposable = client.start();
// Push the disposable to the context's subscriptions so that the
@@ -118,7 +120,7 @@ export function activate(context: ExtensionContext) {
const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/;
return languages.registerCompletionItemProvider(documentSelector, {
- provideCompletionItems(doc, pos) {
+ provideCompletionItems(doc: TextDocument, pos: Position) {
let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos));
let match = lineUntilPos.match(regionCompletionRegExpr);
if (match) {
@@ -162,13 +164,3 @@ export function activate(context: ExtensionContext) {
}
}
}
-
-function readJSONFile(location: string) {
- try {
- return JSON.parse(fs.readFileSync(location).toString());
- } catch (e) {
- console.log(`Problems reading ${location}: ${e}`);
- return {};
- }
-}
-
diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts
index 92d9030a343..50540528d63 100644
--- a/extensions/css-language-features/client/src/customData.ts
+++ b/extensions/css-language-features/client/src/customData.ts
@@ -3,42 +3,78 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import * as path from 'path';
-import { workspace, WorkspaceFolder, extensions } from 'vscode';
+import { workspace, extensions, Uri, EventEmitter, Disposable } from 'vscode';
+import { resolvePath, joinPath } from './requests';
-interface ExperimentalConfig {
- customData?: string[];
- experimental?: {
- customData?: string[];
+export function getCustomDataSource(toDispose: Disposable[]) {
+ let pathsInWorkspace = getCustomDataPathsInAllWorkspaces();
+ let pathsInExtensions = getCustomDataPathsFromAllExtensions();
+
+ const onChange = new EventEmitter();
+
+ toDispose.push(extensions.onDidChange(_ => {
+ const newPathsInExtensions = getCustomDataPathsFromAllExtensions();
+ if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) {
+ pathsInExtensions = newPathsInExtensions;
+ onChange.fire();
+ }
+ }));
+ toDispose.push(workspace.onDidChangeConfiguration(e => {
+ if (e.affectsConfiguration('css.customData')) {
+ pathsInWorkspace = getCustomDataPathsInAllWorkspaces();
+ onChange.fire();
+ }
+ }));
+
+ return {
+ get uris() {
+ return pathsInWorkspace.concat(pathsInExtensions);
+ },
+ get onDidChange() {
+ return onChange.event;
+ }
};
}
-export function getCustomDataPathsInAllWorkspaces(workspaceFolders: readonly WorkspaceFolder[] | undefined): string[] {
+
+function getCustomDataPathsInAllWorkspaces(): string[] {
+ const workspaceFolders = workspace.workspaceFolders;
+
const dataPaths: string[] = [];
if (!workspaceFolders) {
return dataPaths;
}
- workspaceFolders.forEach(wf => {
- const allCssConfig = workspace.getConfiguration(undefined, wf.uri);
- const wfCSSConfig = allCssConfig.inspect('css');
- if (wfCSSConfig && wfCSSConfig.workspaceFolderValue && wfCSSConfig.workspaceFolderValue.customData) {
- const customData = wfCSSConfig.workspaceFolderValue.customData;
- if (Array.isArray(customData)) {
- customData.forEach(t => {
- if (typeof t === 'string') {
- dataPaths.push(path.resolve(wf.uri.fsPath, t));
- }
- });
+ const collect = (paths: string[] | undefined, rootFolder: Uri) => {
+ if (Array.isArray(paths)) {
+ for (const path of paths) {
+ if (typeof path === 'string') {
+ dataPaths.push(resolvePath(rootFolder, path).toString());
+ }
}
}
- });
+ };
+ for (let i = 0; i < workspaceFolders.length; i++) {
+ const folderUri = workspaceFolders[i].uri;
+ const allCssConfig = workspace.getConfiguration('css', folderUri);
+ const customDataInspect = allCssConfig.inspect('customData');
+ if (customDataInspect) {
+ collect(customDataInspect.workspaceFolderValue, folderUri);
+ if (i === 0) {
+ if (workspace.workspaceFile) {
+ collect(customDataInspect.workspaceValue, workspace.workspaceFile);
+ }
+ collect(customDataInspect.globalValue, folderUri);
+ }
+ }
+
+ }
return dataPaths;
}
-export function getCustomDataPathsFromAllExtensions(): string[] {
+function getCustomDataPathsFromAllExtensions(): string[] {
const dataPaths: string[] = [];
for (const extension of extensions.all) {
@@ -47,7 +83,7 @@ export function getCustomDataPathsFromAllExtensions(): string[] {
if (contributes && contributes.css && contributes.css.customData && Array.isArray(contributes.css.customData)) {
const relativePaths: string[] = contributes.css.customData;
relativePaths.forEach(rp => {
- dataPaths.push(path.resolve(extension.extensionPath, rp));
+ dataPaths.push(joinPath(extension.extensionUri, rp).toString());
});
}
}
diff --git a/extensions/css-language-features/client/src/node/cssClientMain.ts b/extensions/css-language-features/client/src/node/cssClientMain.ts
new file mode 100644
index 00000000000..350e0de02d5
--- /dev/null
+++ b/extensions/css-language-features/client/src/node/cssClientMain.ts
@@ -0,0 +1,38 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { getNodeFSRequestService } from './nodeFs';
+import { ExtensionContext, extensions } from 'vscode';
+import { startClient, LanguageClientConstructor } from '../cssClient';
+import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
+import { TextDecoder } from 'util';
+
+// this method is called when vs code is activated
+export function activate(context: ExtensionContext) {
+
+ const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main;
+ const serverMain = clientMain?.replace('client', 'server').replace('cssClientMain', 'cssServerMain');
+ if (!serverMain) {
+ throw new Error('Unable to compute CSS server module path. Client: ' + clientMain);
+ }
+
+ const serverModule = context.asAbsolutePath(serverMain);
+
+ // The debug options for the server
+ const debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] };
+
+ // If the extension is launch in debug mode the debug server options are use
+ // Otherwise the run options are used
+ const serverOptions: ServerOptions = {
+ run: { module: serverModule, transport: TransportKind.ipc },
+ debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
+ };
+
+ const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
+ return new LanguageClient(id, name, serverOptions, clientOptions);
+ };
+
+ startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder });
+}
diff --git a/extensions/css-language-features/client/src/node/nodeFs.ts b/extensions/css-language-features/client/src/node/nodeFs.ts
new file mode 100644
index 00000000000..c13ef2e1c08
--- /dev/null
+++ b/extensions/css-language-features/client/src/node/nodeFs.ts
@@ -0,0 +1,85 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as fs from 'fs';
+import { Uri } from 'vscode';
+import { getScheme, RequestService, FileType } from '../requests';
+
+export function getNodeFSRequestService(): RequestService {
+ function ensureFileUri(location: string) {
+ if (getScheme(location) !== 'file') {
+ throw new Error('fileRequestService can only handle file URLs');
+ }
+ }
+ return {
+ getContent(location: string, encoding?: string) {
+ ensureFileUri(location);
+ return new Promise((c, e) => {
+ const uri = Uri.parse(location);
+ fs.readFile(uri.fsPath, encoding, (err, buf) => {
+ if (err) {
+ return e(err);
+ }
+ c(buf.toString());
+
+ });
+ });
+ },
+ stat(location: string) {
+ ensureFileUri(location);
+ return new Promise((c, e) => {
+ const uri = Uri.parse(location);
+ fs.stat(uri.fsPath, (err, stats) => {
+ if (err) {
+ if (err.code === 'ENOENT') {
+ return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 });
+ } else {
+ return e(err);
+ }
+ }
+
+ let type = FileType.Unknown;
+ if (stats.isFile()) {
+ type = FileType.File;
+ } else if (stats.isDirectory()) {
+ type = FileType.Directory;
+ } else if (stats.isSymbolicLink()) {
+ type = FileType.SymbolicLink;
+ }
+
+ c({
+ type,
+ ctime: stats.ctime.getTime(),
+ mtime: stats.mtime.getTime(),
+ size: stats.size
+ });
+ });
+ });
+ },
+ readDirectory(location: string) {
+ ensureFileUri(location);
+ return new Promise((c, e) => {
+ const path = Uri.parse(location).fsPath;
+
+ fs.readdir(path, { withFileTypes: true }, (err, children) => {
+ if (err) {
+ return e(err);
+ }
+ c(children.map(stat => {
+ if (stat.isSymbolicLink()) {
+ return [stat.name, FileType.SymbolicLink];
+ } else if (stat.isDirectory()) {
+ return [stat.name, FileType.Directory];
+ } else if (stat.isFile()) {
+ return [stat.name, FileType.File];
+ } else {
+ return [stat.name, FileType.Unknown];
+ }
+ }));
+ });
+ });
+ }
+ };
+}
diff --git a/extensions/css-language-features/client/src/requests.ts b/extensions/css-language-features/client/src/requests.ts
new file mode 100644
index 00000000000..1b1e70b2d88
--- /dev/null
+++ b/extensions/css-language-features/client/src/requests.ts
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Uri, workspace } from 'vscode';
+import { RequestType, CommonLanguageClient } from 'vscode-languageclient';
+import { Runtime } from './cssClient';
+
+export namespace FsContentRequest {
+ export const type: RequestType<{ uri: string; encoding?: string; }, string, any, any> = new RequestType('fs/content');
+}
+export namespace FsStatRequest {
+ export const type: RequestType = new RequestType('fs/stat');
+}
+
+export namespace FsReadDirRequest {
+ export const type: RequestType = new RequestType('fs/readDir');
+}
+
+export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) {
+ client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string; }) => {
+ const uri = Uri.parse(param.uri);
+ if (uri.scheme === 'file' && runtime.fs) {
+ return runtime.fs.getContent(param.uri);
+ }
+ return workspace.fs.readFile(uri).then(buffer => {
+ return new runtime.TextDecoder(param.encoding).decode(buffer);
+ });
+ });
+ client.onRequest(FsReadDirRequest.type, (uriString: string) => {
+ const uri = Uri.parse(uriString);
+ if (uri.scheme === 'file' && runtime.fs) {
+ return runtime.fs.readDirectory(uriString);
+ }
+ return workspace.fs.readDirectory(uri);
+ });
+ client.onRequest(FsStatRequest.type, (uriString: string) => {
+ const uri = Uri.parse(uriString);
+ if (uri.scheme === 'file' && runtime.fs) {
+ return runtime.fs.stat(uriString);
+ }
+ return workspace.fs.stat(uri);
+ });
+}
+
+export enum FileType {
+ /**
+ * The file type is unknown.
+ */
+ Unknown = 0,
+ /**
+ * A regular file.
+ */
+ File = 1,
+ /**
+ * A directory.
+ */
+ Directory = 2,
+ /**
+ * A symbolic link to a file.
+ */
+ SymbolicLink = 64
+}
+export interface FileStat {
+ /**
+ * The type of the file, e.g. is a regular file, a directory, or symbolic link
+ * to a file.
+ */
+ type: FileType;
+ /**
+ * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
+ */
+ ctime: number;
+ /**
+ * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
+ */
+ mtime: number;
+ /**
+ * The size in bytes.
+ */
+ size: number;
+}
+
+export interface RequestService {
+ getContent(uri: string, encoding?: string): Promise;
+
+ stat(uri: string): Promise;
+ readDirectory(uri: string): Promise<[string, FileType][]>;
+}
+
+export function getScheme(uri: string) {
+ return uri.substr(0, uri.indexOf(':'));
+}
+
+export function dirname(uri: string) {
+ const lastIndexOfSlash = uri.lastIndexOf('/');
+ return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : '';
+}
+
+export function basename(uri: string) {
+ const lastIndexOfSlash = uri.lastIndexOf('/');
+ return uri.substr(lastIndexOfSlash + 1);
+}
+
+const Slash = '/'.charCodeAt(0);
+const Dot = '.'.charCodeAt(0);
+
+export function isAbsolutePath(path: string) {
+ return path.charCodeAt(0) === Slash;
+}
+
+export function resolvePath(uri: Uri, path: string): Uri {
+ if (isAbsolutePath(path)) {
+ return uri.with({ path: normalizePath(path.split('/')) });
+ }
+ return joinPath(uri, path);
+}
+
+export function normalizePath(parts: string[]): string {
+ const newParts: string[] = [];
+ for (const part of parts) {
+ if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) {
+ // ignore
+ } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) {
+ newParts.pop();
+ } else {
+ newParts.push(part);
+ }
+ }
+ if (parts.length > 1 && parts[parts.length - 1].length === 0) {
+ newParts.push('');
+ }
+ let res = newParts.join('/');
+ if (parts[0].length === 0) {
+ res = '/' + res;
+ }
+ return res;
+}
+
+
+export function joinPath(uri: Uri, ...paths: string[]): Uri {
+ const parts = uri.path.split('/');
+ for (let path of paths) {
+ parts.push(...path.split('/'));
+ }
+ return uri.with({ path: normalizePath(parts) });
+}
diff --git a/extensions/css-language-features/extension-browser.webpack.config.js b/extensions/css-language-features/extension-browser.webpack.config.js
new file mode 100644
index 00000000000..0cf30ec5dc0
--- /dev/null
+++ b/extensions/css-language-features/extension-browser.webpack.config.js
@@ -0,0 +1,48 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+//@ts-check
+
+'use strict';
+
+const withDefaults = require('../shared.webpack.config');
+const path = require('path');
+const webpack = require('webpack');
+
+const vscodeNlsReplacement = new webpack.NormalModuleReplacementPlugin(
+ /vscode\-nls\/lib\/main\.js/,
+ path.join(__dirname, 'client/out/browser/vscodeNlsShim.js')
+);
+
+const clientConfig = withDefaults({
+ target: 'webworker',
+ context: path.join(__dirname, 'client'),
+ entry: {
+ extension: './src/browser/cssClientMain.ts'
+ },
+ output: {
+ filename: 'cssClientMain.js',
+ path: path.join(__dirname, 'client', 'dist', 'browser')
+ }
+});
+clientConfig.plugins[1] = vscodeNlsReplacement; // replace nls bundler
+clientConfig.module.rules[0].use.shift(); // remove nls loader
+
+const serverConfig = withDefaults({
+ target: 'webworker',
+ context: path.join(__dirname, 'server'),
+ entry: {
+ extension: './src/browser/cssServerMain.ts',
+ },
+ output: {
+ filename: 'cssServerMain.js',
+ path: path.join(__dirname, 'server', 'dist', 'browser'),
+ libraryTarget: 'var'
+ }
+});
+serverConfig.plugins[1] = vscodeNlsReplacement; // replace nls bundler
+serverConfig.module.rules[0].use.shift(); // remove nls loader
+
+module.exports = [clientConfig, serverConfig];
diff --git a/extensions/css-language-features/extension.webpack.config.js b/extensions/css-language-features/extension.webpack.config.js
index dec7ad5afb4..a931210ab32 100644
--- a/extensions/css-language-features/extension.webpack.config.js
+++ b/extensions/css-language-features/extension.webpack.config.js
@@ -13,10 +13,10 @@ const path = require('path');
module.exports = withDefaults({
context: path.join(__dirname, 'client'),
entry: {
- extension: './src/cssMain.ts',
+ extension: './src/node/cssClientMain.ts',
},
output: {
- filename: 'cssMain.js',
- path: path.join(__dirname, 'client', 'dist')
+ filename: 'cssClientMain.js',
+ path: path.join(__dirname, 'client', 'dist', 'node')
}
});
diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json
index 4c737601679..4d44ee6d7d5 100644
--- a/extensions/css-language-features/package.json
+++ b/extensions/css-language-features/package.json
@@ -15,7 +15,8 @@
"onLanguage:scss",
"onCommand:_css.applyCodeAction"
],
- "main": "./client/out/cssMain",
+ "main": "./client/out/node/cssClientMain",
+ "browser": "./client/dist/browser/cssClientMain",
"enableProposedApi": true,
"scripts": {
"compile": "gulp compile-extension:css-language-features-client compile-extension:css-language-features-server",
@@ -806,7 +807,7 @@
]
},
"dependencies": {
- "vscode-languageclient": "^6.1.3",
+ "vscode-languageclient": "7.0.0-next.4",
"vscode-nls": "^4.1.2"
},
"devDependencies": {
diff --git a/extensions/css-language-features/server/extension.webpack.config.js b/extensions/css-language-features/server/extension.webpack.config.js
index 68b850b3773..531035f636c 100644
--- a/extensions/css-language-features/server/extension.webpack.config.js
+++ b/extensions/css-language-features/server/extension.webpack.config.js
@@ -13,10 +13,10 @@ const path = require('path');
module.exports = withDefaults({
context: path.join(__dirname),
entry: {
- extension: './src/cssServerMain.ts',
+ extension: './src/node/cssServerMain.ts',
},
output: {
filename: 'cssServerMain.js',
- path: path.join(__dirname, 'dist')
+ path: path.join(__dirname, 'dist', 'node'),
}
});
diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json
index c142ff1e620..247b276dace 100644
--- a/extensions/css-language-features/server/package.json
+++ b/extensions/css-language-features/server/package.json
@@ -7,10 +7,12 @@
"engines": {
"node": "*"
},
- "main": "./out/cssServerMain",
+ "main": "./out/node/cssServerMain",
+ "browser": "./dist/browser/cssServerMain",
"dependencies": {
- "vscode-css-languageservice": "^4.1.2",
- "vscode-languageserver": "^6.1.1"
+ "vscode-css-languageservice": "4.3.0-next.2",
+ "vscode-languageserver": "7.0.0-next.3",
+ "vscode-uri": "^2.1.2"
},
"devDependencies": {
"@types/mocha": "7.0.2",
@@ -27,6 +29,6 @@
"install-service-local": "npm install ../../../../vscode-css-languageservice -f",
"install-server-next": "yarn add vscode-languageserver@next",
"install-server-local": "npm install ../../../../vscode-languageserver-node/server -f",
- "test": "../../../node_modules/.bin/mocha"
+ "test": "node ./test/index.js"
}
}
diff --git a/extensions/css-language-features/server/src/browser/cssServerMain.ts b/extensions/css-language-features/server/src/browser/cssServerMain.ts
new file mode 100644
index 00000000000..13284fadcd9
--- /dev/null
+++ b/extensions/css-language-features/server/src/browser/cssServerMain.ts
@@ -0,0 +1,16 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { createConnection, BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver/browser';
+import { startServer } from '../cssServer';
+
+declare let self: any;
+
+const messageReader = new BrowserMessageReader(self);
+const messageWriter = new BrowserMessageWriter(self);
+
+const connection = createConnection(messageReader, messageWriter);
+
+startServer(connection, {});
diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts
new file mode 100644
index 00000000000..42a88c9a5dc
--- /dev/null
+++ b/extensions/css-language-features/server/src/cssServer.ts
@@ -0,0 +1,373 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import {
+ Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType
+} from 'vscode-languageserver/lib/common/api';
+import { URI } from 'vscode-uri';
+import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position } from 'vscode-css-languageservice';
+import { getLanguageModelCache } from './languageModelCache';
+import { formatError, runSafeAsync } from './utils/runner';
+import { getDocumentContext } from './utils/documentContext';
+import { fetchDataProviders } from './customData';
+import { RequestService, getRequestService } from './requests';
+
+namespace CustomDataChangedNotification {
+ export const type: NotificationType = new NotificationType('css/customDataChanged');
+}
+
+export interface Settings {
+ css: LanguageSettings;
+ less: LanguageSettings;
+ scss: LanguageSettings;
+}
+
+export interface RuntimeEnvironment {
+ file?: RequestService;
+ http?: RequestService
+}
+
+export function startServer(connection: Connection, runtime: RuntimeEnvironment) {
+
+ // Create a text document manager.
+ const documents = new TextDocuments(TextDocument);
+ // Make the text document manager listen on the connection
+ // for open, change and close text document events
+ documents.listen(connection);
+
+ const stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document));
+ documents.onDidClose(e => {
+ stylesheets.onDocumentRemoved(e.document);
+ });
+ connection.onShutdown(() => {
+ stylesheets.dispose();
+ });
+
+ let scopedSettingsSupport = false;
+ let foldingRangeLimit = Number.MAX_VALUE;
+ let workspaceFolders: WorkspaceFolder[];
+
+ let dataProvidersReady: Promise = Promise.resolve();
+
+ const languageServices: { [id: string]: LanguageService } = {};
+
+ const notReady = () => Promise.reject('Not Ready');
+ let requestService: RequestService = { getContent: notReady, stat: notReady, readDirectory: notReady };
+
+ // 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 => {
+ workspaceFolders = (params).workspaceFolders;
+ if (!Array.isArray(workspaceFolders)) {
+ workspaceFolders = [];
+ if (params.rootPath) {
+ workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() });
+ }
+ }
+
+ requestService = getRequestService(params.initializationOptions.handledSchemas || ['file'], connection, runtime);
+
+ function getClientCapability(name: string, def: T) {
+ const keys = name.split('.');
+ let c: any = params.capabilities;
+ for (let i = 0; c && i < keys.length; i++) {
+ if (!c.hasOwnProperty(keys[i])) {
+ return def;
+ }
+ c = c[keys[i]];
+ }
+ return c;
+ }
+ const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
+ scopedSettingsSupport = !!getClientCapability('workspace.configuration', false);
+ foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', 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 capabilities: ServerCapabilities = {
+ textDocumentSync: TextDocumentSyncKind.Incremental,
+ completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined,
+ hoverProvider: true,
+ documentSymbolProvider: true,
+ referencesProvider: true,
+ definitionProvider: true,
+ documentHighlightProvider: true,
+ documentLinkProvider: {
+ resolveProvider: false
+ },
+ codeActionProvider: true,
+ renameProvider: true,
+ colorProvider: {},
+ foldingRangeProvider: true,
+ selectionRangeProvider: true
+ };
+ return { capabilities };
+ });
+
+ function getLanguageService(document: TextDocument) {
+ let service = languageServices[document.languageId];
+ if (!service) {
+ connection.console.log('Document type is ' + document.languageId + ', using css instead.');
+ service = languageServices['css'];
+ }
+ return service;
+ }
+
+ let documentSettings: { [key: string]: Thenable } = {};
+ // remove document settings on close
+ documents.onDidClose(e => {
+ delete documentSettings[e.document.uri];
+ });
+ function getDocumentSettings(textDocument: TextDocument): Thenable {
+ if (scopedSettingsSupport) {
+ 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]);
+ documentSettings[textDocument.uri] = promise;
+ }
+ return promise;
+ }
+ return Promise.resolve(undefined);
+ }
+
+ // The settings have changed. Is send on server activation as well.
+ connection.onDidChangeConfiguration(change => {
+ updateConfiguration(change.settings);
+ });
+
+ function updateConfiguration(settings: Settings) {
+ for (const languageId in languageServices) {
+ languageServices[languageId].configure((settings as any)[languageId]);
+ }
+ // reset all document settings
+ documentSettings = {};
+ // Revalidate any open text documents
+ documents.all().forEach(triggerValidation);
+ }
+
+ const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {};
+ 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) {
+ clearTimeout(request);
+ delete pendingValidationRequests[textDocument.uri];
+ }
+ }
+
+ function triggerValidation(textDocument: TextDocument): void {
+ cleanPendingValidation(textDocument);
+ pendingValidationRequests[textDocument.uri] = setTimeout(() => {
+ delete pendingValidationRequests[textDocument.uri];
+ validateTextDocument(textDocument);
+ }, validationDelayMs);
+ }
+
+ function validateTextDocument(textDocument: TextDocument): void {
+ 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));
+ });
+ }
+
+
+ function updateDataProviders(dataPaths: string[]) {
+ dataProvidersReady = fetchDataProviders(dataPaths, requestService).then(customDataProviders => {
+ for (const lang in languageServices) {
+ languageServices[lang].setDataProviders(true, customDataProviders);
+ }
+ });
+ }
+
+ connection.onCompletion((textDocumentPosition, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(textDocumentPosition.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const styleSheet = stylesheets.get(document);
+ const documentContext = getDocumentContext(document.uri, workspaceFolders);
+ return getLanguageService(document).doComplete2(document, textDocumentPosition.position, styleSheet, documentContext);
+ }
+ return null;
+ }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
+ });
+
+ connection.onHover((textDocumentPosition, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(textDocumentPosition.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const styleSheet = stylesheets.get(document);
+ return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet);
+ }
+ return null;
+ }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token);
+ });
+
+ connection.onDocumentSymbol((documentSymbolParams, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(documentSymbolParams.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).findDocumentSymbols(document, stylesheet);
+ }
+ return [];
+ }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
+ });
+
+ connection.onDefinition((documentDefinitionParams, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(documentDefinitionParams.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet);
+ }
+ return null;
+ }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token);
+ });
+
+ connection.onDocumentHighlight((documentHighlightParams, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(documentHighlightParams.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet);
+ }
+ return [];
+ }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token);
+ });
+
+
+ connection.onDocumentLinks(async (documentLinkParams, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(documentLinkParams.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const documentContext = getDocumentContext(document.uri, workspaceFolders);
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext);
+ }
+ return [];
+ }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token);
+ });
+
+
+ connection.onReferences((referenceParams, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(referenceParams.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet);
+ }
+ return [];
+ }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token);
+ });
+
+ connection.onCodeAction((codeActionParams, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(codeActionParams.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet);
+ }
+ return [];
+ }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token);
+ });
+
+ connection.onDocumentColor((params, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(params.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).findDocumentColors(document, stylesheet);
+ }
+ return [];
+ }, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
+ });
+
+ connection.onColorPresentation((params, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(params.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range);
+ }
+ return [];
+ }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
+ });
+
+ connection.onRenameRequest((renameParameters, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(renameParameters.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet);
+ }
+ return null;
+ }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token);
+ });
+
+ connection.onFoldingRanges((params, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(params.textDocument.uri);
+ if (document) {
+ await dataProvidersReady;
+ return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit });
+ }
+ return null;
+ }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
+ });
+
+ connection.onSelectionRanges((params, token) => {
+ return runSafeAsync(async () => {
+ const document = documents.get(params.textDocument.uri);
+ const positions: Position[] = params.positions;
+
+ if (document) {
+ await dataProvidersReady;
+ const stylesheet = stylesheets.get(document);
+ return getLanguageService(document).getSelectionRanges(document, positions, stylesheet);
+ }
+ return [];
+ }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
+ });
+
+ connection.onNotification(CustomDataChangedNotification.type, updateDataProviders);
+
+ // Listen on the connection
+ connection.listen();
+
+}
+
+
diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts
deleted file mode 100644
index 6a9db0b77e7..00000000000
--- a/extensions/css-language-features/server/src/cssServerMain.ts
+++ /dev/null
@@ -1,390 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import {
- createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind
-} from 'vscode-languageserver';
-import { URI } from 'vscode-uri';
-import { stat as fsStat } from 'fs';
-import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, FileSystemProvider, FileType, TextDocument, CompletionList, Position } from 'vscode-css-languageservice';
-import { getLanguageModelCache } from './languageModelCache';
-import { getPathCompletionParticipant } from './pathCompletion';
-import { formatError, runSafe, runSafeAsync } from './utils/runner';
-import { getDocumentContext } from './utils/documentContext';
-import { getDataProviders } from './customData';
-
-export interface Settings {
- css: LanguageSettings;
- less: LanguageSettings;
- scss: LanguageSettings;
-}
-
-// Create a connection for the server.
-const connection: IConnection = createConnection();
-
-console.log = connection.console.log.bind(connection.console);
-console.error = connection.console.error.bind(connection.console);
-
-process.on('unhandledRejection', (e: any) => {
- connection.console.error(formatError(`Unhandled exception`, e));
-});
-
-// Create a text document manager.
-const documents = new TextDocuments(TextDocument);
-// Make the text document manager listen on the connection
-// for open, change and close text document events
-documents.listen(connection);
-
-const stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document));
-documents.onDidClose(e => {
- stylesheets.onDocumentRemoved(e.document);
-});
-connection.onShutdown(() => {
- stylesheets.dispose();
-});
-
-let scopedSettingsSupport = false;
-let foldingRangeLimit = Number.MAX_VALUE;
-let workspaceFolders: WorkspaceFolder[];
-
-const languageServices: { [id: string]: LanguageService } = {};
-
-const fileSystemProvider: FileSystemProvider = {
- stat(documentUri: string) {
- const filePath = URI.parse(documentUri).fsPath;
-
- return new Promise((c, e) => {
- fsStat(filePath, (err, stats) => {
- if (err) {
- if (err.code === 'ENOENT') {
- return c({
- type: FileType.Unknown,
- ctime: -1,
- mtime: -1,
- size: -1
- });
- } else {
- return e(err);
- }
- }
-
- let type = FileType.Unknown;
- if (stats.isFile()) {
- type = FileType.File;
- } else if (stats.isDirectory()) {
- type = FileType.Directory;
- } else if (stats.isSymbolicLink()) {
- type = FileType.SymbolicLink;
- }
-
- c({
- type,
- ctime: stats.ctime.getTime(),
- mtime: stats.mtime.getTime(),
- size: stats.size
- });
- });
- });
- }
-};
-
-// 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 => {
- workspaceFolders = (params).workspaceFolders;
- if (!Array.isArray(workspaceFolders)) {
- workspaceFolders = [];
- if (params.rootPath) {
- workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() });
- }
- }
-
- const dataPaths: string[] = params.initializationOptions.dataPaths || [];
- const customDataProviders = getDataProviders(dataPaths);
-
- function getClientCapability(name: string, def: T) {
- const keys = name.split('.');
- let c: any = params.capabilities;
- for (let i = 0; c && i < keys.length; i++) {
- if (!c.hasOwnProperty(keys[i])) {
- return def;
- }
- c = c[keys[i]];
- }
- return c;
- }
- const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false);
- scopedSettingsSupport = !!getClientCapability('workspace.configuration', false);
- foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE);
-
- languageServices.css = getCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities });
- languageServices.scss = getSCSSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities });
- languageServices.less = getLESSLanguageService({ customDataProviders, fileSystemProvider, clientCapabilities: params.capabilities });
-
- const capabilities: ServerCapabilities = {
- textDocumentSync: TextDocumentSyncKind.Incremental,
- completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-'] } : undefined,
- hoverProvider: true,
- documentSymbolProvider: true,
- referencesProvider: true,
- definitionProvider: true,
- documentHighlightProvider: true,
- documentLinkProvider: {
- resolveProvider: false
- },
- codeActionProvider: true,
- renameProvider: true,
- colorProvider: {},
- foldingRangeProvider: true,
- selectionRangeProvider: true
- };
- return { capabilities };
-});
-
-function getLanguageService(document: TextDocument) {
- let service = languageServices[document.languageId];
- if (!service) {
- connection.console.log('Document type is ' + document.languageId + ', using css instead.');
- service = languageServices['css'];
- }
- return service;
-}
-
-let documentSettings: { [key: string]: Thenable } = {};
-// remove document settings on close
-documents.onDidClose(e => {
- delete documentSettings[e.document.uri];
-});
-function getDocumentSettings(textDocument: TextDocument): Thenable {
- if (scopedSettingsSupport) {
- 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]);
- documentSettings[textDocument.uri] = promise;
- }
- return promise;
- }
- return Promise.resolve(undefined);
-}
-
-// The settings have changed. Is send on server activation as well.
-connection.onDidChangeConfiguration(change => {
- updateConfiguration(change.settings);
-});
-
-function updateConfiguration(settings: Settings) {
- for (const languageId in languageServices) {
- languageServices[languageId].configure((settings as any)[languageId]);
- }
- // reset all document settings
- documentSettings = {};
- // Revalidate any open text documents
- documents.all().forEach(triggerValidation);
-}
-
-const pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {};
-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) {
- clearTimeout(request);
- delete pendingValidationRequests[textDocument.uri];
- }
-}
-
-function triggerValidation(textDocument: TextDocument): void {
- cleanPendingValidation(textDocument);
- pendingValidationRequests[textDocument.uri] = setTimeout(() => {
- delete pendingValidationRequests[textDocument.uri];
- validateTextDocument(textDocument);
- }, validationDelayMs);
-}
-
-function validateTextDocument(textDocument: TextDocument): void {
- const settingsPromise = getDocumentSettings(textDocument);
- settingsPromise.then(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));
- });
-}
-
-connection.onCompletion((textDocumentPosition, token) => {
- return runSafe(() => {
- const document = documents.get(textDocumentPosition.textDocument.uri);
- if (!document) {
- return null;
- }
- const cssLS = getLanguageService(document);
- const pathCompletionList: CompletionList = {
- isIncomplete: false,
- items: []
- };
- cssLS.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, pathCompletionList)]);
- const result = cssLS.doComplete(document, textDocumentPosition.position, stylesheets.get(document));
- return {
- isIncomplete: pathCompletionList.isIncomplete,
- items: [...pathCompletionList.items, ...result.items]
- };
- }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
-});
-
-connection.onHover((textDocumentPosition, token) => {
- return runSafe(() => {
- const document = documents.get(textDocumentPosition.textDocument.uri);
- if (document) {
- const styleSheet = stylesheets.get(document);
- return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet);
- }
- return null;
- }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token);
-});
-
-connection.onDocumentSymbol((documentSymbolParams, token) => {
- return runSafe(() => {
- const document = documents.get(documentSymbolParams.textDocument.uri);
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).findDocumentSymbols(document, stylesheet);
- }
- return [];
- }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
-});
-
-connection.onDefinition((documentDefinitionParams, token) => {
- return runSafe(() => {
- const document = documents.get(documentDefinitionParams.textDocument.uri);
- if (document) {
-
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet);
- }
- return null;
- }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token);
-});
-
-connection.onDocumentHighlight((documentHighlightParams, token) => {
- return runSafe(() => {
- const document = documents.get(documentHighlightParams.textDocument.uri);
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet);
- }
- return [];
- }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token);
-});
-
-
-connection.onDocumentLinks(async (documentLinkParams, token) => {
- return runSafeAsync(async () => {
- const document = documents.get(documentLinkParams.textDocument.uri);
- if (document) {
- const documentContext = getDocumentContext(document.uri, workspaceFolders);
- const stylesheet = stylesheets.get(document);
- return await getLanguageService(document).findDocumentLinks2(document, stylesheet, documentContext);
- }
- return [];
- }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token);
-});
-
-
-connection.onReferences((referenceParams, token) => {
- return runSafe(() => {
- const document = documents.get(referenceParams.textDocument.uri);
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet);
- }
- return [];
- }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token);
-});
-
-connection.onCodeAction((codeActionParams, token) => {
- return runSafe(() => {
- const document = documents.get(codeActionParams.textDocument.uri);
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet);
- }
- return [];
- }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token);
-});
-
-connection.onDocumentColor((params, token) => {
- return runSafe(() => {
- const document = documents.get(params.textDocument.uri);
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).findDocumentColors(document, stylesheet);
- }
- return [];
- }, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
-});
-
-connection.onColorPresentation((params, token) => {
- return runSafe(() => {
- const document = documents.get(params.textDocument.uri);
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range);
- }
- return [];
- }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
-});
-
-connection.onRenameRequest((renameParameters, token) => {
- return runSafe(() => {
- const document = documents.get(renameParameters.textDocument.uri);
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet);
- }
- return null;
- }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token);
-});
-
-connection.onFoldingRanges((params, token) => {
- return runSafe(() => {
- const document = documents.get(params.textDocument.uri);
- if (document) {
- return getLanguageService(document).getFoldingRanges(document, { rangeLimit: foldingRangeLimit });
- }
- return null;
- }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
-});
-
-connection.onSelectionRanges((params, token) => {
- return runSafe(() => {
- const document = documents.get(params.textDocument.uri);
- const positions: Position[] = params.positions;
-
- if (document) {
- const stylesheet = stylesheets.get(document);
- return getLanguageService(document).getSelectionRanges(document, positions, stylesheet);
- }
- return [];
- }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token);
-});
-
-
-// Listen on the connection
-connection.listen();
diff --git a/extensions/css-language-features/server/src/customData.ts b/extensions/css-language-features/server/src/customData.ts
index f173d884a2b..724dbf9de3f 100644
--- a/extensions/css-language-features/server/src/customData.ts
+++ b/extensions/css-language-features/server/src/customData.ts
@@ -3,48 +3,36 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { CSSDataV1, ICSSDataProvider } from 'vscode-css-languageservice';
-import * as fs from 'fs';
+import { ICSSDataProvider, newCSSDataProvider } from 'vscode-css-languageservice';
+import { RequestService } from './requests';
-export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] {
- const providers = dataPaths.map(p => {
- if (fs.existsSync(p)) {
- const data = parseCSSData(fs.readFileSync(p, 'utf-8'));
- return {
- provideProperties: () => data.properties || [],
- provideAtDirectives: () => data.atDirectives || [],
- providePseudoClasses: () => data.pseudoClasses || [],
- providePseudoElements: () => data.pseudoElements || []
- };
- } else {
- return {
- provideProperties: () => [],
- provideAtDirectives: () => [],
- providePseudoClasses: () => [],
- providePseudoElements: () => []
- };
+export function fetchDataProviders(dataPaths: string[], requestService: RequestService): Promise {
+ const providers = dataPaths.map(async p => {
+ try {
+ const content = await requestService.getContent(p);
+ return parseCSSData(content);
+ } catch (e) {
+ return newCSSDataProvider({ version: 1 });
}
});
- return providers;
+ return Promise.all(providers);
}
-function parseCSSData(source: string): CSSDataV1 {
+function parseCSSData(source: string): ICSSDataProvider {
let rawData: any;
try {
rawData = JSON.parse(source);
} catch (err) {
- return {
- version: 1
- };
+ return newCSSDataProvider({ version: 1 });
}
- return {
+ return newCSSDataProvider({
version: 1,
properties: rawData.properties || [],
atDirectives: rawData.atDirectives || [],
pseudoClasses: rawData.pseudoClasses || [],
pseudoElements: rawData.pseudoElements || []
- };
+ });
}
diff --git a/extensions/css-language-features/server/src/node/cssServerMain.ts b/extensions/css-language-features/server/src/node/cssServerMain.ts
new file mode 100644
index 00000000000..9e145398ff1
--- /dev/null
+++ b/extensions/css-language-features/server/src/node/cssServerMain.ts
@@ -0,0 +1,21 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { createConnection, Connection } from 'vscode-languageserver/node';
+import { formatError } from '../utils/runner';
+import { startServer } from '../cssServer';
+import { getNodeFSRequestService } from './nodeFs';
+
+// Create a connection for the server.
+const connection: Connection = createConnection();
+
+console.log = connection.console.log.bind(connection.console);
+console.error = connection.console.error.bind(connection.console);
+
+process.on('unhandledRejection', (e: any) => {
+ connection.console.error(formatError(`Unhandled exception`, e));
+});
+
+startServer(connection, { file: getNodeFSRequestService() });
diff --git a/extensions/css-language-features/server/src/node/nodeFs.ts b/extensions/css-language-features/server/src/node/nodeFs.ts
new file mode 100644
index 00000000000..c7b1301296d
--- /dev/null
+++ b/extensions/css-language-features/server/src/node/nodeFs.ts
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { RequestService, getScheme } from '../requests';
+import { URI as Uri } from 'vscode-uri';
+
+import * as fs from 'fs';
+import { FileType } from 'vscode-css-languageservice';
+
+export function getNodeFSRequestService(): RequestService {
+ function ensureFileUri(location: string) {
+ if (getScheme(location) !== 'file') {
+ throw new Error('fileRequestService can only handle file URLs');
+ }
+ }
+ return {
+ getContent(location: string, encoding?: string) {
+ ensureFileUri(location);
+ return new Promise((c, e) => {
+ const uri = Uri.parse(location);
+ fs.readFile(uri.fsPath, encoding, (err, buf) => {
+ if (err) {
+ return e(err);
+ }
+ c(buf.toString());
+
+ });
+ });
+ },
+ stat(location: string) {
+ ensureFileUri(location);
+ return new Promise((c, e) => {
+ const uri = Uri.parse(location);
+ fs.stat(uri.fsPath, (err, stats) => {
+ if (err) {
+ if (err.code === 'ENOENT') {
+ return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 });
+ } else {
+ return e(err);
+ }
+ }
+
+ let type = FileType.Unknown;
+ if (stats.isFile()) {
+ type = FileType.File;
+ } else if (stats.isDirectory()) {
+ type = FileType.Directory;
+ } else if (stats.isSymbolicLink()) {
+ type = FileType.SymbolicLink;
+ }
+
+ c({
+ type,
+ ctime: stats.ctime.getTime(),
+ mtime: stats.mtime.getTime(),
+ size: stats.size
+ });
+ });
+ });
+ },
+ readDirectory(location: string) {
+ ensureFileUri(location);
+ return new Promise((c, e) => {
+ const path = Uri.parse(location).fsPath;
+
+ fs.readdir(path, { withFileTypes: true }, (err, children) => {
+ if (err) {
+ return e(err);
+ }
+ c(children.map(stat => {
+ if (stat.isSymbolicLink()) {
+ return [stat.name, FileType.SymbolicLink];
+ } else if (stat.isDirectory()) {
+ return [stat.name, FileType.Directory];
+ } else if (stat.isFile()) {
+ return [stat.name, FileType.File];
+ } else {
+ return [stat.name, FileType.Unknown];
+ }
+ }));
+ });
+ });
+ }
+ };
+}
diff --git a/extensions/css-language-features/server/src/pathCompletion.ts b/extensions/css-language-features/server/src/pathCompletion.ts
deleted file mode 100644
index 6862f28e034..00000000000
--- a/extensions/css-language-features/server/src/pathCompletion.ts
+++ /dev/null
@@ -1,213 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as path from 'path';
-import * as fs from 'fs';
-import { URI } from 'vscode-uri';
-
-import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-languageserver-types';
-import { WorkspaceFolder } from 'vscode-languageserver';
-import { ICompletionParticipant } from 'vscode-css-languageservice';
-
-import { startsWith, endsWith } from './utils/strings';
-
-export function getPathCompletionParticipant(
- document: TextDocument,
- workspaceFolders: WorkspaceFolder[],
- result: CompletionList
-): ICompletionParticipant {
- return {
- onCssURILiteralValue: ({ position, range, uriValue }) => {
- const fullValue = stripQuotes(uriValue);
- if (!shouldDoPathCompletion(uriValue, workspaceFolders)) {
- if (fullValue === '.' || fullValue === '..') {
- result.isIncomplete = true;
- }
- return;
- }
-
- let suggestions = providePathSuggestions(uriValue, position, range, document, workspaceFolders);
- result.items = [...suggestions, ...result.items];
- },
- onCssImportPath: ({ position, range, pathValue }) => {
- const fullValue = stripQuotes(pathValue);
- if (!shouldDoPathCompletion(pathValue, workspaceFolders)) {
- if (fullValue === '.' || fullValue === '..') {
- result.isIncomplete = true;
- }
- return;
- }
-
- let suggestions = providePathSuggestions(pathValue, position, range, document, workspaceFolders);
-
- if (document.languageId === 'scss') {
- suggestions.forEach(s => {
- if (startsWith(s.label, '_') && endsWith(s.label, '.scss')) {
- if (s.textEdit) {
- s.textEdit.newText = s.label.slice(1, -5);
- } else {
- s.label = s.label.slice(1, -5);
- }
- }
- });
- }
-
- result.items = [...suggestions, ...result.items];
- }
- };
-}
-
-function providePathSuggestions(pathValue: string, position: Position, range: Range, document: TextDocument, workspaceFolders: WorkspaceFolder[]) {
- const fullValue = stripQuotes(pathValue);
- const isValueQuoted = startsWith(pathValue, `'`) || startsWith(pathValue, `"`);
- const valueBeforeCursor = isValueQuoted
- ? fullValue.slice(0, position.character - (range.start.character + 1))
- : fullValue.slice(0, position.character - range.start.character);
- const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
- const currentDocFsPath = URI.parse(document.uri).fsPath;
-
- const paths = providePaths(valueBeforeCursor, currentDocFsPath, workspaceRoot)
- .filter(p => {
- // Exclude current doc's path
- return path.resolve(currentDocFsPath, '../', p) !== currentDocFsPath;
- })
- .filter(p => {
- // Exclude paths that start with `.`
- return p[0] !== '.';
- });
-
- const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range;
- const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange);
-
- const suggestions = paths.map(p => pathToSuggestion(p, replaceRange));
- return suggestions;
-}
-
-function shouldDoPathCompletion(pathValue: string, workspaceFolders: WorkspaceFolder[]): boolean {
- const fullValue = stripQuotes(pathValue);
- if (fullValue === '.' || fullValue === '..') {
- return false;
- }
-
- if (!workspaceFolders || workspaceFolders.length === 0) {
- return false;
- }
-
- return true;
-}
-
-function stripQuotes(fullValue: string) {
- if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) {
- return fullValue.slice(1, -1);
- } else {
- return fullValue;
- }
-}
-
-/**
- * Get a list of path suggestions. Folder suggestions are suffixed with a slash.
- */
-function providePaths(valueBeforeCursor: string, activeDocFsPath: string, root?: string): string[] {
- const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/');
- const valueBeforeLastSlash = valueBeforeCursor.slice(0, lastIndexOfSlash + 1);
-
- const startsWithSlash = startsWith(valueBeforeCursor, '/');
- let parentDir: string;
- if (startsWithSlash) {
- if (!root) {
- return [];
- }
- parentDir = path.resolve(root, '.' + valueBeforeLastSlash);
- } else {
- parentDir = path.resolve(activeDocFsPath, '..', valueBeforeLastSlash);
- }
-
- try {
- return fs.readdirSync(parentDir).map(f => {
- return isDir(path.resolve(parentDir, f))
- ? f + '/'
- : f;
- });
- } catch (e) {
- return [];
- }
-}
-
-const isDir = (p: string) => {
- try {
- return fs.statSync(p).isDirectory();
- } catch (e) {
- return false;
- }
-};
-
-function pathToReplaceRange(valueBeforeCursor: string, fullValue: string, fullValueRange: Range) {
- let replaceRange: Range;
- const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/');
- if (lastIndexOfSlash === -1) {
- replaceRange = fullValueRange;
- } else {
- // For cases where cursor is in the middle of attribute value, like `);
- async function asWebviewUri(path: string) {
- const root = await webview.webview.asWebviewUri(vscode.Uri.file(vscode.workspace.rootPath!));
+ function asWebviewUri(path: string) {
+ const root = webview.webview.asWebviewUri(vscode.Uri.file(vscode.workspace.rootPath!));
return root.toString() + path;
}
{
- const imagePath = await asWebviewUri('/image.png');
+ const imagePath = asWebviewUri('/image.png');
const response = sendRecieveMessage(webview, { src: imagePath });
assert.strictEqual((await response).value, true);
}
{
- const imagePath = await asWebviewUri('/no-such-image.png');
+ const imagePath = asWebviewUri('/no-such-image.png');
const response = sendRecieveMessage(webview, { src: imagePath });
assert.strictEqual((await response).value, false);
}
{
- const imagePath = vscode.Uri.file(join(vscode.workspace.rootPath!, '..', '..', '..', 'resources', 'linux', 'code.png')).with({ scheme: 'vscode-resource' });
+ const imagePath = webview.webview.asWebviewUri(vscode.Uri.file(join(vscode.workspace.rootPath!, '..', '..', '..', 'resources', 'linux', 'code.png')));
const response = sendRecieveMessage(webview, { src: imagePath.toString() });
assert.strictEqual((await response).value, false);
}
@@ -292,18 +292,38 @@ suite('vscode API - webview', () => {
});
`);
- const workspaceRootUri = vscode.Uri.file(vscode.workspace.rootPath!).with({ scheme: 'vscode-resource' });
-
{
- const response = sendRecieveMessage(webview, { src: workspaceRootUri.toString() + '/sub/image.png' });
+ const response = sendRecieveMessage(webview, { src: webview.webview.asWebviewUri(vscode.Uri.file(join(vscode.workspace.rootPath!, 'sub', 'image.png'))).toString() });
assert.strictEqual((await response).value, true);
}
{
- const response = sendRecieveMessage(webview, { src: workspaceRootUri.toString() + '/image.png' });
+ const response = sendRecieveMessage(webview, { src: webview.webview.asWebviewUri(vscode.Uri.file(join(vscode.workspace.rootPath!, 'image.png'))).toString() });
assert.strictEqual((await response).value, false);
}
});
+ conditionalTest('webviews using hard-coded old style vscode-resource uri should work', async () => {
+ const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, {
+ enableScripts: true,
+ localResourceRoots: [vscode.Uri.file(join(vscode.workspace.rootPath!, 'sub'))]
+ }));
+
+ const imagePath = vscode.Uri.file(join(vscode.workspace.rootPath!, 'sub', 'image.png')).with({ scheme: 'vscode-resource' }).toString();
+
+ webview.webview.html = createHtmlDocumentWithBody(/*html*/`
+
+ `);
+
+ const firstResponse = getMesssage(webview);
+
+ assert.strictEqual((await firstResponse).value, true);
+ });
+
test('webviews should have real view column after they are created, #56097', async () => {
const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.Active }, { enableScripts: true }));
diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts
index 2a58c4630e0..ff6a2418f4d 100644
--- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts
+++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts
@@ -8,6 +8,7 @@ import * as vscode from 'vscode';
import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled } from '../utils';
import { join, posix, basename } from 'path';
import * as fs from 'fs';
+import { TestFS } from '../memfs';
suite('vscode API - workspace', () => {
@@ -163,6 +164,40 @@ suite('vscode API - workspace', () => {
});
});
+ test('openTextDocument, actual casing first', async function () {
+
+ const fs = new TestFS('this-fs', false);
+ const reg = vscode.workspace.registerFileSystemProvider(fs.scheme, fs, { isCaseSensitive: fs.isCaseSensitive });
+
+ let uriOne = vscode.Uri.parse('this-fs:/one');
+ let uriTwo = vscode.Uri.parse('this-fs:/two');
+ let uriONE = vscode.Uri.parse('this-fs:/ONE'); // same resource, different uri
+ let uriTWO = vscode.Uri.parse('this-fs:/TWO');
+
+ fs.writeFile(uriOne, Buffer.from('one'), { create: true, overwrite: true });
+ fs.writeFile(uriTwo, Buffer.from('two'), { create: true, overwrite: true });
+
+ // lower case (actual case) comes first
+ let docOne = await vscode.workspace.openTextDocument(uriOne);
+ assert.equal(docOne.uri.toString(), uriOne.toString());
+
+ let docONE = await vscode.workspace.openTextDocument(uriONE);
+ assert.equal(docONE === docOne, true);
+ assert.equal(docONE.uri.toString(), uriOne.toString());
+ assert.equal(docONE.uri.toString() !== uriONE.toString(), true); // yep
+
+ // upper case (NOT the actual case) comes first
+ let docTWO = await vscode.workspace.openTextDocument(uriTWO);
+ assert.equal(docTWO.uri.toString(), uriTWO.toString());
+
+ let docTwo = await vscode.workspace.openTextDocument(uriTwo);
+ assert.equal(docTWO === docTwo, true);
+ assert.equal(docTwo.uri.toString(), uriTWO.toString());
+ assert.equal(docTwo.uri.toString() !== uriTwo.toString(), true); // yep
+
+ reg.dispose();
+ });
+
test('eol, read', () => {
const a = createRandomFile('foo\nbar\nbar').then(file => {
return vscode.workspace.openTextDocument(file).then(doc => {
diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts
index e270cd73adf..3c34028feb4 100644
--- a/extensions/vscode-api-tests/src/utils.ts
+++ b/extensions/vscode-api-tests/src/utils.ts
@@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
-import { MemFS } from './memfs';
+import { TestFS } from './memfs';
import * as assert from 'assert';
export function rndName() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10);
}
-export const testFs = new MemFS();
-vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs);
+export const testFs = new TestFS('fake-fs', true);
+vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs, { isCaseSensitive: testFs.isCaseSensitive });
export async function createRandomFile(contents = '', dir: vscode.Uri | undefined = undefined, ext = ''): Promise {
let fakeFile: vscode.Uri;
diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json
index 8d7e92bd255..83f73669e0a 100644
--- a/extensions/vscode-notebook-tests/package.json
+++ b/extensions/vscode-notebook-tests/package.json
@@ -27,6 +27,12 @@
"mocha": "^2.3.3"
},
"contributes": {
+ "commands": [
+ {
+ "command": "vscode-notebook-tests.createNewNotebook",
+ "title": "Create New Notebook"
+ }
+ ],
"notebookProvider": [
{
"viewType": "notebookCoreTest",
@@ -37,6 +43,25 @@
"excludeFileNamePattern": ""
}
]
+ },
+ {
+ "viewType": "notebookSmokeTest",
+ "displayName": "Notebook Smoke Test",
+ "selector": [
+ {
+ "filenamePattern": "*.smoke-nb",
+ "excludeFileNamePattern": ""
+ }
+ ]
+ }
+ ],
+ "notebookOutputRenderer": [
+ {
+ "viewType": "notebookCoreTestRenderer",
+ "displayName": "Notebook Core Test Renderer",
+ "mimeTypes": [
+ "text/custom"
+ ]
}
]
}
diff --git a/extensions/vscode-notebook-tests/src/customRenderer.js b/extensions/vscode-notebook-tests/src/customRenderer.js
new file mode 100644
index 00000000000..75e2ec1eb7a
--- /dev/null
+++ b/extensions/vscode-notebook-tests/src/customRenderer.js
@@ -0,0 +1,13 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+const vscode = acquireVsCodeApi();
+
+vscode.postMessage({
+ type: 'custom_renderer_initialize',
+ payload: {
+ firstMessage: true
+ }
+});
diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts
index d512d5e0ff0..944b179a216 100644
--- a/extensions/vscode-notebook-tests/src/notebook.test.ts
+++ b/extensions/vscode-notebook-tests/src/notebook.test.ts
@@ -8,25 +8,282 @@ import * as assert from 'assert';
import * as vscode from 'vscode';
import { join } from 'path';
-function waitFor(ms: number): Promise {
- let resolveFunc: () => void;
-
- const promise = new Promise(resolve => {
- resolveFunc = resolve;
+export function timeoutAsync(n: number): Promise {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve();
+ }, n);
});
- setTimeout(() => {
- resolveFunc!();
- }, ms);
-
- return promise;
}
+export function once(event: vscode.Event): vscode.Event {
+ return (listener: any, thisArgs = null, disposables?: any) => {
+ // we need this, in case the event fires during the listener call
+ let didFire = false;
+ let result: vscode.Disposable;
+ result = event(e => {
+ if (didFire) {
+ return;
+ } else if (result) {
+ result.dispose();
+ } else {
+ didFire = true;
+ }
+
+ return listener.call(thisArgs, e);
+ }, null, disposables);
+
+ if (didFire) {
+ result.dispose();
+ }
+
+ return result;
+ };
+}
+
+async function getEventOncePromise(event: vscode.Event): Promise {
+ return new Promise((resolve, _reject) => {
+ once(event)((result: T) => resolve(result));
+ });
+}
+
+// Since `workbench.action.splitEditor` command does await properly
+// Notebook editor/document events are not guaranteed to be sent to the ext host when promise resolves
+// The workaround here is waiting for the first visible notebook editor change event.
+async function splitEditor() {
+ const once = getEventOncePromise(vscode.notebook.onDidChangeVisibleNotebookEditors);
+ await vscode.commands.executeCommand('workbench.action.splitEditor');
+ await once;
+}
+
+suite('API tests', () => {
+ test('document open/close event', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const firstDocumentOpen = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument);
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await firstDocumentOpen;
+
+ const firstDocumentClose = getEventOncePromise(vscode.notebook.onDidCloseNotebookDocument);
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ await firstDocumentClose;
+ });
+
+ test('shared document in notebook editors', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ let counter = 0;
+ const disposables: vscode.Disposable[] = [];
+ disposables.push(vscode.notebook.onDidOpenNotebookDocument(() => {
+ counter++;
+ }));
+ disposables.push(vscode.notebook.onDidCloseNotebookDocument(() => {
+ counter--;
+ }));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ assert.equal(counter, 1);
+
+ await splitEditor();
+ assert.equal(counter, 1);
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ assert.equal(counter, 0);
+
+ disposables.forEach(d => d.dispose());
+ });
+
+ test('editor open/close event', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const firstEditorOpen = getEventOncePromise(vscode.notebook.onDidChangeVisibleNotebookEditors);
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await firstEditorOpen;
+
+ const firstEditorClose = getEventOncePromise(vscode.notebook.onDidChangeVisibleNotebookEditors);
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ await firstEditorClose;
+ });
+
+ test('editor open/close event', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ let count = 0;
+ const disposables: vscode.Disposable[] = [];
+ disposables.push(vscode.notebook.onDidChangeVisibleNotebookEditors(() => {
+ count = vscode.notebook.visibleNotebookEditors.length;
+ }));
+
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ assert.equal(count, 1);
+
+ await splitEditor();
+ assert.equal(count, 2);
+
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ assert.equal(count, 0);
+ });
+
+ test('editor editing event 2', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+
+ const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells);
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ const cellChangeEventRet = await cellsChangeEvent;
+ assert.equal(cellChangeEventRet.document, vscode.notebook.activeNotebookEditor?.document);
+ assert.equal(cellChangeEventRet.changes.length, 1);
+ assert.deepEqual(cellChangeEventRet.changes[0], {
+ start: 1,
+ deletedCount: 0,
+ items: [
+ vscode.notebook.activeNotebookEditor!.document.cells[1]
+ ]
+ });
+
+ const moveCellEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells);
+ await vscode.commands.executeCommand('notebook.cell.moveUp');
+ const moveCellEventRet = await moveCellEvent;
+ assert.deepEqual(moveCellEventRet, {
+ document: vscode.notebook.activeNotebookEditor!.document,
+ changes: [
+ {
+ start: 1,
+ deletedCount: 1,
+ items: []
+ },
+ {
+ start: 0,
+ deletedCount: 0,
+ items: [vscode.notebook.activeNotebookEditor?.document.cells[0]]
+ }
+ ]
+ });
+
+ const cellOutputChange = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs);
+ await vscode.commands.executeCommand('notebook.cell.execute');
+ const cellOutputsAddedRet = await cellOutputChange;
+ assert.deepEqual(cellOutputsAddedRet, {
+ document: vscode.notebook.activeNotebookEditor!.document,
+ cells: [vscode.notebook.activeNotebookEditor!.document.cells[0]]
+ });
+ assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 1);
+
+ const cellOutputClear = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs);
+ await vscode.commands.executeCommand('notebook.cell.clearOutputs');
+ const cellOutputsCleardRet = await cellOutputClear;
+ assert.deepEqual(cellOutputsCleardRet, {
+ document: vscode.notebook.activeNotebookEditor!.document,
+ cells: [vscode.notebook.activeNotebookEditor!.document.cells[0]]
+ });
+ assert.equal(cellOutputsAddedRet.cells[0].outputs.length, 0);
+
+ // const cellChangeLanguage = getEventOncePromise(vscode.notebook.onDidChangeCellLanguage);
+ // await vscode.commands.executeCommand('notebook.cell.changeToMarkdown');
+ // const cellChangeLanguageRet = await cellChangeLanguage;
+ // assert.deepEqual(cellChangeLanguageRet, {
+ // document: vscode.notebook.activeNotebookEditor!.document,
+ // cells: vscode.notebook.activeNotebookEditor!.document.cells[0],
+ // language: 'markdown'
+ // });
+
+ await vscode.commands.executeCommand('workbench.action.files.save');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+ test('editor move cell event', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
+ await vscode.commands.executeCommand('notebook.focusTop');
+
+ const activeCell = vscode.notebook.activeNotebookEditor!.selection;
+ assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);
+ const moveChange = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells);
+ await vscode.commands.executeCommand('notebook.cell.moveDown');
+ const ret = await moveChange;
+ assert.deepEqual(ret, {
+ document: vscode.notebook.activeNotebookEditor?.document,
+ changes: [
+ {
+ start: 0,
+ deletedCount: 1,
+ items: []
+ },
+ {
+ start: 1,
+ deletedCount: 0,
+ items: [activeCell]
+ }
+ ]
+ });
+ });
+
+ test('notebook editor active/visible', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ const firstEditor = vscode.notebook.activeNotebookEditor;
+ assert.equal(firstEditor?.active, true);
+ assert.equal(firstEditor?.visible, true);
+
+ await splitEditor();
+ const secondEditor = vscode.notebook.activeNotebookEditor;
+ assert.equal(secondEditor?.active, true);
+ assert.equal(secondEditor?.visible, true);
+ assert.equal(firstEditor?.active, false);
+
+ assert.equal(vscode.notebook.visibleNotebookEditors.length, 2);
+
+ await vscode.commands.executeCommand('workbench.action.files.newUntitledFile');
+ assert.equal(firstEditor?.visible, true);
+ assert.equal(firstEditor?.active, false);
+ assert.equal(secondEditor?.visible, false);
+ assert.equal(secondEditor?.active, false);
+ assert.equal(vscode.notebook.visibleNotebookEditors.length, 1);
+
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ assert.equal(secondEditor?.active, true);
+ assert.equal(secondEditor?.visible, true);
+ assert.equal(vscode.notebook.visibleNotebookEditors.length, 2);
+
+ await vscode.commands.executeCommand('workbench.action.files.save');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+ test('notebook active editor change', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const firstEditorOpen = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor);
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await firstEditorOpen;
+
+ const firstEditorDeactivate = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor);
+ await vscode.commands.executeCommand('workbench.action.splitEditor');
+ await firstEditorDeactivate;
+
+ await vscode.commands.executeCommand('workbench.action.files.save');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+ test('edit API', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+
+ const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells);
+ await vscode.notebook.activeNotebookEditor!.edit(editBuilder => {
+ editBuilder.insert(1, 'test 2', 'javascript', vscode.CellKind.Code, [], undefined);
+ });
+
+ const cellChangeEventRet = await cellsChangeEvent;
+ assert.equal(cellChangeEventRet.document, vscode.notebook.activeNotebookEditor?.document);
+ assert.equal(cellChangeEventRet.changes.length, 1);
+ assert.deepEqual(cellChangeEventRet.changes[0].start, 1);
+ assert.deepEqual(cellChangeEventRet.changes[0].deletedCount, 0);
+ assert.equal(cellChangeEventRet.changes[0].items[0], vscode.notebook.activeNotebookEditor!.document.cells[1]);
+
+ await vscode.commands.executeCommand('workbench.action.files.save');
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ });
+});
+
suite('notebook workflow', () => {
test('notebook open', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
@@ -46,10 +303,8 @@ suite('notebook workflow', () => {
});
test('notebook cell actions', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
@@ -101,14 +356,18 @@ suite('notebook workflow', () => {
// ---- move up and down ---- //
await vscode.commands.executeCommand('notebook.cell.moveDown');
- await vscode.commands.executeCommand('notebook.cell.moveDown');
- activeCell = vscode.notebook.activeNotebookEditor!.selection;
+ assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1,
+ `first move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.source}`);
- assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2);
- assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].source, 'test');
- assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].source, '');
- assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].source, 'test');
- assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].source, '');
+ // await vscode.commands.executeCommand('notebook.cell.moveDown');
+ // activeCell = vscode.notebook.activeNotebookEditor!.selection;
+
+ // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2,
+ // `second move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.source}`);
+ // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].source, 'test');
+ // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].source, '');
+ // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].source, 'test');
+ // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].source, '');
// ---- ---- //
@@ -116,11 +375,30 @@ suite('notebook workflow', () => {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
- // test.only('document metadata is respected', async function () {
- // const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
- // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ test('move cells will not recreate cells in ExtHost', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
+ await vscode.commands.executeCommand('notebook.focusTop');
- // await waitFor(500);
+ const activeCell = vscode.notebook.activeNotebookEditor!.selection;
+ assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);
+ await vscode.commands.executeCommand('notebook.cell.moveDown');
+ await vscode.commands.executeCommand('notebook.cell.moveDown');
+
+ const newActiveCell = vscode.notebook.activeNotebookEditor!.selection;
+ assert.deepEqual(activeCell, newActiveCell);
+ await vscode.commands.executeCommand('workbench.action.files.saveAll');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+
+ // TODO@rebornix, there are still some events order issue.
+ // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(newActiveCell!), 2);
+ });
+
+ // test.only('document metadata is respected', async function () {
+ // const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
// const editor = vscode.notebook.activeNotebookEditor!;
@@ -143,11 +421,8 @@ suite('notebook workflow', () => {
// });
test('cell runnable metadata is respected', async () => {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
-
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.notebook.activeNotebookEditor!;
@@ -167,11 +442,8 @@ suite('notebook workflow', () => {
});
test('document runnable metadata is respected', async () => {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
-
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.notebook.activeNotebookEditor!;
@@ -192,10 +464,8 @@ suite('notebook workflow', () => {
suite('notebook dirty state', () => {
test('notebook open', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
@@ -216,8 +486,6 @@ suite('notebook dirty state', () => {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
@@ -230,10 +498,8 @@ suite('notebook dirty state', () => {
suite('notebook undo redo', () => {
test('notebook open', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'test');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
@@ -275,10 +541,8 @@ suite('notebook undo redo', () => {
suite('notebook working copy', () => {
test('notebook revert on close', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
@@ -288,8 +552,6 @@ suite('notebook working copy', () => {
// close active editor from command will revert the file
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[0], vscode.notebook.activeNotebookEditor?.selection);
@@ -300,10 +562,8 @@ suite('notebook working copy', () => {
});
test('notebook revert', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
@@ -319,29 +579,199 @@ suite('notebook working copy', () => {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
+
+ test('multiple tabs: dirty + clean', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
+
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
+ await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
+
+ const secondResource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './second.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+
+ // make sure that the previous dirty editor is still restored in the extension host and no data loss
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;');
+
+ await vscode.commands.executeCommand('workbench.action.files.save');
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ });
+
+ test('multiple tabs: two dirty tabs and switching', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
+
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
+ await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
+
+ const secondResource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './second.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
+
+ // switch to the first editor
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;');
+
+ // switch to the second editor
+ await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 2);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, '');
+
+ await vscode.commands.executeCommand('workbench.action.files.saveAll');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+ test('multiple tabs: different editors with same document', async function () {
+
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ const firstNotebookEditor = vscode.notebook.activeNotebookEditor;
+ assert.equal(firstNotebookEditor !== undefined, true, 'notebook first');
+ assert.equal(firstNotebookEditor!.selection?.source, 'test');
+ assert.equal(firstNotebookEditor!.selection?.language, 'typescript');
+
+ await splitEditor();
+ const secondNotebookEditor = vscode.notebook.activeNotebookEditor;
+ assert.equal(secondNotebookEditor !== undefined, true, 'notebook first');
+ assert.equal(secondNotebookEditor!.selection?.source, 'test');
+ assert.equal(secondNotebookEditor!.selection?.language, 'typescript');
+
+ assert.notEqual(firstNotebookEditor, secondNotebookEditor);
+ assert.equal(firstNotebookEditor?.document, secondNotebookEditor?.document, 'split notebook editors share the same document');
+ assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')));
+
+ await vscode.commands.executeCommand('workbench.action.files.saveAll');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
});
suite('metadata', () => {
test('custom metadata should be supported', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123);
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
});
+
+ // TODO copy cell should not copy metadata
+
+ test('custom metadata should be supported', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
+ assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123);
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
+
+ await vscode.commands.executeCommand('notebook.cell.copyDown');
+ const activeCell = vscode.notebook.activeNotebookEditor!.selection;
+ assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1);
+ assert.equal(activeCell?.metadata.custom, undefined);
+ });
});
suite('regression', () => {
test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () {
- const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
-
- await waitFor(500);
assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
+ await vscode.commands.executeCommand('workbench.action.files.saveAll');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+ test('#97830, #97764. Support switch to other editor types', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
+
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'var abc = 0;');
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript');
+
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
+ assert.equal(vscode.window.activeTextEditor?.document.uri.path, resource.path);
+
+ await vscode.commands.executeCommand('workbench.action.files.saveAll');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+ // open text editor, pin, and then open a notebook
+ test('#96105 - dirty editors', async function () {
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './empty.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
+
+ // now it's dirty, open the resource with notebook editor should open a new one
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ assert.notEqual(vscode.notebook.activeNotebookEditor, undefined, 'notebook first');
+ assert.notEqual(vscode.window.activeTextEditor, undefined);
+
+ // await vscode.commands.executeCommand('workbench.action.files.saveAll');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+});
+
+suite('webview', () => {
+ // for web, `asWebUri` gets `https`?
+ test('asWebviewUri', async function () {
+ if (vscode.env.uiKind === vscode.UIKind.Web) {
+ return;
+ }
+
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
+ const uri = vscode.notebook.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png'));
+ assert.equal(uri.scheme, 'vscode-webview-resource');
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
+ });
+
+
+ // 404 on web
+ test('custom renderer message', async function () {
+ if (vscode.env.uiKind === vscode.UIKind.Web) {
+ return;
+ }
+
+ const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './customRenderer.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+
+ const editor = vscode.notebook.activeNotebookEditor;
+ const promise = new Promise(resolve => {
+ const messageEmitter = editor?.onDidReceiveMessage(e => {
+ if (e.type === 'custom_renderer_initialize') {
+ resolve();
+ messageEmitter?.dispose();
+ }
+ });
+ });
+
+ await vscode.commands.executeCommand('notebook.cell.execute');
+ await promise;
+ await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});
});
diff --git a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts b/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts
new file mode 100644
index 00000000000..0a8c4a3d105
--- /dev/null
+++ b/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 * as child_process from 'child_process';
+import * as path from 'path';
+
+function wait(ms: number): Promise {
+ return new Promise(r => setTimeout(r, ms));
+}
+
+export function smokeTestActivate(context: vscode.ExtensionContext): any {
+ context.subscriptions.push(vscode.commands.registerCommand('vscode-notebook-tests.createNewNotebook', async () => {
+ const workspacePath = vscode.workspace.workspaceFolders![0].uri.fsPath;
+ const notebookPath = path.join(workspacePath, 'test.smoke-nb');
+ child_process.execSync('echo \'\' > ' + notebookPath);
+ await wait(500);
+ await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(notebookPath));
+ }));
+
+ context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookSmokeTest', {
+ onDidChangeNotebook: new vscode.EventEmitter().event,
+ openNotebook: async (_resource: vscode.Uri) => {
+ const dto: vscode.NotebookData = {
+ languages: ['typescript'],
+ metadata: {},
+ cells: [
+ {
+ source: 'code()',
+ language: 'typescript',
+ cellKind: vscode.CellKind.Code,
+ outputs: [],
+ metadata: {
+ custom: { testCellMetadata: 123 }
+ }
+ },
+ {
+ source: 'Markdown Cell',
+ language: 'markdown',
+ cellKind: vscode.CellKind.Markdown,
+ outputs: [],
+ metadata: {
+ custom: { testCellMetadata: 123 }
+ }
+ }
+ ]
+ };
+
+ return dto;
+ },
+ saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
+ return;
+ },
+ saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
+ return;
+ }
+ }));
+
+ context.subscriptions.push(vscode.notebook.registerNotebookKernel('notebookSmokeTest', ['*.vsctestnb'], {
+ label: 'notebookSmokeTest',
+ executeAllCells: async (_document: vscode.NotebookDocument) => {
+ for (let i = 0; i < _document.cells.length; i++) {
+ _document.cells[i].outputs = [{
+ outputKind: vscode.CellOutputKind.Rich,
+ data: {
+ 'text/html': ['test output']
+ }
+ }];
+ }
+ },
+ executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined, _token: vscode.CancellationToken) => {
+ if (!_cell) {
+ _cell = _document.cells[0];
+ }
+
+ _cell.outputs = [{
+ outputKind: vscode.CellOutputKind.Rich,
+ data: {
+ 'text/html': ['test output']
+ }
+ }];
+ return;
+ },
+ }));
+}
diff --git a/extensions/vscode-notebook-tests/src/notebookTestMain.ts b/extensions/vscode-notebook-tests/src/notebookTestMain.ts
index 6cdd3619de7..059f998acae 100644
--- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts
+++ b/extensions/vscode-notebook-tests/src/notebookTestMain.ts
@@ -4,8 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
+import * as path from 'path';
+import { smokeTestActivate } from './notebookSmokeTestMain';
export function activate(context: vscode.ExtensionContext): any {
+ smokeTestActivate(context);
+
context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', {
onDidChangeNotebook: new vscode.EventEmitter().event,
openNotebook: async (_resource: vscode.Uri) => {
@@ -37,24 +41,64 @@ export function activate(context: vscode.ExtensionContext): any {
return dto;
},
+ saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
+ return;
+ },
+ saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
+ return;
+ }
+ }));
+
+ context.subscriptions.push(vscode.notebook.registerNotebookKernel('notebookKernelTest', ['*.vsctestnb'], {
+ label: 'Notebook Test Kernel',
+ executeAllCells: async (_document: vscode.NotebookDocument, _token: vscode.CancellationToken) => {
+ let cell = _document.cells[0];
+
+ cell.outputs = [{
+ outputKind: vscode.CellOutputKind.Rich,
+ data: {
+ 'text/plain': ['my output']
+ }
+ }];
+ return;
+ },
executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined, _token: vscode.CancellationToken) => {
if (!_cell) {
_cell = _document.cells[0];
}
+ if (_document.uri.path.endsWith('customRenderer.vsctestnb')) {
+ _cell.outputs = [{
+ outputKind: vscode.CellOutputKind.Rich,
+ data: {
+ 'text/custom': 'test'
+ }
+ }];
+
+ return;
+ }
+
_cell.outputs = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
'text/plain': ['my output']
}
}];
- return;
- },
- saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
- return;
- },
- saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
+
return;
}
}));
+
+ const preloadUri = vscode.Uri.file(path.resolve(__dirname, '../src/customRenderer.js'));
+ context.subscriptions.push(vscode.notebook.registerNotebookOutputRenderer('notebookCoreTestRenderer', {
+ type: 'display_data',
+ subTypes: [
+ 'text/custom'
+ ]
+ }, {
+ preloads: [preloadUri],
+ render(_document: vscode.NotebookDocument, _output: vscode.CellDisplayOutput, _mimeType: string): string {
+ return 'test
';
+ }
+ }));
}
diff --git a/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb b/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb
new file mode 100644
index 00000000000..a4a092d8349
--- /dev/null
+++ b/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb
@@ -0,0 +1,4 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
diff --git a/extensions/vscode-notebook-tests/test/second.vsctestnb b/extensions/vscode-notebook-tests/test/second.vsctestnb
new file mode 100644
index 00000000000..a4a092d8349
--- /dev/null
+++ b/extensions/vscode-notebook-tests/test/second.vsctestnb
@@ -0,0 +1,4 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
diff --git a/extensions/vscode-web-playground/package.json b/extensions/vscode-web-playground/package.json
index 46b6102c10e..05fa8bdeb78 100644
--- a/extensions/vscode-web-playground/package.json
+++ b/extensions/vscode-web-playground/package.json
@@ -10,7 +10,7 @@
"onFileSystem:memfs",
"onDebug"
],
- "main": "./out/extension",
+ "browser": "./out/extension",
"engines": {
"vscode": "^1.25.0"
},
diff --git a/extensions/yarn.lock b/extensions/yarn.lock
index 4e0ca82acd1..31924f9e866 100644
--- a/extensions/yarn.lock
+++ b/extensions/yarn.lock
@@ -2,7 +2,7 @@
# yarn lockfile v1
-typescript@^3.9.2-insiders.20200509:
- version "3.9.2-insiders.20200509"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.2-insiders.20200509.tgz#8c90ed86a91f9692f10f5ac9c1fd6cb241419e6c"
- integrity sha512-AAbhs55BZMbyHGfJd0pNfO3+B6jjPpa38zgaIb9MRExkRGLkIUpbUetoh+HgmM5LAtg128sHGiwhLc49pOcgFw==
+typescript@3.9.4:
+ version "3.9.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.4.tgz#5aa0a54904b51b96dfd67870ce2db70251802f10"
+ integrity sha512-9OL+r0KVHqsYVH7K18IBR9hhC82YwLNlpSZfQDupGcfg8goB9p/s/9Okcy+ztnTeHR2U68xq21/igW9xpoGTgA==
diff --git a/package.json b/package.json
index d4bc7dbf2d8..92aa5c37eee 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.46.0",
- "distro": "19675eb92c1d28a53751978b99c47674b997ea3f",
+ "distro": "da9a049e7e7a7beb80287dce52a3966d8cfcf554",
"author": {
"name": "Microsoft Corporation"
},
@@ -33,8 +33,7 @@
"strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes",
"update-distro": "node build/npm/update-distro.js",
"web": "node scripts/code-web.js",
- "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions",
- "generate-github-config": "node extensions/github-authentication/build/generateconfig.js"
+ "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions"
},
"dependencies": {
"applicationinsights": "1.0.8",
@@ -60,11 +59,10 @@
"vscode-ripgrep": "^1.5.8",
"vscode-sqlite3": "4.0.10",
"vscode-textmate": "5.1.1",
- "xterm": "4.6.0-beta.44",
- "xterm-addon-search": "0.7.0-beta.2",
- "xterm-addon-unicode11": "0.2.0-beta.5",
- "xterm-addon-web-links": "0.4.0-beta.6",
- "xterm-addon-webgl": "0.7.0-beta.10",
+ "xterm": "4.7.0-beta.3",
+ "xterm-addon-search": "0.7.0",
+ "xterm-addon-unicode11": "0.2.0",
+ "xterm-addon-webgl": "0.7.0",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
@@ -101,7 +99,7 @@
"css-loader": "^3.2.0",
"debounce": "^1.0.0",
"deemon": "^1.4.0",
- "electron": "7.2.4",
+ "electron": "7.3.0",
"eslint": "6.8.0",
"eslint-plugin-jsdoc": "^19.1.0",
"event-stream": "3.3.4",
@@ -156,7 +154,7 @@
"source-map": "^0.4.4",
"style-loader": "^1.0.0",
"ts-loader": "^4.4.2",
- "typescript": "^3.9.1-rc",
+ "typescript": "^3.9.3",
"typescript-formatter": "7.1.0",
"underscore": "^1.8.2",
"vinyl": "^2.0.0",
diff --git a/product.json b/product.json
index 77409b04faa..740f44e9ee6 100644
--- a/product.json
+++ b/product.json
@@ -11,8 +11,10 @@
"win32RegValueName": "CodeOSS",
"win32AppId": "{{E34003BB-9E10-4501-8C11-BE3FAA83F23F}",
"win32x64AppId": "{{D77B7E06-80BA-4137-BCF4-654B95CCEBC5}",
+ "win32arm64AppId": "{{D1ACE434-89C5-48D1-88D3-E2991DF85475}",
"win32UserAppId": "{{C6065F05-9603-4FC4-8101-B9781A25D88E}",
"win32x64UserAppId": "{{C6065F05-9603-4FC4-8101-B9781A25D88E}",
+ "win32arm64UserAppId": "{{3AEBF0C8-F733-4AD4-BADE-FDB816D53D7B}",
"win32AppUserModelId": "Microsoft.CodeOSS",
"win32ShellNameShort": "C&ode - OSS",
"darwinBundleIdentifier": "com.visualstudio.code.oss",
@@ -21,6 +23,7 @@
"reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new",
"urlProtocol": "code-oss",
"extensionAllowedProposedApi": [
+ "ms-vscode.vscode-js-profile-flame",
"ms-vscode.vscode-js-profile-table",
"ms-vscode.references-view"
],
@@ -42,7 +45,7 @@
},
{
"name": "ms-vscode.node-debug2",
- "version": "1.42.2",
+ "version": "1.42.4",
"repo": "https://github.com/Microsoft/vscode-node-debug2",
"metadata": {
"id": "36d19e17-7569-4841-a001-947eb18602b2",
@@ -57,7 +60,7 @@
},
{
"name": "ms-vscode.references-view",
- "version": "0.0.53",
+ "version": "0.0.57",
"repo": "https://github.com/Microsoft/vscode-reference-view",
"metadata": {
"id": "dc489f46-520d-4556-ae85-1f9eab3c412d",
@@ -72,7 +75,7 @@
},
{
"name": "ms-vscode.js-debug-companion",
- "version": "1.0.0",
+ "version": "1.0.2",
"repo": "https://github.com/microsoft/vscode-js-debug-companion",
"metadata": {
"id": "99cb0b7f-7354-4278-b8da-6cc79972169d",
@@ -87,7 +90,7 @@
},
{
"name": "ms-vscode.js-debug-nightly",
- "version": "2020.5.417",
+ "version": "2020.5.2717",
"repo": "https://github.com/Microsoft/vscode-js-debug",
"metadata": {
"id": "7acbb4ce-c85a-49d4-8d95-a8054406ae97",
@@ -102,7 +105,7 @@
},
{
"name": "ms-vscode.vscode-js-profile-table",
- "version": "0.0.2",
+ "version": "0.0.4",
"repo": "https://github.com/Microsoft/vscode-js-debug",
"metadata": {
"id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb",
diff --git a/remote/package.json b/remote/package.json
index b8927215f16..cba584d935d 100644
--- a/remote/package.json
+++ b/remote/package.json
@@ -20,11 +20,10 @@
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.5.8",
"vscode-textmate": "5.1.1",
- "xterm": "4.6.0-beta.44",
- "xterm-addon-search": "0.7.0-beta.2",
- "xterm-addon-unicode11": "0.2.0-beta.5",
- "xterm-addon-web-links": "0.4.0-beta.6",
- "xterm-addon-webgl": "0.7.0-beta.10",
+ "xterm": "4.7.0-beta.3",
+ "xterm-addon-search": "0.7.0",
+ "xterm-addon-unicode11": "0.2.0",
+ "xterm-addon-webgl": "0.7.0",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
diff --git a/remote/web/package.json b/remote/web/package.json
index 98baff7fd26..5a4ef271910 100644
--- a/remote/web/package.json
+++ b/remote/web/package.json
@@ -5,10 +5,9 @@
"semver-umd": "^5.5.6",
"vscode-oniguruma": "1.3.1",
"vscode-textmate": "5.1.1",
- "xterm": "4.6.0-beta.44",
- "xterm-addon-search": "0.7.0-beta.2",
- "xterm-addon-unicode11": "0.2.0-beta.5",
- "xterm-addon-web-links": "0.4.0-beta.6",
- "xterm-addon-webgl": "0.7.0-beta.10"
+ "xterm": "4.7.0-beta.3",
+ "xterm-addon-search": "0.7.0",
+ "xterm-addon-unicode11": "0.2.0",
+ "xterm-addon-webgl": "0.7.0"
}
}
diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock
index 4b7efc15550..fa3681e1a98 100644
--- a/remote/web/yarn.lock
+++ b/remote/web/yarn.lock
@@ -17,27 +17,22 @@ vscode-textmate@5.1.1:
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.1.1.tgz#d88dbf271bee7cede455a21bd4894ba5724a4a7e"
integrity sha512-5VHjF+Fglf9d2JI5OyQ7FHutK6/29G0qYyD920K0SWO7uY8JTWbqyKAHEtfB/ZDk2fOe/E23n3wz9fHXKi63yg==
-xterm-addon-search@0.7.0-beta.2:
- version "0.7.0-beta.2"
- resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0-beta.2.tgz#384bda136c707f97a77eefc76cc7d9e572ce0719"
- integrity sha512-A9fyiBBvG6ZNIwSJ03+sRCv9y20/uzd1wjCoaYUqp9fu3YGiHaGwyo9rAfm2M/fQM5vBmyJk4Qw/lwVq7TtlAw==
+xterm-addon-search@0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0.tgz#c929d3e5cbb335e82bff72f158ea82936d9cd4ef"
+ integrity sha512-6060evmJJ+tZcjnx33FXaeEHLpuXEa7l9UzUsYfMlCKbu88AbE+5LJocTKCHYd71cwCwb9pjmv/G1o9Rf9Zbcg==
-xterm-addon-unicode11@0.2.0-beta.5:
- version "0.2.0-beta.5"
- resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298"
- integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q==
+xterm-addon-unicode11@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c"
+ integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q==
-xterm-addon-web-links@0.4.0-beta.6:
- version "0.4.0-beta.6"
- resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa"
- integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ==
+xterm-addon-webgl@0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0.tgz#a13732ac937170e53ce02ec91963da042c80614b"
+ integrity sha512-PMWLgccAF31GulCYkQxIA8qwMI4q4UbRi5O/zwMnSJWBozB0yy84lX31ZhJeJhcrlEn1Vpcd+OUGPE8Z1hBjnw==
-xterm-addon-webgl@0.7.0-beta.10:
- version "0.7.0-beta.10"
- resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4"
- integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg==
-
-xterm@4.6.0-beta.44:
- version "4.6.0-beta.44"
- resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615"
- integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg==
+xterm@4.7.0-beta.3:
+ version "4.7.0-beta.3"
+ resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.7.0-beta.3.tgz#d8997f190430d750201717adf3857f6c8052f149"
+ integrity sha512-mL9VCB7Ql7KSql2PJmRQYba77mMXlliK9lVKd3XCDqtOYYWjg+CKKeNtFljIrPoiI25nvoqlkrv5dFuuIAR5hA==
diff --git a/remote/yarn.lock b/remote/yarn.lock
index 20fbc3637a4..7ba3b88854f 100644
--- a/remote/yarn.lock
+++ b/remote/yarn.lock
@@ -404,30 +404,25 @@ vscode-windows-registry@1.0.2:
resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a"
integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA==
-xterm-addon-search@0.7.0-beta.2:
- version "0.7.0-beta.2"
- resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0-beta.2.tgz#384bda136c707f97a77eefc76cc7d9e572ce0719"
- integrity sha512-A9fyiBBvG6ZNIwSJ03+sRCv9y20/uzd1wjCoaYUqp9fu3YGiHaGwyo9rAfm2M/fQM5vBmyJk4Qw/lwVq7TtlAw==
+xterm-addon-search@0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0.tgz#c929d3e5cbb335e82bff72f158ea82936d9cd4ef"
+ integrity sha512-6060evmJJ+tZcjnx33FXaeEHLpuXEa7l9UzUsYfMlCKbu88AbE+5LJocTKCHYd71cwCwb9pjmv/G1o9Rf9Zbcg==
-xterm-addon-unicode11@0.2.0-beta.5:
- version "0.2.0-beta.5"
- resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.5.tgz#5961850162df20b5e966166423cd6957ac2db298"
- integrity sha512-IjnbBcyfS5JgJDXPO0W2nk/VBtGwx6GWE2snMC676z4DmAABUqPXfTzJKfUoWqoT6UcbxB0oIjDzykCfoRJp6Q==
+xterm-addon-unicode11@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c"
+ integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q==
-xterm-addon-web-links@0.4.0-beta.6:
- version "0.4.0-beta.6"
- resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0-beta.6.tgz#d159d4542eb9a02d57977fe7eb5f42f8ef2f27fa"
- integrity sha512-dsQVD/EyVq8PtAYGh2PGQTCt009UipIfX6Q2SBDlz+W9x7IkXjhRxRaryMmLsBCca20qeVKwmbQ+ANhLi+nTaQ==
+xterm-addon-webgl@0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0.tgz#a13732ac937170e53ce02ec91963da042c80614b"
+ integrity sha512-PMWLgccAF31GulCYkQxIA8qwMI4q4UbRi5O/zwMnSJWBozB0yy84lX31ZhJeJhcrlEn1Vpcd+OUGPE8Z1hBjnw==
-xterm-addon-webgl@0.7.0-beta.10:
- version "0.7.0-beta.10"
- resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.10.tgz#39fdb96351e97a1bf15f4c4c8944ba3d05cacee4"
- integrity sha512-nQl/ASk+ck11aSrBZXb2a0tu+SNDnm89owBk/sAZeZzi5MHNo6bB8y2VTKNNC6D3i3aFouTz4VorYB25LUgNFg==
-
-xterm@4.6.0-beta.44:
- version "4.6.0-beta.44"
- resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.44.tgz#76b2a6b8e147595ab44aa752c0e721d935464615"
- integrity sha512-vYtfz4spFcSKLEUpC6anH7TwDams71+k2wAtUzCJ47dNL2IrwYafcFsvGPm46QLTtq4M2Bp9rQo3R3V746yxNg==
+xterm@4.7.0-beta.3:
+ version "4.7.0-beta.3"
+ resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.7.0-beta.3.tgz#d8997f190430d750201717adf3857f6c8052f149"
+ integrity sha512-mL9VCB7Ql7KSql2PJmRQYba77mMXlliK9lVKd3XCDqtOYYWjg+CKKeNtFljIrPoiI25nvoqlkrv5dFuuIAR5hA==
yauzl@^2.9.2:
version "2.10.0"
diff --git a/scripts/code-web.js b/scripts/code-web.js
index b7441ea0158..e78d39835b5 100755
--- a/scripts/code-web.js
+++ b/scripts/code-web.js
@@ -14,6 +14,7 @@ const path = require('path');
const util = require('util');
const opn = require('opn');
const minimist = require('minimist');
+const webpack = require('webpack');
const APP_ROOT = path.dirname(__dirname);
const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions');
@@ -21,6 +22,7 @@ const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench'
const args = minimist(process.argv, {
boolean: [
+ 'watch',
'no-launch',
'help'
],
@@ -35,6 +37,7 @@ const args = minimist(process.argv, {
if (args.help) {
console.log(
'yarn web [options]\n' +
+ ' --watch Watch extensions that require browser specific builds\n' +
' --no-launch Do not open VSCode web in the browser\n' +
' --scheme Protocol (https or http)\n' +
' --host Remote host\n' +
@@ -53,6 +56,104 @@ const SCHEME = args.scheme || process.env.VSCODE_SCHEME || 'http';
const HOST = args.host || 'localhost';
const AUTHORITY = process.env.VSCODE_AUTHORITY || `${HOST}:${PORT}`;
+const exists = (path) => util.promisify(fs.exists)(path);
+const readFile = (path) => util.promisify(fs.readFile)(path);
+const CharCode_PC = '%'.charCodeAt(0);
+
+async function initialize() {
+ const extensionFolders = await util.promisify(fs.readdir)(EXTENSIONS_ROOT);
+
+ const staticExtensions = [];
+
+ const webpackConfigs = [];
+
+ await Promise.all(extensionFolders.map(async extensionFolder => {
+ const packageJSONPath = path.join(EXTENSIONS_ROOT, extensionFolder, 'package.json');
+ if (await exists(packageJSONPath)) {
+ try {
+ const packageJSON = JSON.parse((await readFile(packageJSONPath)).toString());
+ if (packageJSON.main && !packageJSON.browser) {
+ return; // unsupported
+ }
+
+ if (packageJSON.browser) {
+ packageJSON.main = packageJSON.browser;
+ const webpackConfigPath = path.join(EXTENSIONS_ROOT, extensionFolder, 'extension-browser.webpack.config.js');
+ if ((await exists(webpackConfigPath))) {
+ const configOrFnOrArray = require(webpackConfigPath);
+ function addConfig(configOrFn) {
+ if (typeof configOrFn === 'function') {
+ webpackConfigs.push(configOrFn({}, {}));
+ } else {
+ webpackConfigs.push(configOrFn);
+ }
+ }
+ if (Array.isArray(configOrFnOrArray)) {
+ configOrFnOrArray.forEach(addConfig);
+ } else {
+ addConfig(configOrFnOrArray);
+ }
+ }
+ }
+
+ const packageNlsPath = path.join(EXTENSIONS_ROOT, extensionFolder, 'package.nls.json');
+ if (await exists(packageNlsPath)) {
+ const packageNls = JSON.parse((await readFile(packageNlsPath)).toString());
+ const translate = (obj) => {
+ for (let key in obj) {
+ const val = obj[key];
+ if (Array.isArray(val)) {
+ val.forEach(translate);
+ } else if (val && typeof val === 'object') {
+ translate(val);
+ } else if (typeof val === 'string' && val.charCodeAt(0) === CharCode_PC && val.charCodeAt(val.length - 1) === CharCode_PC) {
+ const translated = packageNls[val.substr(1, val.length - 2)];
+ if (translated) {
+ obj[key] = translated;
+ }
+ }
+ }
+ };
+ translate(packageJSON);
+ }
+ packageJSON.extensionKind = ['web']; // enable for Web
+ staticExtensions.push({
+ packageJSON,
+ extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/static-extension/${extensionFolder}` }
+ });
+ } catch (e) {
+ console.log(e);
+ }
+ }
+ }));
+
+ return new Promise((resolve, reject) => {
+ if (args.watch) {
+ webpack(webpackConfigs).watch({}, (err, stats) => {
+ if (err) {
+ console.log(err);
+ reject();
+ } else {
+ console.log(stats.toString());
+ resolve(staticExtensions);
+ }
+ });
+ } else {
+ webpack(webpackConfigs).run((err, stats) => {
+ if (err) {
+ console.log(err);
+ reject();
+ } else {
+ console.log(stats.toString());
+ resolve(staticExtensions);
+ }
+ });
+ }
+ });
+}
+
+const staticExtensionsPromise = initialize();
+
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
@@ -139,43 +240,25 @@ function handleStaticExtension(req, res, parsedUrl) {
* @param {import('http').ServerResponse} res
*/
async function handleRoot(req, res) {
- const extensionFolders = await util.promisify(fs.readdir)(EXTENSIONS_ROOT);
- const mapExtensionFolderToExtensionPackageJSON = new Map();
-
- await Promise.all(extensionFolders.map(async extensionFolder => {
- try {
- const packageJSON = JSON.parse((await util.promisify(fs.readFile)(path.join(EXTENSIONS_ROOT, extensionFolder, 'package.json'))).toString());
- if (packageJSON.main && packageJSON.name !== 'vscode-web-playground') {
- return; // unsupported
- }
-
- if (packageJSON.name === 'scss') {
- return; // seems to fail to JSON.parse()?!
- }
-
- packageJSON.extensionKind = ['web']; // enable for Web
-
- mapExtensionFolderToExtensionPackageJSON.set(extensionFolder, packageJSON);
- } catch (error) {
- return null;
+ const match = req.url && req.url.match(/\?([^#]+)/);
+ let ghPath;
+ if (match) {
+ const qs = new URLSearchParams(match[1]);
+ ghPath = qs.get('gh');
+ if (ghPath && !ghPath.startsWith('/')) {
+ ghPath = '/' + ghPath;
}
+ }
+
+ const staticExtensions = await staticExtensionsPromise;
+ const webConfiguration = escapeAttribute(JSON.stringify({
+ staticExtensions, folderUri: ghPath
+ ? { scheme: 'github', authority: 'github.com', path: ghPath }
+ : { scheme: 'memfs', path: `/sample-folder` }
}));
- const staticExtensions = [];
-
- // Built in extensions
- mapExtensionFolderToExtensionPackageJSON.forEach((packageJSON, extensionFolder) => {
- staticExtensions.push({
- packageJSON,
- extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/static-extension/${extensionFolder}` }
- });
- });
-
const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString()
- .replace('{{WORKBENCH_WEB_CONFIGURATION}}', escapeAttribute(JSON.stringify({
- staticExtensions,
- folderUri: { scheme: 'memfs', path: `/sample-folder` }
- })))
+ .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => webConfiguration) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied
.replace('{{WEBVIEW_ENDPOINT}}', '')
.replace('{{REMOTE_USER_DATA_URI}}', '');
diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat
index e5e6c7d8892..f250eb40f80 100644
--- a/scripts/test-integration.bat
+++ b/scripts/test-integration.bat
@@ -42,6 +42,9 @@ if %errorlevel% neq 0 exit /b %errorlevel%
:: Tests in the extension host
+call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
+if %errorlevel% neq 0 exit /b %errorlevel%
+
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh
index 0342b23c838..25a7cc7a278 100755
--- a/scripts/test-integration.sh
+++ b/scripts/test-integration.sh
@@ -60,4 +60,4 @@ fi
cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js
cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js
-rm -r $VSCODEUSERDATADIR
+rm -rf $VSCODEUSERDATADIR
diff --git a/src/main.js b/src/main.js
index 23915126b8f..1eef87ad080 100644
--- a/src/main.js
+++ b/src/main.js
@@ -79,7 +79,15 @@ setCurrentWorkingDirectory();
// Register custom schemes with privileges
protocol.registerSchemesAsPrivileged([
- { scheme: 'vscode-resource', privileges: { secure: true, supportFetchAPI: true, corsEnabled: true } }
+ {
+ scheme: 'vscode-webview-resource',
+ privileges: {
+ secure: true,
+ standard: true,
+ supportFetchAPI: true,
+ corsEnabled: true,
+ }
+ },
]);
// Global app listeners
diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts
index caf1d299038..6a5d3f79d2b 100644
--- a/src/vs/base/browser/contextmenu.ts
+++ b/src/vs/base/browser/contextmenu.ts
@@ -34,4 +34,5 @@ export interface IContextMenuDelegate {
actionRunner?: IActionRunner;
autoSelectFirstItem?: boolean;
anchorAlignment?: AnchorAlignment;
+ anchorAsContainer?: boolean;
}
diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts
index da658dd0d57..4726ad8b8de 100644
--- a/src/vs/base/browser/markdownRenderer.ts
+++ b/src/vs/base/browser/markdownRenderer.ts
@@ -111,7 +111,10 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
}
href = _href(href, false);
if (options.baseUrl) {
- href = resolvePath(options.baseUrl, href).toString();
+ const hasScheme = /^\w[\w\d+.-]*:/.test(href);
+ if (!hasScheme) {
+ href = resolvePath(options.baseUrl, href).toString();
+ }
}
title = removeMarkdownEscapes(title);
href = removeMarkdownEscapes(href);
@@ -185,6 +188,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
}));
}
+ // Use our own sanitizer so that we can let through only spans.
+ // Otherwise, we'd be letting all html be rendered.
+ // If we want to allow markdown permitted tags, then we can delete sanitizer and sanitize.
+ markedOptions.sanitizer = (html: string): string => {
+ const match = markdown.isTrusted ? html.match(/^()|(<\/\s*span>)$/) : undefined;
+ return match ? html : '';
+ };
markedOptions.sanitize = true;
markedOptions.renderer = renderer;
@@ -200,18 +210,32 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
markedOptions
);
+ function filter(token: { tag: string, attrs: { readonly [key: string]: string } }): boolean {
+ if (token.tag === 'span' && markdown.isTrusted) {
+ if (token.attrs['style'] && Object.keys(token.attrs).length === 1) {
+ return !!token.attrs['style'].match(/^(color\:#[0-9a-fA-F]+;)?(background-color\:#[0-9a-fA-F]+;)?$/);
+ }
+ return false;
+ }
+ return true;
+ }
+
element.innerHTML = insane(renderedMarkdown, {
allowedSchemes,
+ // allowedTags should included everything that markdown renders to.
+ // Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure.
+ // HTML tags that can result from markdown are from reading https://spec.commonmark.org/0.29/
+ allowedTags: ['ul', 'li', 'p', 'code', 'blockquote', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'em', 'pre', 'table', 'tr', 'td', 'div', 'del', 'a', 'strong', 'br', 'img', 'span'],
allowedAttributes: {
'a': ['href', 'name', 'target', 'data-href'],
- 'iframe': ['allowfullscreen', 'frameborder', 'src'],
'img': ['src', 'title', 'alt', 'width', 'height'],
'div': ['class', 'data-code'],
- 'span': ['class'],
+ 'span': ['class', 'style'],
// https://github.com/microsoft/vscode/issues/95937
'th': ['align'],
'td': ['align']
- }
+ },
+ filter
});
signalInnerHTML!();
diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts
index ba7c5ce4344..1f68379260d 100644
--- a/src/vs/base/browser/ui/actionbar/actionbar.ts
+++ b/src/vs/base/browser/ui/actionbar/actionbar.ts
@@ -405,6 +405,7 @@ export interface IActionBarOptions {
ariaLabel?: string;
animated?: boolean;
triggerKeys?: ActionTrigger;
+ allowContextMenu?: boolean;
}
const defaultOptions: IActionBarOptions = {
@@ -634,9 +635,11 @@ export class ActionBar extends Disposable implements IActionRunner {
actionViewItemElement.setAttribute('role', 'presentation');
// Prevent native context menu on actions
- this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
- DOM.EventHelper.stop(e, true);
- }));
+ if (!this.options.allowContextMenu) {
+ this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
+ DOM.EventHelper.stop(e, true);
+ }));
+ }
let item: IActionViewItem | undefined;
diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf
index abcf8283afa..ba25be2a8e9 100644
Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ
diff --git a/src/vs/base/browser/ui/contextview/contextview.css b/src/vs/base/browser/ui/contextview/contextview.css
index b1b67bbe3c3..bb7ebbcfdb2 100644
--- a/src/vs/base/browser/ui/contextview/contextview.css
+++ b/src/vs/base/browser/ui/contextview/contextview.css
@@ -7,3 +7,12 @@
position: absolute;
z-index: 2500;
}
+
+.context-view.fixed {
+ all: initial;
+ font-family: inherit;
+ font-size: 13px;
+ position: fixed;
+ z-index: 2500;
+ color: inherit;
+}
diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts
index eacf37f358a..7be14b621ab 100644
--- a/src/vs/base/browser/ui/contextview/contextview.ts
+++ b/src/vs/base/browser/ui/contextview/contextview.ts
@@ -38,7 +38,7 @@ export interface IDelegate {
}
export interface IContextViewProvider {
- showContextView(delegate: IDelegate): void;
+ showContextView(delegate: IDelegate, container?: HTMLElement): void;
hideContextView(): void;
layout(): void;
}
@@ -104,23 +104,25 @@ export class ContextView extends Disposable {
private container: HTMLElement | null = null;
private view: HTMLElement;
+ private useFixedPosition: boolean;
private delegate: IDelegate | null = null;
private toDisposeOnClean: IDisposable = Disposable.None;
private toDisposeOnSetContainer: IDisposable = Disposable.None;
- constructor(container: HTMLElement) {
+ constructor(container: HTMLElement, useFixedPosition: boolean) {
super();
this.view = DOM.$('.context-view');
+ this.useFixedPosition = false;
DOM.hide(this.view);
- this.setContainer(container);
+ this.setContainer(container, useFixedPosition);
- this._register(toDisposable(() => this.setContainer(null)));
+ this._register(toDisposable(() => this.setContainer(null, false)));
}
- setContainer(container: HTMLElement | null): void {
+ setContainer(container: HTMLElement | null, useFixedPosition: boolean): void {
if (this.container) {
this.toDisposeOnSetContainer.dispose();
this.container.removeChild(this.view);
@@ -146,6 +148,8 @@ export class ContextView extends Disposable {
this.toDisposeOnSetContainer = toDisposeOnSetContainer;
}
+
+ this.useFixedPosition = useFixedPosition;
}
show(delegate: IDelegate): void {
@@ -254,10 +258,11 @@ export class ContextView extends Disposable {
DOM.removeClasses(this.view, 'top', 'bottom', 'left', 'right');
DOM.addClass(this.view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
DOM.addClass(this.view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right');
+ DOM.toggleClass(this.view, 'fixed', this.useFixedPosition);
const containerPosition = DOM.getDomNodePagePosition(this.container!);
- this.view.style.top = `${top - containerPosition.top}px`;
- this.view.style.left = `${left - containerPosition.left}px`;
+ this.view.style.top = `${top - (this.useFixedPosition ? DOM.getDomNodePagePosition(this.view).top : containerPosition.top)}px`;
+ this.view.style.left = `${left - (this.useFixedPosition ? DOM.getDomNodePagePosition(this.view).left : containerPosition.left)}px`;
this.view.style.width = 'initial';
}
diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts
index 089af090e28..623aa9c00d0 100644
--- a/src/vs/base/browser/ui/dialog/dialog.ts
+++ b/src/vs/base/browser/ui/dialog/dialog.ts
@@ -73,6 +73,7 @@ export class Dialog extends Disposable {
this.modal = this.container.appendChild($(`.monaco-dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`));
this.shadowElement = this.modal.appendChild($('.dialog-shadow'));
this.element = this.shadowElement.appendChild($('.monaco-dialog-box'));
+ this.element.setAttribute('role', 'dialog');
hide(this.element);
// If no button is provided, default to OK
@@ -109,6 +110,28 @@ export class Dialog extends Disposable {
this.toolbarContainer = toolbarRowElement.appendChild($('.dialog-toolbar'));
}
+ private getAriaLabel(): string {
+ let typeLabel = nls.localize('dialogInfoMessage', 'Info');
+ switch (this.options.type) {
+ case 'error':
+ nls.localize('dialogErrorMessage', 'Error');
+ break;
+ case 'warning':
+ nls.localize('dialogWarningMessage', 'Warning');
+ break;
+ case 'pending':
+ nls.localize('dialogPendingMessage', 'In Progress');
+ break;
+ case 'none':
+ case 'info':
+ case 'question':
+ default:
+ break;
+ }
+
+ return `${typeLabel}: ${this.message} ${this.options.detail || ''}`;
+ }
+
updateMessage(message: string): void {
if (this.messageDetailElement) {
this.messageDetailElement.innerText = message;
@@ -242,7 +265,7 @@ export class Dialog extends Disposable {
this.applyStyles();
- this.element.setAttribute('aria-label', this.message);
+ this.element.setAttribute('aria-label', this.getAriaLabel());
show(this.element);
// Focus first element
diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts
index 99c11cdd6e2..22693e1971c 100644
--- a/src/vs/base/browser/ui/dropdown/dropdown.ts
+++ b/src/vs/base/browser/ui/dropdown/dropdown.ts
@@ -52,7 +52,7 @@ export class BaseDropdown extends ActionRunner {
}
for (const event of [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap]) {
- this._register(addDisposableListener(this._label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger
+ this._register(addDisposableListener(this.element, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger
}
for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) {
@@ -266,7 +266,8 @@ export class DropdownMenu extends BaseDropdown {
getMenuClassName: () => this.menuClassName,
onHide: () => this.onHide(),
actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined,
- anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT
+ anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT,
+ anchorAsContainer: true
});
}
diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts
index a61fd2c5bab..b08b72a9479 100644
--- a/src/vs/base/browser/ui/grid/gridview.ts
+++ b/src/vs/base/browser/ui/grid/gridview.ts
@@ -380,7 +380,7 @@ class BranchNode implements ISplitView, IDisposable {
throw new Error('Invalid index');
}
- this.splitview.addView(node, size, index);
+ this.splitview.addView(node, size, index, skipLayout);
this._addChild(node, index);
this.onDidChildrenChange();
}
@@ -791,7 +791,7 @@ function flipNode(node: T, size: number, orthogonalSize: number)
newSize += size - totalSize;
}
- result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0);
+ result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0, true);
}
return result as T;
diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css
similarity index 57%
rename from src/vs/editor/contrib/hover/hover.css
rename to src/vs/base/browser/ui/hover/hover.css
index 9c7aae9f330..33957a8e43b 100644
--- a/src/vs/editor/contrib/hover/hover.css
+++ b/src/vs/base/browser/ui/hover/hover.css
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-.monaco-editor-hover {
+.monaco-hover {
cursor: default;
position: absolute;
overflow: hidden;
@@ -16,34 +16,34 @@
line-height: 1.5em;
}
-.monaco-editor-hover.hidden {
+.monaco-hover.hidden {
display: none;
}
-.monaco-editor-hover .hover-contents {
+.monaco-hover .hover-contents {
padding: 4px 8px;
}
-.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) {
+.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) {
max-width: 500px;
word-wrap: break-word;
}
-.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr {
+.monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr {
/* This is a strange rule but it avoids https://github.com/microsoft/vscode/issues/96795, just 100vw on its own caused the actual hover width to increase */
min-width: calc(100% + 100vw);
}
-.monaco-editor-hover p,
-.monaco-editor-hover ul {
+.monaco-hover p,
+.monaco-hover ul {
margin: 8px 0;
}
-.monaco-editor-hover code {
+.monaco-hover code {
font-family: var(--monaco-monospace-font);
}
-.monaco-editor-hover hr {
+.monaco-hover hr {
margin-top: 4px;
margin-bottom: -6px;
margin-left: -10px;
@@ -51,78 +51,78 @@
height: 1px;
}
-.monaco-editor-hover p:first-child,
-.monaco-editor-hover ul:first-child {
+.monaco-hover p:first-child,
+.monaco-hover ul:first-child {
margin-top: 0;
}
-.monaco-editor-hover p:last-child,
-.monaco-editor-hover ul:last-child {
+.monaco-hover p:last-child,
+.monaco-hover ul:last-child {
margin-bottom: 0;
}
/* MarkupContent Layout */
-.monaco-editor-hover ul {
+.monaco-hover ul {
padding-left: 20px;
}
-.monaco-editor-hover ol {
+.monaco-hover ol {
padding-left: 20px;
}
-.monaco-editor-hover li > p {
+.monaco-hover li > p {
margin-bottom: 0;
}
-.monaco-editor-hover li > ul {
+.monaco-hover li > ul {
margin-top: 0;
}
-.monaco-editor-hover code {
+.monaco-hover code {
border-radius: 3px;
padding: 0 0.4em;
}
-.monaco-editor-hover .monaco-tokenized-source {
+.monaco-hover .monaco-tokenized-source {
white-space: pre-wrap;
word-break: break-all;
}
-.monaco-editor-hover .hover-row.status-bar {
+.monaco-hover .hover-row.status-bar {
font-size: 12px;
line-height: 22px;
}
-.monaco-editor-hover .hover-row.status-bar .actions {
+.monaco-hover .hover-row.status-bar .actions {
display: flex;
padding: 0px 8px;
}
-.monaco-editor-hover .hover-row.status-bar .actions .action-container {
+.monaco-hover .hover-row.status-bar .actions .action-container {
margin-right: 16px;
cursor: pointer;
}
-.monaco-editor-hover .hover-row.status-bar .actions .action-container .action .icon {
+.monaco-hover .hover-row.status-bar .actions .action-container .action .icon {
padding-right: 4px;
}
-.monaco-editor-hover .markdown-hover .hover-contents .codicon {
+.monaco-hover .markdown-hover .hover-contents .codicon {
color: inherit;
font-size: inherit;
vertical-align: middle;
}
-.monaco-editor-hover .hover-contents a.code-link:before {
+.monaco-hover .hover-contents a.code-link:before {
content: '(';
}
-.monaco-editor-hover .hover-contents a.code-link:after {
+.monaco-hover .hover-contents a.code-link:after {
content: ')';
}
-.monaco-editor-hover .hover-contents a.code-link {
+.monaco-hover .hover-contents a.code-link {
color: inherit;
}
-.monaco-editor-hover .hover-contents a.code-link > span {
+.monaco-hover .hover-contents a.code-link > span {
text-decoration: underline;
/** Hack to force underline to show **/
border-bottom: 1px solid transparent;
diff --git a/src/vs/base/browser/ui/hover/hoverWidget.ts b/src/vs/base/browser/ui/hover/hoverWidget.ts
new file mode 100644
index 00000000000..6162569486e
--- /dev/null
+++ b/src/vs/base/browser/ui/hover/hoverWidget.ts
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import 'vs/css!./hover';
+import * as dom from 'vs/base/browser/dom';
+import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
+import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
+
+const $ = dom.$;
+
+export class HoverWidget extends Disposable {
+
+ public readonly containerDomNode: HTMLElement;
+ public readonly contentsDomNode: HTMLElement;
+ private readonly _scrollbar: DomScrollableElement;
+
+ constructor() {
+ super();
+
+ this.containerDomNode = document.createElement('div');
+ this.containerDomNode.className = 'monaco-hover';
+ this.containerDomNode.tabIndex = 0;
+ this.containerDomNode.setAttribute('role', 'tooltip');
+
+ this.contentsDomNode = document.createElement('div');
+ this.contentsDomNode.className = 'monaco-hover-content';
+
+ this._scrollbar = this._register(new DomScrollableElement(this.contentsDomNode, {}));
+ this.containerDomNode.appendChild(this._scrollbar.getDomNode());
+ }
+
+ public onContentsChanged(): void {
+ this._scrollbar.scanDomNode();
+ }
+}
+
+export function renderHoverAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }, keybindingLabel: string | null): IDisposable {
+ const actionContainer = dom.append(parent, $('div.action-container'));
+ const action = dom.append(actionContainer, $('a.action'));
+ action.setAttribute('href', '#');
+ action.setAttribute('role', 'button');
+ if (actionOptions.iconClass) {
+ dom.append(action, $(`span.icon.${actionOptions.iconClass}`));
+ }
+ const label = dom.append(action, $('span'));
+ label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label;
+ return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => {
+ e.stopPropagation();
+ e.preventDefault();
+ actionOptions.run(actionContainer);
+ });
+}
diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts
index b76361c2695..84978eab45d 100644
--- a/src/vs/base/browser/ui/list/listView.ts
+++ b/src/vs/base/browser/ui/list/listView.ts
@@ -9,8 +9,8 @@ import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/brow
import * as DOM from 'vs/base/browser/dom';
import { Event, Emitter } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
-import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
-import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable';
+import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
+import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions, Scrollable } from 'vs/base/common/scrollable';
import { RangeMap, shift } from './rangeMap';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list';
import { RowCache, IRow } from './rowCache';
@@ -44,11 +44,16 @@ export interface IListViewDragAndDrop extends IListDragAndDrop {
export interface IListViewAccessibilityProvider {
getSetSize?(element: T, index: number, listLength: number): number;
getPosInSet?(element: T, index: number): number;
- getRole?(element: T): string;
+ getRole?(element: T): string | undefined;
isChecked?(element: T): boolean | undefined;
}
-export interface IListViewOptions {
+export interface IListViewOptionsUpdate {
+ readonly additionalScrollHeight?: number;
+ readonly smoothScrolling?: boolean;
+}
+
+export interface IListViewOptions extends IListViewOptionsUpdate {
readonly dnd?: IListViewDragAndDrop;
readonly useShadows?: boolean;
readonly verticalScrollMode?: ScrollbarVisibility;
@@ -58,7 +63,6 @@ export interface IListViewOptions {
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly accessibilityProvider?: IListViewAccessibilityProvider;
- readonly additionalScrollHeight?: number;
readonly transformOptimization?: boolean;
}
@@ -158,7 +162,7 @@ class ListViewAccessibilityProvider implements Required number;
readonly getPosInSet: (element: any, index: number) => number;
- readonly getRole: (element: T) => string;
+ readonly getRole: (element: T) => string | undefined;
readonly isChecked: (element: T) => boolean | undefined;
constructor(accessibilityProvider?: IListViewAccessibilityProvider) {
@@ -204,7 +208,8 @@ export class ListView implements ISpliceable, IDisposable {
private lastRenderHeight: number;
private renderWidth = 0;
private rowsContainer: HTMLElement;
- private scrollableElement: ScrollableElement;
+ private scrollable: Scrollable;
+ private scrollableElement: SmoothScrollableElement;
private _scrollHeight: number = 0;
private scrollableElementUpdateDisposable: IDisposable | null = null;
private scrollableElementWidthDelayer = new Delayer(50);
@@ -285,12 +290,13 @@ export class ListView implements ISpliceable, IDisposable {
this.disposables.add(Gesture.addTarget(this.rowsContainer));
- this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, {
+ this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => DOM.scheduleAtNextAnimationFrame(cb));
+ this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, {
alwaysConsumeMouseWheel: true,
horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden,
vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
- useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows)
- }));
+ useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows),
+ }, this.scrollable));
this.domNode.appendChild(this.scrollableElement.getDomNode());
container.appendChild(this.domNode);
@@ -320,6 +326,10 @@ export class ListView implements ISpliceable, IDisposable {
if (options.additionalScrollHeight !== undefined) {
this.additionalScrollHeight = options.additionalScrollHeight;
}
+
+ if (options.smoothScrolling !== undefined) {
+ this.scrollable.setSmoothScrollDuration(options.smoothScrolling ? 125 : 0);
+ }
}
triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {
@@ -652,7 +662,7 @@ export class ListView implements ISpliceable, IDisposable {
if (!item.row) {
item.row = this.cache.alloc(item.templateId);
- const role = this.accessibilityProvider.getRole(item.element);
+ const role = this.accessibilityProvider.getRole(item.element) || 'listitem';
item.row!.domNode!.setAttribute('role', role);
const checked = this.accessibilityProvider.isChecked(item.element);
if (typeof checked !== 'undefined') {
diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts
index 856b97f1a6a..ca09bbf018a 100644
--- a/src/vs/base/browser/ui/list/listWidget.ts
+++ b/src/vs/base/browser/ui/list/listWidget.ts
@@ -16,7 +16,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list';
-import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider } from './listView';
+import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
@@ -1107,10 +1107,9 @@ class ListViewDragAndDrop implements IListViewDragAndDrop {
}
}
-export interface IListOptionsUpdate {
+export interface IListOptionsUpdate extends IListViewOptionsUpdate {
readonly enableKeyboardNavigation?: boolean;
readonly automaticKeyboardNavigation?: boolean;
- readonly additionalScrollHeight?: number;
}
export class List implements ISpliceable, IDisposable {
@@ -1288,9 +1287,7 @@ export class List implements ISpliceable, IDisposable {
this.typeLabelController.updateOptions(this._options);
}
- if (optionsUpdate.additionalScrollHeight !== undefined) {
- this.view.updateOptions(optionsUpdate);
- }
+ this.view.updateOptions(optionsUpdate);
}
get options(): IListOptions {
diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css
index 140168678a2..d86ee055d51 100644
--- a/src/vs/base/browser/ui/menu/menu.css
+++ b/src/vs/base/browser/ui/menu/menu.css
@@ -88,6 +88,9 @@
padding: 0.5em 0 0 0;
margin-bottom: 0.5em;
width: 100%;
+ height: 0px !important;
+ margin-left: .8em !important;
+ margin-right: .8em !important;
}
.monaco-menu .monaco-action-bar.vertical .action-label.separator.text {
diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts
index 8612b5b1b84..ed48038df11 100644
--- a/src/vs/base/browser/ui/menu/menu.ts
+++ b/src/vs/base/browser/ui/menu/menu.ts
@@ -41,6 +41,7 @@ export interface IMenuOptions {
enableMnemonics?: boolean;
anchorAlignment?: AnchorAlignment;
expandDirection?: Direction;
+ useEventAsContext?: boolean;
}
export interface IMenuStyles {
@@ -316,7 +317,7 @@ export class Menu extends ActionBar {
return menuActionViewItem;
} else {
- const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics };
+ const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics, useEventAsContext: options.useEventAsContext };
if (options.getKeyBinding) {
const keybinding = options.getKeyBinding(action);
if (keybinding) {
diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts
index 463073dc0a7..f4506d877b5 100644
--- a/src/vs/base/browser/ui/menu/menubar.ts
+++ b/src/vs/base/browser/ui/menu/menubar.ts
@@ -945,7 +945,8 @@ export class MenuBar extends Disposable {
actionRunner: this.actionRunner,
enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics),
ariaLabel: withNullAsUndefined(customMenu.buttonElement.getAttribute('aria-label')),
- expandDirection: this.options.compactMode !== undefined ? this.options.compactMode : Direction.Right
+ expandDirection: this.options.compactMode !== undefined ? this.options.compactMode : Direction.Right,
+ useEventAsContext: true
};
let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions));
diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts
index da2f2b6cee8..933af1512ed 100644
--- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts
+++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts
@@ -533,6 +533,14 @@ export class SmoothScrollableElement extends AbstractScrollableElement {
super(element, options, scrollable);
}
+ public setScrollPosition(update: INewScrollPosition): void {
+ this._scrollable.setScrollPositionNow(update);
+ }
+
+ public getScrollPosition(): IScrollPosition {
+ return this._scrollable.getCurrentScrollPosition();
+ }
+
}
export class DomScrollableElement extends ScrollableElement {
diff --git a/src/vs/base/browser/ui/selectBox/selectBox.css b/src/vs/base/browser/ui/selectBox/selectBox.css
index f684dd1085c..199ca5f98b8 100644
--- a/src/vs/base/browser/ui/selectBox/selectBox.css
+++ b/src/vs/base/browser/ui/selectBox/selectBox.css
@@ -6,3 +6,9 @@
.monaco-select-box {
width: 100%;
}
+
+.monaco-select-box-dropdown-container {
+ font-size: 13px;
+ font-weight: normal;
+ text-transform: none;
+}
diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts
index cff14f50206..79f0da0e6cd 100644
--- a/src/vs/base/browser/ui/selectBox/selectBox.ts
+++ b/src/vs/base/browser/ui/selectBox/selectBox.ts
@@ -40,6 +40,7 @@ export interface ISelectBoxOptions {
useCustomDrawn?: boolean;
ariaLabel?: string;
minBottomMargin?: number;
+ optionsAsChildren?: boolean;
}
// Utilize optionItem interface to capture all option parameters
diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts
index 4924646f11c..efb7421773a 100644
--- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts
+++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts
@@ -89,6 +89,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
private _isVisible: boolean;
private selectBoxOptions: ISelectBoxOptions;
private selectElement: HTMLSelectElement;
+ private container?: HTMLElement;
private options: ISelectOptionItem[] = [];
private selected: number;
private readonly _onDidSelect: Emitter;
@@ -307,6 +308,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
}
public render(container: HTMLElement): void {
+ this.container = container;
dom.addClass(container, 'select-container');
container.appendChild(this.selectElement);
this.applyStyles();
@@ -442,7 +444,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
dom.toggleClass(this.selectElement, 'synthetic-focus', false);
},
anchorPosition: this._dropDownPosition
- });
+ }, this.selectBoxOptions.optionsAsChildren ? this.container : undefined);
// Hide so we can relay out
this._isVisible = true;
@@ -457,7 +459,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
dom.toggleClass(this.selectElement, 'synthetic-focus', false);
},
anchorPosition: this._dropDownPosition
- });
+ }, this.selectBoxOptions.optionsAsChildren ? this.container : undefined);
// Track initial selection the case user escape, blur
this._currentSelection = this.selected;
@@ -727,7 +729,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
mouseSupport: false,
accessibilityProvider: {
getAriaLabel: (element) => element.text,
- getWidgetAriaLabel: () => localize('selectBox', "Select Box"),
+ getWidgetAriaLabel: () => localize({ key: 'selectBox', comment: ['Behave like native select dropdown element.'] }, "Select Box"),
getRole: () => 'option',
getWidgetRole: () => 'listbox'
}
diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts
index 1a7c66cc8a9..292764c8807 100644
--- a/src/vs/base/browser/ui/splitview/paneview.ts
+++ b/src/vs/base/browser/ui/splitview/paneview.ts
@@ -126,7 +126,7 @@ export abstract class Pane extends Disposable implements IView {
this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded;
this._orientation = typeof options.orientation === 'undefined' ? Orientation.VERTICAL : options.orientation;
this.ariaHeaderLabel = localize('viewSection', "{0} Section", options.title);
- this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120;
+ this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 200 : 120;
this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY;
this.element = $('.pane');
@@ -263,8 +263,6 @@ export abstract class Pane extends Disposable implements IView {
style(styles: IPaneStyles): void {
this.styles = styles;
- this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : '';
-
if (!this.header) {
return;
}
@@ -284,6 +282,7 @@ export abstract class Pane extends Disposable implements IView {
this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : '';
this.header.style.borderTop = this.styles.headerBorder && this.orientation === Orientation.VERTICAL ? `1px solid ${this.styles.headerBorder}` : '';
this._dropBackground = this.styles.dropBackground;
+ this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : '';
}
protected abstract renderHeader(container: HTMLElement): void;
diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts
index 863623a8c0d..482a3a3d51e 100644
--- a/src/vs/base/browser/ui/splitview/splitview.ts
+++ b/src/vs/base/browser/ui/splitview/splitview.ts
@@ -331,8 +331,8 @@ export class SplitView extends Disposable {
}
}
- addView(view: IView, size: number | Sizing, index = this.viewItems.length): void {
- this.doAddView(view, size, index, false);
+ addView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
+ this.doAddView(view, size, index, skipLayout);
}
removeView(index: number, sizing?: Sizing): IView {
diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts
index a71e4d6ae48..bfac12835d1 100644
--- a/src/vs/base/browser/ui/tree/abstractTree.ts
+++ b/src/vs/base/browser/ui/tree/abstractTree.ts
@@ -707,7 +707,7 @@ class TypeFilterController implements IDisposable {
.map(e => new StandardKeyboardEvent(e))
.filter(this.keyboardNavigationEventFilter || (() => true))
.filter(() => this.automaticKeyboardNavigation || this.triggered)
- .filter(e => this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey)))
+ .filter(e => (this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) && !(e.keyCode === KeyCode.DownArrow || e.keyCode === KeyCode.UpArrow || e.keyCode === KeyCode.LeftArrow || e.keyCode === KeyCode.RightArrow)) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey)))
.forEach(e => { e.stopPropagation(); e.preventDefault(); })
.event;
@@ -962,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
readonly simpleKeyboardNavigation?: boolean;
readonly filterOnType?: boolean;
readonly openOnSingleClick?: boolean;
+ readonly smoothScrolling?: boolean;
}
export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions {
@@ -1360,7 +1361,8 @@ export abstract class AbstractTree implements IDisposable
this.view.updateOptions({
enableKeyboardNavigation: this._options.simpleKeyboardNavigation,
- automaticKeyboardNavigation: this._options.automaticKeyboardNavigation
+ automaticKeyboardNavigation: this._options.automaticKeyboardNavigation,
+ smoothScrolling: this._options.smoothScrolling
});
if (this.typeFilterController) {
diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts
index 3002b4bcb59..3c861744021 100644
--- a/src/vs/base/common/codicons.ts
+++ b/src/vs/base/common/codicons.ts
@@ -244,7 +244,7 @@ export namespace Codicon {
export const collapseAll = new Codicon('collapse-all', { character: '\\eac5' });
export const colorMode = new Codicon('color-mode', { character: '\\eac6' });
export const commentDiscussion = new Codicon('comment-discussion', { character: '\\eac7' });
- export const compareChanges = new Codicon('compare-changes', { character: '\\eac8' });
+ export const compareChanges = new Codicon('compare-changes', { character: '\\eafd' });
export const creditCard = new Codicon('credit-card', { character: '\\eac9' });
export const dash = new Codicon('dash', { character: '\\eacc' });
export const dashboard = new Codicon('dashboard', { character: '\\eacd' });
@@ -448,7 +448,6 @@ export namespace Codicon {
export const debugReverseContinue = new Codicon('debug-reverse-continue', { character: '\\eb8e' });
export const debugStepBack = new Codicon('debug-step-back', { character: '\\eb8f' });
export const debugRestartFrame = new Codicon('debug-restart-frame', { character: '\\eb90' });
- export const debugAlternate = new Codicon('debug-alternate', { character: '\\eb91' });
export const callIncoming = new Codicon('call-incoming', { character: '\\eb92' });
export const callOutgoing = new Codicon('call-outgoing', { character: '\\eb93' });
export const menu = new Codicon('menu', { character: '\\eb94' });
@@ -465,10 +464,13 @@ export namespace Codicon {
export const syncIgnored = new Codicon('sync-ignored', { character: '\\eb9f' });
export const pinned = new Codicon('pinned', { character: '\\eba0' });
export const githubInverted = new Codicon('github-inverted', { character: '\\eba1' });
- export const debugAlt2 = new Codicon('debug-alt-2', { character: '\\f101' });
- export const debugAlt = new Codicon('debug-alt', { character: '\\f102' });
+ export const debugAlt = new Codicon('debug-alt', { character: '\\eb91' });
export const serverProcess = new Codicon('server-process', { character: '\\eba2' });
export const serverEnvironment = new Codicon('server-environment', { character: '\\eba3' });
+ export const pass = new Codicon('pass', { character: '\\eba4' });
+ export const stopCircle = new Codicon('stop-circle', { character: '\\eba5' });
+ export const playCircle = new Codicon('play-circle', { character: '\\eba6' });
+ export const record = new Codicon('record', { character: '\\eba7' });
}
diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts
index 032f447081e..b0bd0ab2799 100644
--- a/src/vs/base/common/event.ts
+++ b/src/vs/base/common/event.ts
@@ -328,8 +328,8 @@ export namespace Event {
}
export interface NodeEventEmitter {
- on(event: string | symbol, listener: Function): this;
- removeListener(event: string | symbol, listener: Function): this;
+ on(event: string | symbol, listener: Function): unknown;
+ removeListener(event: string | symbol, listener: Function): unknown;
}
export function fromNodeEventEmitter(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event {
diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts
index e064829c5a0..8fe9f9e1c2a 100644
--- a/src/vs/base/common/extpath.ts
+++ b/src/vs/base/common/extpath.ts
@@ -7,6 +7,7 @@ import { isWindows } from 'vs/base/common/platform';
import { startsWithIgnoreCase, equalsIgnoreCase, rtrim } from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
import { sep, posix, isAbsolute, join, normalize } from 'vs/base/common/path';
+import { isNumber } from 'vs/base/common/types';
export function isPathSeparator(code: number) {
return code === CharCode.Slash || code === CharCode.Backslash;
@@ -300,3 +301,38 @@ export function indexOfPath(path: string, candidate: string, ignoreCase: boolean
return path.indexOf(candidate);
}
+
+export interface IPathWithLineAndColumn {
+ path: string;
+ line?: number;
+ column?: number;
+}
+
+export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn {
+ const segments = rawPath.split(':'); // C:\file.txt::
+
+ let path: string | undefined = undefined;
+ let line: number | undefined = undefined;
+ let column: number | undefined = undefined;
+
+ segments.forEach(segment => {
+ const segmentAsNumber = Number(segment);
+ if (!isNumber(segmentAsNumber)) {
+ path = !!path ? [path, segment].join(':') : segment; // a colon can well be part of a path (e.g. C:\...)
+ } else if (line === undefined) {
+ line = segmentAsNumber;
+ } else if (column === undefined) {
+ column = segmentAsNumber;
+ }
+ });
+
+ if (!path) {
+ throw new Error('Format for `--goto` should be: `FILE:LINE(:COLUMN)`');
+ }
+
+ return {
+ path,
+ line: line !== undefined ? line : undefined,
+ column: column !== undefined ? column : line !== undefined ? 1 : undefined // if we have a line, make sure column is also set
+ };
+}
diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts
index 8599e947205..278dc001699 100644
--- a/src/vs/base/common/fuzzyScorer.ts
+++ b/src/vs/base/common/fuzzyScorer.ts
@@ -833,7 +833,7 @@ export interface IPreparedQuery extends IPreparedQueryPiece {
values: IPreparedQueryPiece[] | undefined;
/**
- * Wether the query contains path separator(s) or not.
+ * Whether the query contains path separator(s) or not.
*/
containsPathSeparator: boolean;
}
diff --git a/src/vs/base/common/insane/insane.d.ts b/src/vs/base/common/insane/insane.d.ts
index 9b5a77c3b8e..13fa1f2662b 100644
--- a/src/vs/base/common/insane/insane.d.ts
+++ b/src/vs/base/common/insane/insane.d.ts
@@ -9,6 +9,7 @@ export function insane(
readonly allowedSchemes?: readonly string[],
readonly allowedTags?: readonly string[],
readonly allowedAttributes?: { readonly [key: string]: string[] },
+ readonly filter?: (token: { tag: string, attrs: { readonly [key: string]: string } }) => boolean,
},
strict?: boolean,
): string;
diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts
index 671816830bd..1cdbc8d8e9e 100644
--- a/src/vs/base/common/keybindingLabels.ts
+++ b/src/vs/base/common/keybindingLabels.ts
@@ -182,7 +182,9 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe
}
// the actual key
- result.push(key);
+ if (key !== '') {
+ result.push(key);
+ }
return result.join(labels.separator);
}
diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts
index e43ab8e5f1d..ed8c14248af 100644
--- a/src/vs/base/common/map.ts
+++ b/src/vs/base/common/map.ts
@@ -486,7 +486,6 @@ export class ResourceMap implements Map {
readonly [Symbol.toStringTag] = 'ResourceMap';
protected readonly map: Map;
- protected readonly ignoreCase?: boolean = false; // in the future this should be an uri-comparator
constructor(other?: ResourceMap) {
this.map = other ? new Map(other.map) : new Map();
@@ -549,20 +548,7 @@ export class ResourceMap implements Map {
}
private toKey(resource: URI): string {
- let key = resource.toString();
- if (this.ignoreCase) {
- key = key.toLowerCase();
- }
-
- return key;
- }
-
- clone(): ResourceMap {
- const resourceMap = new ResourceMap();
-
- this.map.forEach((value, key) => resourceMap.map.set(key, value));
-
- return resourceMap;
+ return resource.toString();
}
}
diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts
index 0bf9ce1cc6b..26c88f7e3b0 100644
--- a/src/vs/base/common/mime.ts
+++ b/src/vs/base/common/mime.ts
@@ -335,3 +335,13 @@ export function getMediaMime(path: string): string | undefined {
const ext = extname(path);
return mapExtToMediaMimes[ext.toLowerCase()];
}
+
+export function getExtensionForMimeType(mimeType: string): string | undefined {
+ for (const extension in mapExtToMediaMimes) {
+ if (mapExtToMediaMimes[extension] === mimeType) {
+ return extension;
+ }
+ }
+
+ return undefined;
+}
diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts
index e4546b2cf60..65a18ee4cbb 100644
--- a/src/vs/base/common/network.ts
+++ b/src/vs/base/common/network.ts
@@ -59,6 +59,8 @@ export namespace Schemas {
export const vscodeSettings = 'vscode-settings';
export const webviewPanel = 'webview-panel';
+
+ export const vscodeWebviewResource = 'vscode-webview-resource';
}
class RemoteAuthoritiesImpl {
diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts
index 616e2b03b38..9ffce85d3e8 100644
--- a/src/vs/base/common/resources.ts
+++ b/src/vs/base/common/resources.ts
@@ -17,279 +17,368 @@ export function originalFSPath(uri: URI): string {
return uriToFsPath(uri, true);
}
-// DO NOT EXPORT, DO NOT USE
-function _hasToIgnoreCase(resource: URI | undefined): boolean {
- // A file scheme resource is in the same platform as code, so ignore case for non linux platforms
- // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
- return resource && resource.scheme === Schemas.file ? !isLinux : true;
+//#region IExtUri
+
+export interface IExtUri {
+
+ // --- identity
+
+ /**
+ * Compares two uris.
+ *
+ * @param uri1 Uri
+ * @param uri2 Uri
+ * @param ignoreFragment Ignore the fragment (defaults to `false`)
+ */
+ compare(uri1: URI, uri2: URI, ignoreFragment?: boolean): number;
+
+ /**
+ * Tests whether two uris are equal
+ *
+ * @param uri1 Uri
+ * @param uri2 Uri
+ * @param ignoreFragment Ignore the fragment (defaults to `false`)
+ */
+ isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment?: boolean): boolean;
+
+ /**
+ * Tests whether a `candidate` URI is a parent or equal of a given `base` URI.
+ *
+ * @param base A uri which is "longer"
+ * @param parentCandidate A uri which is "shorter" then `base`
+ * @param ignoreFragment Ignore the fragment (defaults to `false`)
+ */
+ isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment?: boolean): boolean;
+
+ /**
+ * Creates a key from a resource URI to be used to resource comparison and for resource maps.
+ * @see ResourceMap
+ * @param uri Uri
+ * @param ignoreFragment Ignore the fragment (defaults to `false`)
+ */
+ getComparisonKey(uri: URI, ignoreFragment?: boolean): string;
+
+ // --- path math
+
+ basenameOrAuthority(resource: URI): string;
+
+ /**
+ * Returns the basename of the path component of an uri.
+ * @param resource
+ */
+ basename(resource: URI): string;
+
+ /**
+ * Returns the extension of the path component of an uri.
+ * @param resource
+ */
+ extname(resource: URI): string;
+ /**
+ * Return a URI representing the directory of a URI path.
+ *
+ * @param resource The input URI.
+ * @returns The URI representing the directory of the input URI.
+ */
+ dirname(resource: URI): URI;
+ /**
+ * Join a URI path with path fragments and normalizes the resulting path.
+ *
+ * @param resource The input URI.
+ * @param pathFragment The path fragment to add to the URI path.
+ * @returns The resulting URI.
+ */
+ joinPath(resource: URI, ...pathFragment: string[]): URI
+ /**
+ * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names.
+ *
+ * @param resource The URI to normalize the path.
+ * @returns The URI with the normalized path.
+ */
+ normalizePath(resource: URI): URI;
+ /**
+ *
+ * @param from
+ * @param to
+ */
+ relativePath(from: URI, to: URI): string | undefined;
+ /**
+ * Resolves an absolute or relative path against a base URI.
+ * The path can be relative or absolute posix or a Windows path
+ */
+ resolvePath(base: URI, path: string): URI;
+
+ // --- misc
+
+ /**
+ * Returns true if the URI path is absolute.
+ */
+ isAbsolutePath(resource: URI): boolean;
+ /**
+ * Tests whether the two authorities are the same
+ */
+ isEqualAuthority(a1: string, a2: string): boolean;
+ /**
+ * Returns true if the URI path has a trailing path separator
+ */
+ hasTrailingPathSeparator(resource: URI, sep?: string): boolean;
+ /**
+ * Removes a trailing path separator, if there's one.
+ * Important: Doesn't remove the first slash, it would make the URI invalid
+ */
+ removeTrailingPathSeparator(resource: URI, sep?: string): URI;
+ /**
+ * Adds a trailing path separator to the URI if there isn't one already.
+ * For example, c:\ would be unchanged, but c:\users would become c:\users\
+ */
+ addTrailingPathSeparator(resource: URI, sep?: string): URI;
}
-/**
- * Creates a key from a resource URI to be used to resource comparison and for resource maps.
- *
- * @param resource Uri
- * @param caseInsensitivePath Ignore casing when comparing path component (defaults mostly to `true`)
- * @param ignoreFragment Ignore the fragment (defaults to `false`)
- */
-export function getComparisonKey(resource: URI, caseInsensitivePath: boolean = _hasToIgnoreCase(resource), ignoreFragment: boolean = false): string {
- return resource.with({
- path: caseInsensitivePath ? resource.path.toLowerCase() : undefined,
- fragment: ignoreFragment ? null : undefined
- }).toString();
-}
+export class ExtUri implements IExtUri {
-/**
- * Tests whether two uris are equal
- *
- * @param first Uri
- * @param second Uri
- * @param caseInsensitivePath Ignore casing when comparing path component (defaults mostly to `true`)
- * @param ignoreFragment Ignore the fragment (defaults to `false`)
- */
-export function isEqual(first: URI | undefined, second: URI | undefined, caseInsensitivePath: boolean = _hasToIgnoreCase(first), ignoreFragment: boolean = false): boolean {
- if (first === second) {
- return true;
- }
- if (!first || !second) {
- return false;
- }
- if (first.scheme !== second.scheme || !isEqualAuthority(first.authority, second.authority)) {
- return false;
- }
- const p1 = first.path, p2 = second.path;
- return (p1 === p2 || caseInsensitivePath && equalsIgnoreCase(p1, p2)) && first.query === second.query && (ignoreFragment || first.fragment === second.fragment);
-}
+ constructor(private _ignorePathCasing: (uri: URI) => boolean) { }
-export function compare(uri1: URI, uri2: URI, caseInsensitivePath: boolean = _hasToIgnoreCase(uri1), ignoreFragment: boolean = false): number {
- // scheme
- let ret = strCompare(uri1.scheme, uri2.scheme);
- if (ret === 0) {
- // authority
- ret = compareIgnoreCase(uri1.authority, uri2.authority);
+ compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number {
+ // scheme
+ let ret = strCompare(uri1.scheme, uri2.scheme);
if (ret === 0) {
- // path
- ret = caseInsensitivePath ? compareIgnoreCase(uri1.path, uri2.path) : strCompare(uri1.path, uri2.path);
- // query
+ // authority
+ ret = compareIgnoreCase(uri1.authority, uri2.authority);
if (ret === 0) {
- ret = strCompare(uri1.query, uri2.query);
- // fragment
- if (ret === 0 && !ignoreFragment) {
- ret = strCompare(uri1.fragment, uri2.fragment);
+ // path
+ ret = this._ignorePathCasing(uri1) ? compareIgnoreCase(uri1.path, uri2.path) : strCompare(uri1.path, uri2.path);
+ // query
+ if (ret === 0) {
+ ret = strCompare(uri1.query, uri2.query);
+ // fragment
+ if (ret === 0 && !ignoreFragment) {
+ ret = strCompare(uri1.fragment, uri2.fragment);
+ }
}
}
}
+ return ret;
}
- return ret;
-}
-/**
- * Tests whether a `candidate` URI is a parent or equal of a given `base` URI.
- *
- * @param base A uri which is "longer"
- * @param parentCandidate A uri which is "shorter" then `base`
- * @param caseInsensitivePath Ignore casing when comparing path component (defaults mostly to `true`)
- * @param ignoreFragment Ignore the fragment (defaults to `false`)
- */
-export function isEqualOrParent(base: URI, parentCandidate: URI, caseInsensitivePath: boolean = _hasToIgnoreCase(base), ignoreFragment: boolean = false): boolean {
- if (base.scheme === parentCandidate.scheme) {
- if (base.scheme === Schemas.file) {
- return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), caseInsensitivePath) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
+ getComparisonKey(uri: URI, ignoreFragment: boolean = false): string {
+ return uri.with({
+ path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
+ fragment: ignoreFragment ? null : undefined
+ }).toString();
+ }
+
+ isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment: boolean = false): boolean {
+ if (uri1 === uri2) {
+ return true;
}
- if (isEqualAuthority(base.authority, parentCandidate.authority)) {
- return extpath.isEqualOrParent(base.path, parentCandidate.path, caseInsensitivePath, '/') && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
+ if (!uri1 || !uri2) {
+ return false;
}
- }
- return false;
-}
-
-
-export function basenameOrAuthority(resource: URI): string {
- return basename(resource) || resource.authority;
-}
-
-/**
- * Tests whether the two authorities are the same
- */
-export function isEqualAuthority(a1: string, a2: string) {
- return a1 === a2 || equalsIgnoreCase(a1, a2);
-}
-
-export function basename(resource: URI): string {
- return paths.posix.basename(resource.path);
-}
-
-export function extname(resource: URI): string {
- return paths.posix.extname(resource.path);
-}
-
-/**
- * Return a URI representing the directory of a URI path.
- *
- * @param resource The input URI.
- * @returns The URI representing the directory of the input URI.
- */
-export function dirname(resource: URI): URI {
- if (resource.path.length === 0) {
- return resource;
- }
- let dirname;
- if (resource.scheme === Schemas.file) {
- dirname = URI.file(paths.dirname(originalFSPath(resource))).path;
- } else {
- dirname = paths.posix.dirname(resource.path);
- if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) {
- console.error(`dirname("${resource.toString})) resulted in a relative path`);
- dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character
+ if (uri1.scheme !== uri2.scheme || !isEqualAuthority(uri1.authority, uri2.authority)) {
+ return false;
}
+ if (uri1.toString() === uri2.toString()) {
+ // TODO@jrieken see https://github.com/microsoft/vscode/issues/98934
+ return true;
+ }
+ const p1 = uri1.path, p2 = uri2.path;
+ return (p1 === p2 || this._ignorePathCasing(uri1) && equalsIgnoreCase(p1, p2)) && uri1.query === uri2.query && (ignoreFragment || uri1.fragment === uri2.fragment);
}
- return resource.with({
- path: dirname
- });
-}
-/**
- * Join a URI path with path fragments and normalizes the resulting path.
- *
- * @param resource The input URI.
- * @param pathFragment The path fragment to add to the URI path.
- * @returns The resulting URI.
- */
-export function joinPath(resource: URI, ...pathFragment: string[]): URI {
- let joinedPath: string;
- if (resource.scheme === 'file') {
- joinedPath = URI.file(paths.join(originalFSPath(resource), ...pathFragment)).path;
- } else {
- joinedPath = paths.posix.join(resource.path || '/', ...pathFragment);
- }
- return resource.with({
- path: joinedPath
- });
-}
-
-/**
- * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names.
- *
- * @param resource The URI to normalize the path.
- * @returns The URI with the normalized path.
- */
-export function normalizePath(resource: URI): URI {
- if (!resource.path.length) {
- return resource;
- }
- let normalizedPath: string;
- if (resource.scheme === Schemas.file) {
- normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path;
- } else {
- normalizedPath = paths.posix.normalize(resource.path);
- }
- return resource.with({
- path: normalizedPath
- });
-}
-
-/**
- * Returns true if the URI path is absolute.
- */
-export function isAbsolutePath(resource: URI): boolean {
- return !!resource.path && resource.path[0] === '/';
-}
-
-/**
- * Returns true if the URI path has a trailing path separator
- */
-export function hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean {
- if (resource.scheme === Schemas.file) {
- const fsp = originalFSPath(resource);
- return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep;
- } else {
- const p = resource.path;
- return (p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0
- }
-}
-
-/**
- * Removes a trailing path separator, if there's one.
- * Important: Doesn't remove the first slash, it would make the URI invalid
- */
-export function removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
- // Make sure that the path isn't a drive letter. A trailing separator there is not removable.
- if (hasTrailingPathSeparator(resource, sep)) {
- return resource.with({ path: resource.path.substr(0, resource.path.length - 1) });
- }
- return resource;
-}
-
-/**
- * Adds a trailing path separator to the URI if there isn't one already.
- * For example, c:\ would be unchanged, but c:\users would become c:\users\
- */
-export function addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
- let isRootSep: boolean = false;
- if (resource.scheme === Schemas.file) {
- const fsp = originalFSPath(resource);
- isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep));
- } else {
- sep = '/';
- const p = resource.path;
- isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash;
- }
- if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) {
- return resource.with({ path: resource.path + '/' });
- }
- return resource;
-}
-
-/**
- * Returns a relative path between two URIs. If the URIs don't have the same schema or authority, `undefined` is returned.
- * The returned relative path always uses forward slashes.
- */
-export function relativePath(from: URI, to: URI, caseInsensitivePath = _hasToIgnoreCase(from)): string | undefined {
- if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
- return undefined;
- }
- if (from.scheme === Schemas.file) {
- const relativePath = paths.relative(originalFSPath(from), originalFSPath(to));
- return isWindows ? extpath.toSlashes(relativePath) : relativePath;
- }
- let fromPath = from.path || '/', toPath = to.path || '/';
- if (caseInsensitivePath) {
- // make casing of fromPath match toPath
- let i = 0;
- for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) {
- if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) {
- if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) {
- break;
- }
+ isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean {
+ if (base.scheme === parentCandidate.scheme) {
+ if (base.scheme === Schemas.file) {
+ return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), this._ignorePathCasing(base)) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
+ }
+ if (isEqualAuthority(base.authority, parentCandidate.authority)) {
+ return extpath.isEqualOrParent(base.path, parentCandidate.path, this._ignorePathCasing(base), '/') && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
}
}
- fromPath = toPath.substr(0, i) + fromPath.substr(i);
+ return false;
}
- return paths.posix.relative(fromPath, toPath);
-}
-/**
- * Resolves an absolute or relative path against a base URI.
- * The path can be relative or absolute posix or a Windows path
- */
-export function resolvePath(base: URI, path: string): URI {
- if (base.scheme === Schemas.file) {
- const newURI = URI.file(paths.resolve(originalFSPath(base), path));
- return base.with({
- authority: newURI.authority,
- path: newURI.path
+ // --- path math
+
+ joinPath(resource: URI, ...pathFragment: string[]): URI {
+ return URI.joinPath(resource, ...pathFragment);
+ }
+
+ basenameOrAuthority(resource: URI): string {
+ return basename(resource) || resource.authority;
+ }
+
+ basename(resource: URI): string {
+ return paths.posix.basename(resource.path);
+ }
+
+ extname(resource: URI): string {
+ return paths.posix.extname(resource.path);
+ }
+
+ dirname(resource: URI): URI {
+ if (resource.path.length === 0) {
+ return resource;
+ }
+ let dirname;
+ if (resource.scheme === Schemas.file) {
+ dirname = URI.file(paths.dirname(originalFSPath(resource))).path;
+ } else {
+ dirname = paths.posix.dirname(resource.path);
+ if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) {
+ console.error(`dirname("${resource.toString})) resulted in a relative path`);
+ dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character
+ }
+ }
+ return resource.with({
+ path: dirname
});
}
- if (path.indexOf('/') === -1) { // no slashes? it's likely a Windows path
- path = extpath.toSlashes(path);
- if (/^[a-zA-Z]:(\/|$)/.test(path)) { // starts with a drive letter
- path = '/' + path;
+
+ normalizePath(resource: URI): URI {
+ if (!resource.path.length) {
+ return resource;
+ }
+ let normalizedPath: string;
+ if (resource.scheme === Schemas.file) {
+ normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path;
+ } else {
+ normalizedPath = paths.posix.normalize(resource.path);
+ }
+ return resource.with({
+ path: normalizedPath
+ });
+ }
+
+ relativePath(from: URI, to: URI): string | undefined {
+ if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
+ return undefined;
+ }
+ if (from.scheme === Schemas.file) {
+ const relativePath = paths.relative(originalFSPath(from), originalFSPath(to));
+ return isWindows ? extpath.toSlashes(relativePath) : relativePath;
+ }
+ let fromPath = from.path || '/', toPath = to.path || '/';
+ if (this._ignorePathCasing(from)) {
+ // make casing of fromPath match toPath
+ let i = 0;
+ for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) {
+ if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) {
+ if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) {
+ break;
+ }
+ }
+ }
+ fromPath = toPath.substr(0, i) + fromPath.substr(i);
+ }
+ return paths.posix.relative(fromPath, toPath);
+ }
+
+ resolvePath(base: URI, path: string): URI {
+ if (base.scheme === Schemas.file) {
+ const newURI = URI.file(paths.resolve(originalFSPath(base), path));
+ return base.with({
+ authority: newURI.authority,
+ path: newURI.path
+ });
+ }
+ if (path.indexOf('/') === -1) { // no slashes? it's likely a Windows path
+ path = extpath.toSlashes(path);
+ if (/^[a-zA-Z]:(\/|$)/.test(path)) { // starts with a drive letter
+ path = '/' + path;
+ }
+ }
+ return base.with({
+ path: paths.posix.resolve(base.path, path)
+ });
+ }
+
+ // --- misc
+
+ isAbsolutePath(resource: URI): boolean {
+ return !!resource.path && resource.path[0] === '/';
+ }
+
+ isEqualAuthority(a1: string, a2: string) {
+ return a1 === a2 || equalsIgnoreCase(a1, a2);
+ }
+
+ hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean {
+ if (resource.scheme === Schemas.file) {
+ const fsp = originalFSPath(resource);
+ return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep;
+ } else {
+ const p = resource.path;
+ return (p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0
}
}
- return base.with({
- path: paths.posix.resolve(base.path, path)
- });
+
+ removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
+ // Make sure that the path isn't a drive letter. A trailing separator there is not removable.
+ if (hasTrailingPathSeparator(resource, sep)) {
+ return resource.with({ path: resource.path.substr(0, resource.path.length - 1) });
+ }
+ return resource;
+ }
+
+ addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
+ let isRootSep: boolean = false;
+ if (resource.scheme === Schemas.file) {
+ const fsp = originalFSPath(resource);
+ isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep));
+ } else {
+ sep = '/';
+ const p = resource.path;
+ isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash;
+ }
+ if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) {
+ return resource.with({ path: resource.path + '/' });
+ }
+ return resource;
+ }
}
+/**
+ * Unbiased utility that takes uris "as they are". This means it can be interchanged with
+ * uri#toString() usages. The following is true
+ * ```
+ * assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri))
+ * ```
+ */
+export const extUri = new ExtUri(() => false);
+
+/**
+ * BIASED utility that always ignores the casing of uris path. ONLY use these util if you
+ * understand what you are doing.
+ *
+ * Note that `IUriIdentityService#extUri` is a better replacement for this because that utility
+ * knows when path casing matters and when not.
+ */
+export const extUriIgnorePathCase = new ExtUri(_ => true);
+
+const exturiBiasedIgnorePathCase = new ExtUri(uri => {
+ // A file scheme resource is in the same platform as code, so ignore case for non linux platforms
+ // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
+ return uri && uri.scheme === Schemas.file ? !isLinux : true;
+});
+
+export const isEqual = exturiBiasedIgnorePathCase.isEqual.bind(exturiBiasedIgnorePathCase);
+export const isEqualOrParent = exturiBiasedIgnorePathCase.isEqualOrParent.bind(exturiBiasedIgnorePathCase);
+export const getComparisonKey = exturiBiasedIgnorePathCase.getComparisonKey.bind(exturiBiasedIgnorePathCase);
+export const basenameOrAuthority = exturiBiasedIgnorePathCase.basenameOrAuthority.bind(exturiBiasedIgnorePathCase);
+export const basename = exturiBiasedIgnorePathCase.basename.bind(exturiBiasedIgnorePathCase);
+export const extname = exturiBiasedIgnorePathCase.extname.bind(exturiBiasedIgnorePathCase);
+export const dirname = exturiBiasedIgnorePathCase.dirname.bind(exturiBiasedIgnorePathCase);
+export const joinPath = extUri.joinPath.bind(extUri);
+export const normalizePath = exturiBiasedIgnorePathCase.normalizePath.bind(exturiBiasedIgnorePathCase);
+export const relativePath = exturiBiasedIgnorePathCase.relativePath.bind(exturiBiasedIgnorePathCase);
+export const resolvePath = exturiBiasedIgnorePathCase.resolvePath.bind(exturiBiasedIgnorePathCase);
+export const isAbsolutePath = exturiBiasedIgnorePathCase.isAbsolutePath.bind(exturiBiasedIgnorePathCase);
+export const isEqualAuthority = exturiBiasedIgnorePathCase.isEqualAuthority.bind(exturiBiasedIgnorePathCase);
+export const hasTrailingPathSeparator = exturiBiasedIgnorePathCase.hasTrailingPathSeparator.bind(exturiBiasedIgnorePathCase);
+export const removeTrailingPathSeparator = exturiBiasedIgnorePathCase.removeTrailingPathSeparator.bind(exturiBiasedIgnorePathCase);
+export const addTrailingPathSeparator = exturiBiasedIgnorePathCase.addTrailingPathSeparator.bind(exturiBiasedIgnorePathCase);
+
+//#endregion
+
export function distinctParents(items: T[], resourceAccessor: (item: T) => URI): T[] {
const distinctParents: T[] = [];
for (let i = 0; i < items.length; i++) {
diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts
index c507366e853..a45e0763ab1 100644
--- a/src/vs/base/common/scrollable.ts
+++ b/src/vs/base/common/scrollable.ts
@@ -13,10 +13,18 @@ export const enum ScrollbarVisibility {
}
export interface ScrollEvent {
+ oldWidth: number;
+ oldScrollWidth: number;
+ oldScrollLeft: number;
+
width: number;
scrollWidth: number;
scrollLeft: number;
+ oldHeight: number;
+ oldScrollHeight: number;
+ oldScrollTop: number;
+
height: number;
scrollHeight: number;
scrollTop: number;
@@ -134,10 +142,18 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
const scrollTopChanged = (this.scrollTop !== previous.scrollTop);
return {
+ oldWidth: previous.width,
+ oldScrollWidth: previous.scrollWidth,
+ oldScrollLeft: previous.scrollLeft,
+
width: this.width,
scrollWidth: this.scrollWidth,
scrollLeft: this.scrollLeft,
+ oldHeight: previous.height,
+ oldScrollHeight: previous.scrollHeight,
+ oldScrollTop: previous.scrollTop,
+
height: this.height,
scrollHeight: this.scrollHeight,
scrollTop: this.scrollTop,
diff --git a/src/vs/base/common/skipList.ts b/src/vs/base/common/skipList.ts
new file mode 100644
index 00000000000..1243ebdddf7
--- /dev/null
+++ b/src/vs/base/common/skipList.ts
@@ -0,0 +1,203 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+
+class Node {
+ readonly forward: Node[];
+ constructor(readonly level: number, readonly key: K, public value: V) {
+ this.forward = [];
+ }
+}
+
+const NIL: undefined = undefined;
+
+interface Comparator {
+ (a: K, b: K): number;
+}
+
+export class SkipList implements Map {
+
+ readonly [Symbol.toStringTag] = 'SkipList';
+
+ private _maxLevel: number;
+ private _level: number = 1;
+ private _header: Node;
+ private _size: number = 0;
+
+ /**
+ *
+ * @param capacity Capacity at which the list performs best
+ */
+ constructor(
+ readonly comparator: (a: K, b: K) => number,
+ capacity: number = 2 ** 16
+ ) {
+ this._maxLevel = Math.max(1, Math.log2(capacity) | 0);
+ this._header = new Node(this._maxLevel, NIL, NIL);
+ }
+
+ get size(): number {
+ return this._size;
+ }
+
+ clear(): void {
+ this._header = new Node(this._maxLevel, NIL, NIL);
+ }
+
+ has(key: K): boolean {
+ return Boolean(SkipList._search(this, key, this.comparator));
+ }
+
+ get(key: K): V | undefined {
+ return SkipList._search(this, key, this.comparator)?.value;
+ }
+
+ set(key: K, value: V): this {
+ if (SkipList._insert(this, key, value, this.comparator)) {
+ this._size += 1;
+ }
+ return this;
+ }
+
+ delete(key: K): boolean {
+ const didDelete = SkipList._delete(this, key, this.comparator);
+ if (didDelete) {
+ this._size -= 1;
+ }
+ return didDelete;
+ }
+
+ // --- iteration
+
+ forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void {
+ let node = this._header.forward[0];
+ while (node) {
+ callbackfn.call(thisArg, node.value, node.key, this);
+ node = node.forward[0];
+ }
+ }
+
+ [Symbol.iterator](): IterableIterator<[K, V]> {
+ return this.entries();
+ }
+
+ *entries(): IterableIterator<[K, V]> {
+ let node = this._header.forward[0];
+ while (node) {
+ yield [node.key, node.value];
+ node = node.forward[0];
+ }
+ }
+
+ *keys(): IterableIterator {
+ let node = this._header.forward[0];
+ while (node) {
+ yield node.key;
+ node = node.forward[0];
+ }
+ }
+
+ *values(): IterableIterator {
+ let node = this._header.forward[0];
+ while (node) {
+ yield node.value;
+ node = node.forward[0];
+ }
+ }
+
+ toString(): string {
+ // debug string...
+ let result = '[SkipList]:';
+ let node = this._header.forward[0];
+ while (node) {
+ result += `node(${node.key}, ${node.value}, lvl:${node.level})`;
+ node = node.forward[0];
+ }
+ return result;
+ }
+
+ // from https://www.epaperpress.com/sortsearch/download/skiplist.pdf
+
+ private static _search(list: SkipList, searchKey: K, comparator: Comparator) {
+ let x = list._header;
+ for (let i = list._level; i >= 0; i--) {
+ while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
+ x = x.forward[i];
+ }
+ }
+ x = x.forward[0];
+ if (x && comparator(x.key, searchKey) === 0) {
+ return x;
+ }
+ return undefined;
+ }
+
+ private static _insert(list: SkipList, searchKey: K, value: V, comparator: Comparator) {
+ let update: Node[] = [];
+ let x = list._header;
+ for (let i = list._level; i >= 0; i--) {
+ while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
+ x = x.forward[i];
+ }
+ update[i] = x;
+ }
+ x = x.forward[0];
+ if (x && comparator(x.key, searchKey) === 0) {
+ // update
+ x.value = value;
+ return false;
+ } else {
+ // insert
+ let lvl = SkipList._randomLevel(list);
+ if (lvl > list._level) {
+ for (let i = list._level + 1; i <= lvl; i++) {
+ update[i] = list._header;
+ }
+ list._level = lvl;
+ }
+ x = new Node(lvl, searchKey, value);
+ for (let i = 0; i <= lvl; i++) {
+ x.forward[i] = update[i].forward[i];
+ update[i].forward[i] = x;
+ }
+ return true;
+ }
+ }
+
+ private static _randomLevel(list: SkipList, p: number = 0.5): number {
+ let lvl = 1;
+ while (Math.random() < p && lvl < list._maxLevel) {
+ lvl += 1;
+ }
+ return lvl;
+ }
+
+ private static _delete(list: SkipList, searchKey: K, comparator: Comparator) {
+ let update: Node[] = [];
+ let x = list._header;
+ for (let i = list._level; i >= 0; i--) {
+ while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) {
+ x = x.forward[i];
+ }
+ update[i] = x;
+ }
+ x = x.forward[0];
+ if (!x || comparator(x.key, searchKey) !== 0) {
+ // not found
+ return false;
+ }
+ for (let i = 0; i < list._level; i++) {
+ if (update[i].forward[i] !== x) {
+ break;
+ }
+ update[i].forward[i] = x.forward[i];
+ }
+ while (list._level >= 1 && list._header.forward[list._level] === NIL) {
+ list._level -= 1;
+ }
+ return true;
+ }
+
+}
diff --git a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts
similarity index 87%
rename from src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts
rename to src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts
index da008e59930..4b5c3fbdf75 100644
--- a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts
+++ b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { ipcRenderer, Event } from 'electron';
+import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { IContextMenuItem, ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions, IContextMenuEvent } from 'vs/base/parts/contextmenu/common/contextmenu';
let contextMenuIdPool = 0;
@@ -13,7 +13,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions): void
const contextMenuId = contextMenuIdPool++;
const onClickChannel = `vscode:onContextMenu${contextMenuId}`;
- const onClickChannelHandler = (_event: Event, itemId: number, context: IContextMenuEvent) => {
+ const onClickChannelHandler = (event: unknown, itemId: number, context: IContextMenuEvent) => {
const item = processedItems[itemId];
if (item.click) {
item.click(context);
@@ -21,7 +21,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions): void
};
ipcRenderer.once(onClickChannel, onClickChannelHandler);
- ipcRenderer.once(CONTEXT_MENU_CLOSE_CHANNEL, (_event: Event, closedContextMenuId: number) => {
+ ipcRenderer.once(CONTEXT_MENU_CLOSE_CHANNEL, (event: unknown, closedContextMenuId: number) => {
if (closedContextMenuId !== contextMenuId) {
return;
}
diff --git a/src/vs/base/parts/ipc/node/ipc.electron.ts b/src/vs/base/parts/ipc/common/ipc.electron.ts
similarity index 83%
rename from src/vs/base/parts/ipc/node/ipc.electron.ts
rename to src/vs/base/parts/ipc/common/ipc.electron.ts
index 09c97ba47b1..516351e1509 100644
--- a/src/vs/base/parts/ipc/node/ipc.electron.ts
+++ b/src/vs/base/parts/ipc/common/ipc.electron.ts
@@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event';
import { VSBuffer } from 'vs/base/common/buffer';
export interface Sender {
- send(channel: string, msg: Buffer | null): void;
+ send(channel: string, msg: unknown): void;
}
export class Protocol implements IMessagePassingProtocol {
@@ -17,13 +17,13 @@ export class Protocol implements IMessagePassingProtocol {
send(message: VSBuffer): void {
try {
- this.sender.send('ipc:message', (message.buffer));
+ this.sender.send('vscode:message', message.buffer);
} catch (e) {
// systems are going down
}
}
dispose(): void {
- this.sender.send('ipc:disconnect', null);
+ this.sender.send('vscode:disconnect', null);
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts
index 8acfc6bce4e..411bfe46cca 100644
--- a/src/vs/base/parts/ipc/common/ipc.ts
+++ b/src/vs/base/parts/ipc/common/ipc.ts
@@ -10,7 +10,9 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import * as errors from 'vs/base/common/errors';
import { VSBuffer } from 'vs/base/common/buffer';
import { getRandomElement } from 'vs/base/common/arrays';
-import { isFunction } from 'vs/base/common/types';
+import { isFunction, isUndefinedOrNull } from 'vs/base/common/types';
+import { revive } from 'vs/base/common/marshalling';
+import { isUpperAsciiLetter } from 'vs/base/common/strings';
/**
* An `IChannel` is an abstraction over a collection of commands.
@@ -919,3 +921,142 @@ export class StaticRouter implements IClientRouter
return await this.route(hub);
}
}
+
+
+//#region createChannelReceiver / createChannelSender
+
+/**
+ * Use both `createChannelReceiver` and `createChannelSender`
+ * for automated process <=> process communication over methods
+ * and events. You do not need to spell out each method on both
+ * sides, a proxy will take care of this.
+ *
+ * Rules:
+ * - if marshalling is enabled, only `URI` and `RegExp` is converted
+ * automatically for you
+ * - events must follow the naming convention `onUppercase`
+ * - `CancellationToken` is currently not supported
+ * - if a context is provided, you can use `AddFirstParameterToFunctions`
+ * utility to signal this in the receiving side type
+ */
+
+export interface IBaseChannelOptions {
+
+ /**
+ * Disables automatic marshalling of `URI`.
+ * If marshalling is disabled, `UriComponents`
+ * must be used instead.
+ */
+ disableMarshalling?: boolean;
+}
+
+export interface IChannelReceiverOptions extends IBaseChannelOptions { }
+
+export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel {
+ const handler = service as { [key: string]: unknown };
+ const disableMarshalling = options && options.disableMarshalling;
+
+ // Buffer any event that should be supported by
+ // iterating over all property keys and finding them
+ const mapEventNameToEvent = new Map>();
+ for (const key in handler) {
+ if (propertyIsEvent(key)) {
+ mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event, true));
+ }
+ }
+
+ return new class implements IServerChannel {
+
+ listen(_: unknown, event: string): Event {
+ const eventImpl = mapEventNameToEvent.get(event);
+ if (eventImpl) {
+ return eventImpl as Event;
+ }
+
+ throw new Error(`Event not found: ${event}`);
+ }
+
+ call(_: unknown, command: string, args?: any[]): Promise {
+ const target = handler[command];
+ if (typeof target === 'function') {
+
+ // Revive unless marshalling disabled
+ if (!disableMarshalling && Array.isArray(args)) {
+ for (let i = 0; i < args.length; i++) {
+ args[i] = revive(args[i]);
+ }
+ }
+
+ return target.apply(handler, args);
+ }
+
+ throw new Error(`Method not found: ${command}`);
+ }
+ };
+}
+
+export interface IChannelSenderOptions extends IBaseChannelOptions {
+
+ /**
+ * If provided, will add the value of `context`
+ * to each method call to the target.
+ */
+ context?: unknown;
+
+ /**
+ * If provided, will not proxy any of the properties
+ * that are part of the Map but rather return that value.
+ */
+ properties?: Map;
+}
+
+export function createChannelSender(channel: IChannel, options?: IChannelSenderOptions): T {
+ const disableMarshalling = options && options.disableMarshalling;
+
+ return new Proxy({}, {
+ get(_target: T, propKey: PropertyKey) {
+ if (typeof propKey === 'string') {
+
+ // Check for predefined values
+ if (options?.properties?.has(propKey)) {
+ return options.properties.get(propKey);
+ }
+
+ // Event
+ if (propertyIsEvent(propKey)) {
+ return channel.listen(propKey);
+ }
+
+ // Function
+ return async function (...args: any[]) {
+
+ // Add context if any
+ let methodArgs: any[];
+ if (options && !isUndefinedOrNull(options.context)) {
+ methodArgs = [options.context, ...args];
+ } else {
+ methodArgs = args;
+ }
+
+ const result = await channel.call(propKey, methodArgs);
+
+ // Revive unless marshalling disabled
+ if (!disableMarshalling) {
+ return revive(result);
+ }
+
+ return result;
+ };
+ }
+
+ throw new Error(`Property not found: ${String(propKey)}`);
+ }
+ }) as T;
+}
+
+function propertyIsEvent(name: string): boolean {
+ // Assume a property is an event if it has a form of "onSomething"
+ return name[0] === 'o' && name[1] === 'n' && isUpperAsciiLetter(name.charCodeAt(2));
+}
+
+//#endregion
diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts
index bac2223ede6..b84a1d653dc 100644
--- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts
+++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts
@@ -5,7 +5,7 @@
import { Event, Emitter } from 'vs/base/common/event';
import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc';
-import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron';
+import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron';
import { ipcMain, WebContents } from 'electron';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
@@ -26,7 +26,7 @@ export class Server extends IPCServer {
private static readonly Clients = new Map();
private static getOnDidClientConnect(): Event {
- const onHello = Event.fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender);
+ const onHello = Event.fromNodeEventEmitter(ipcMain, 'vscode:hello', ({ sender }) => sender);
return Event.map(onHello, webContents => {
const id = webContents.id;
@@ -39,8 +39,8 @@ export class Server extends IPCServer {
const onDidClientReconnect = new Emitter();
Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire()));
- const onMessage = createScopedOnMessageEvent(id, 'ipc:message') as Event;
- const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event);
+ const onMessage = createScopedOnMessageEvent(id, 'vscode:message') as Event;
+ const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'vscode:disconnect')), onDidClientReconnect.event);
const protocol = new Protocol(webContents, onMessage);
return { protocol, onDidClientDisconnect };
diff --git a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts
similarity index 80%
rename from src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts
rename to src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts
index 2690300b83a..19ca487c890 100644
--- a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts
+++ b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts
@@ -5,18 +5,18 @@
import { Event } from 'vs/base/common/event';
import { IPCClient } from 'vs/base/parts/ipc/common/ipc';
-import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron';
-import { ipcRenderer } from 'electron';
+import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron';
import { IDisposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
+import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
export class Client extends IPCClient implements IDisposable {
private protocol: Protocol;
private static createProtocol(): Protocol {
- const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: Buffer) => VSBuffer.wrap(message));
- ipcRenderer.send('ipc:hello');
+ const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'vscode:message', (_, message) => VSBuffer.wrap(message));
+ ipcRenderer.send('vscode:hello');
return new Protocol(ipcRenderer, onMessage);
}
@@ -29,4 +29,4 @@ export class Client extends IPCClient implements IDisposable {
dispose(): void {
this.protocol.dispose();
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts
deleted file mode 100644
index 631e5139133..00000000000
--- a/src/vs/base/parts/ipc/node/ipc.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { Event } from 'vs/base/common/event';
-import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
-import { revive } from 'vs/base/common/marshalling';
-import { isUndefinedOrNull } from 'vs/base/common/types';
-import { isUpperAsciiLetter } from 'vs/base/common/strings';
-
-/**
- * Use both `createChannelReceiver` and `createChannelSender`
- * for automated process <=> process communication over methods
- * and events. You do not need to spell out each method on both
- * sides, a proxy will take care of this.
- *
- * Rules:
- * - if marshalling is enabled, only `URI` and `RegExp` is converted
- * automatically for you
- * - events must follow the naming convention `onUppercase`
- * - `CancellationToken` is currently not supported
- * - if a context is provided, you can use `AddFirstParameterToFunctions`
- * utility to signal this in the receiving side type
- */
-
-export interface IBaseChannelOptions {
-
- /**
- * Disables automatic marshalling of `URI`.
- * If marshalling is disabled, `UriComponents`
- * must be used instead.
- */
- disableMarshalling?: boolean;
-}
-
-export interface IChannelReceiverOptions extends IBaseChannelOptions { }
-
-export function createChannelReceiver(service: unknown, options?: IChannelReceiverOptions): IServerChannel {
- const handler = service as { [key: string]: unknown };
- const disableMarshalling = options && options.disableMarshalling;
-
- // Buffer any event that should be supported by
- // iterating over all property keys and finding them
- const mapEventNameToEvent = new Map>();
- for (const key in handler) {
- if (propertyIsEvent(key)) {
- mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event, true));
- }
- }
-
- return new class implements IServerChannel {
-
- listen(_: unknown, event: string): Event {
- const eventImpl = mapEventNameToEvent.get(event);
- if (eventImpl) {
- return eventImpl as Event;
- }
-
- throw new Error(`Event not found: ${event}`);
- }
-
- call(_: unknown, command: string, args?: any[]): Promise {
- const target = handler[command];
- if (typeof target === 'function') {
-
- // Revive unless marshalling disabled
- if (!disableMarshalling && Array.isArray(args)) {
- for (let i = 0; i < args.length; i++) {
- args[i] = revive(args[i]);
- }
- }
-
- return target.apply(handler, args);
- }
-
- throw new Error(`Method not found: ${command}`);
- }
- };
-}
-
-export interface IChannelSenderOptions extends IBaseChannelOptions {
-
- /**
- * If provided, will add the value of `context`
- * to each method call to the target.
- */
- context?: unknown;
-}
-
-export function createChannelSender(channel: IChannel, options?: IChannelSenderOptions): T {
- const disableMarshalling = options && options.disableMarshalling;
-
- return new Proxy({}, {
- get(_target: T, propKey: PropertyKey) {
- if (typeof propKey === 'string') {
-
- // Event
- if (propertyIsEvent(propKey)) {
- return channel.listen(propKey);
- }
-
- // Function
- return async function (...args: any[]) {
-
- // Add context if any
- let methodArgs: any[];
- if (options && !isUndefinedOrNull(options.context)) {
- methodArgs = [options.context, ...args];
- } else {
- methodArgs = args;
- }
-
- const result = await channel.call(propKey, methodArgs);
-
- // Revive unless marshalling disabled
- if (!disableMarshalling) {
- return revive(result);
- }
-
- return result;
- };
- }
-
- throw new Error(`Property not found: ${String(propKey)}`);
- }
- }) as T;
-}
-
-function propertyIsEvent(name: string): boolean {
- // Assume a property is an event if it has a form of "onSomething"
- return name[0] === 'o' && name[1] === 'n' && isUpperAsciiLetter(name.charCodeAt(2));
-}
diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/common/ipc.test.ts
similarity index 95%
rename from src/vs/base/parts/ipc/test/node/ipc.test.ts
rename to src/vs/base/parts/ipc/test/common/ipc.test.ts
index 3539f5dea8c..8ed0f3797f1 100644
--- a/src/vs/base/parts/ipc/test/node/ipc.test.ts
+++ b/src/vs/base/parts/ipc/test/common/ipc.test.ts
@@ -4,8 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
-import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient } from 'vs/base/parts/ipc/common/ipc';
-import { createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/node/ipc';
+import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { Emitter, Event } from 'vs/base/common/event';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { canceled } from 'vs/base/common/errors';
@@ -103,7 +102,7 @@ interface ITestService {
error(message: string): Promise;
neverComplete(): Promise;
neverCompleteCT(cancellationToken: CancellationToken): Promise;
- buffersLength(buffers: Buffer[]): Promise;
+ buffersLength(buffers: VSBuffer[]): Promise;
marshall(uri: URI): Promise;
context(): Promise;
@@ -135,8 +134,8 @@ class TestService implements ITestService {
return new Promise((_, e) => cancellationToken.onCancellationRequested(() => e(canceled())));
}
- buffersLength(buffers: Buffer[]): Promise {
- return Promise.resolve(buffers.reduce((r, b) => r + b.length, 0));
+ buffersLength(buffers: VSBuffer[]): Promise {
+ return Promise.resolve(buffers.reduce((r, b) => r + b.buffer.length, 0));
}
ping(msg: string): void {
@@ -199,7 +198,7 @@ class TestChannelClient implements ITestService {
return this.channel.call('neverCompleteCT', undefined, cancellationToken);
}
- buffersLength(buffers: Buffer[]): Promise {
+ buffersLength(buffers: VSBuffer[]): Promise {
return this.channel.call('buffersLength', buffers);
}
@@ -317,7 +316,7 @@ suite('Base IPC', function () {
});
test('buffers in arrays', async function () {
- const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]);
+ const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]);
return assert.equal(r, 5);
});
});
@@ -383,7 +382,7 @@ suite('Base IPC', function () {
});
test('buffers in arrays', async function () {
- const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]);
+ const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]);
return assert.equal(r, 5);
});
});
diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts
index f09e508bed7..ebad819fffa 100644
--- a/src/vs/base/parts/quickinput/common/quickInput.ts
+++ b/src/vs/base/parts/quickinput/common/quickInput.ts
@@ -307,7 +307,7 @@ export interface IQuickInputButton {
iconClass?: string;
tooltip?: string;
/**
- * Wether to always show the button. By default buttons
+ * Whether to always show the button. By default buttons
* are only visible when hovering over them with the mouse
*/
alwaysVisible?: boolean;
diff --git a/src/vs/base/parts/sandbox/common/electronTypes.ts b/src/vs/base/parts/sandbox/common/electronTypes.ts
new file mode 100644
index 00000000000..651a9d575ef
--- /dev/null
+++ b/src/vs/base/parts/sandbox/common/electronTypes.ts
@@ -0,0 +1,249 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+
+// #######################################################################
+// ### ###
+// ### electron.d.ts types we need in a common layer for reuse ###
+// ### (copied from Electron 7.x) ###
+// ### ###
+// #######################################################################
+
+
+export interface MessageBoxOptions {
+ /**
+ * Can be `"none"`, `"info"`, `"error"`, `"question"` or `"warning"`. On Windows,
+ * `"question"` displays the same icon as `"info"`, unless you set an icon using
+ * the `"icon"` option. On macOS, both `"warning"` and `"error"` display the same
+ * warning icon.
+ */
+ type?: string;
+ /**
+ * Array of texts for buttons. On Windows, an empty array will result in one button
+ * labeled "OK".
+ */
+ buttons?: string[];
+ /**
+ * Index of the button in the buttons array which will be selected by default when
+ * the message box opens.
+ */
+ defaultId?: number;
+ /**
+ * Title of the message box, some platforms will not show it.
+ */
+ title?: string;
+ /**
+ * Content of the message box.
+ */
+ message: string;
+ /**
+ * Extra information of the message.
+ */
+ detail?: string;
+ /**
+ * If provided, the message box will include a checkbox with the given label.
+ */
+ checkboxLabel?: string;
+ /**
+ * Initial checked state of the checkbox. `false` by default.
+ */
+ checkboxChecked?: boolean;
+ // icon?: NativeImage;
+ /**
+ * The index of the button to be used to cancel the dialog, via the `Esc` key. By
+ * default this is assigned to the first button with "cancel" or "no" as the label.
+ * If no such labeled buttons exist and this option is not set, `0` will be used as
+ * the return value.
+ */
+ cancelId?: number;
+ /**
+ * On Windows Electron will try to figure out which one of the `buttons` are common
+ * buttons (like "Cancel" or "Yes"), and show the others as command links in the
+ * dialog. This can make the dialog appear in the style of modern Windows apps. If
+ * you don't like this behavior, you can set `noLink` to `true`.
+ */
+ noLink?: boolean;
+ /**
+ * Normalize the keyboard access keys across platforms. Default is `false`.
+ * Enabling this assumes `&` is used in the button labels for the placement of the
+ * keyboard shortcut access key and labels will be converted so they work correctly
+ * on each platform, `&` characters are removed on macOS, converted to `_` on
+ * Linux, and left untouched on Windows. For example, a button label of `Vie&w`
+ * will be converted to `Vie_w` on Linux and `View` on macOS and can be selected
+ * via `Alt-W` on Windows and Linux.
+ */
+ normalizeAccessKeys?: boolean;
+}
+
+export interface MessageBoxReturnValue {
+ /**
+ * The index of the clicked button.
+ */
+ response: number;
+ /**
+ * The checked state of the checkbox if `checkboxLabel` was set. Otherwise `false`.
+ */
+ checkboxChecked: boolean;
+}
+
+export interface OpenDevToolsOptions {
+ /**
+ * Opens the devtools with specified dock state, can be `right`, `bottom`,
+ * `undocked`, `detach`. Defaults to last used dock state. In `undocked` mode it's
+ * possible to dock back. In `detach` mode it's not.
+ */
+ mode: ('right' | 'bottom' | 'undocked' | 'detach');
+ /**
+ * Whether to bring the opened devtools window to the foreground. The default is
+ * `true`.
+ */
+ activate?: boolean;
+}
+
+export interface SaveDialogOptions {
+ title?: string;
+ /**
+ * Absolute directory path, absolute file path, or file name to use by default.
+ */
+ defaultPath?: string;
+ /**
+ * Custom label for the confirmation button, when left empty the default label will
+ * be used.
+ */
+ buttonLabel?: string;
+ filters?: FileFilter[];
+ /**
+ * Message to display above text fields.
+ *
+ * @platform darwin
+ */
+ message?: string;
+ /**
+ * Custom label for the text displayed in front of the filename text field.
+ *
+ * @platform darwin
+ */
+ nameFieldLabel?: string;
+ /**
+ * Show the tags input box, defaults to `true`.
+ *
+ * @platform darwin
+ */
+ showsTagField?: boolean;
+ /**
+ * Create a security scoped bookmark when packaged for the Mac App Store. If this
+ * option is enabled and the file doesn't already exist a blank file will be
+ * created at the chosen path.
+ *
+ * @platform darwin,mas
+ */
+ securityScopedBookmarks?: boolean;
+}
+
+export interface OpenDialogOptions {
+ title?: string;
+ defaultPath?: string;
+ /**
+ * Custom label for the confirmation button, when left empty the default label will
+ * be used.
+ */
+ buttonLabel?: string;
+ filters?: FileFilter[];
+ /**
+ * Contains which features the dialog should use. The following values are
+ * supported:
+ */
+ properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'>;
+ /**
+ * Message to display above input boxes.
+ *
+ * @platform darwin
+ */
+ message?: string;
+ /**
+ * Create security scoped bookmarks when packaged for the Mac App Store.
+ *
+ * @platform darwin,mas
+ */
+ securityScopedBookmarks?: boolean;
+}
+
+export interface OpenDialogReturnValue {
+ /**
+ * whether or not the dialog was canceled.
+ */
+ canceled: boolean;
+ /**
+ * An array of file paths chosen by the user. If the dialog is cancelled this will
+ * be an empty array.
+ */
+ filePaths: string[];
+ /**
+ * An array matching the `filePaths` array of base64 encoded strings which contains
+ * security scoped bookmark data. `securityScopedBookmarks` must be enabled for
+ * this to be populated. (For return values, see table here.)
+ *
+ * @platform darwin,mas
+ */
+ bookmarks?: string[];
+}
+
+export interface SaveDialogReturnValue {
+ /**
+ * whether or not the dialog was canceled.
+ */
+ canceled: boolean;
+ /**
+ * If the dialog is canceled, this will be `undefined`.
+ */
+ filePath?: string;
+ /**
+ * Base64 encoded string which contains the security scoped bookmark data for the
+ * saved file. `securityScopedBookmarks` must be enabled for this to be present.
+ * (For return values, see table here.)
+ *
+ * @platform darwin,mas
+ */
+ bookmark?: string;
+}
+
+export interface CrashReporterStartOptions {
+ companyName: string;
+ /**
+ * URL that crash reports will be sent to as POST.
+ */
+ submitURL: string;
+ /**
+ * Defaults to `app.name`.
+ */
+ productName?: string;
+ /**
+ * Whether crash reports should be sent to the server. Default is `true`.
+ */
+ uploadToServer?: boolean;
+ /**
+ * Default is `false`.
+ */
+ ignoreSystemCrashHandler?: boolean;
+ /**
+ * An object you can define that will be sent along with the report. Only string
+ * properties are sent correctly. Nested objects are not supported. When using
+ * Windows, the property names and values must be fewer than 64 characters.
+ */
+ extra?: Record;
+ /**
+ * Directory to store the crash reports temporarily (only used when the crash
+ * reporter is started via `process.crashReporter.start`).
+ */
+ crashesDirectory?: string;
+}
+
+export interface FileFilter {
+
+ // Docs: http://electronjs.org/docs/api/structures/file-filter
+
+ extensions: string[];
+ name: string;
+}
diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js
new file mode 100644
index 00000000000..1cb22cfcdd8
--- /dev/null
+++ b/src/vs/base/parts/sandbox/electron-browser/preload.js
@@ -0,0 +1,115 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+// @ts-check
+(function () {
+ 'use strict';
+
+ const { ipcRenderer, webFrame, crashReporter } = require('electron');
+
+ // @ts-ignore
+ window.vscode = {
+
+ /**
+ * A minimal set of methods exposed from ipcRenderer
+ * to support communication to electron-main
+ *
+ * @type {typeof import('../electron-sandbox/globals').ipcRenderer}
+ */
+ ipcRenderer: {
+
+ /**
+ * @param {string} channel
+ * @param {any[]} args
+ */
+ send(channel, ...args) {
+ validateIPC(channel);
+
+ ipcRenderer.send(channel, ...args);
+ },
+
+ /**
+ * @param {string} channel
+ * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
+ */
+ on(channel, listener) {
+ validateIPC(channel);
+
+ ipcRenderer.on(channel, listener);
+ },
+
+ /**
+ * @param {string} channel
+ * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
+ */
+ once(channel, listener) {
+ validateIPC(channel);
+
+ ipcRenderer.once(channel, listener);
+ },
+
+ /**
+ * @param {string} channel
+ * @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
+ */
+ removeListener(channel, listener) {
+ validateIPC(channel);
+
+ ipcRenderer.removeListener(channel, listener);
+ }
+ },
+
+ /**
+ * Support for methods of webFrame type.
+ *
+ * @type {typeof import('../electron-sandbox/globals').webFrame}
+ */
+ webFrame: {
+
+ getZoomFactor() {
+ return webFrame.getZoomFactor();
+ },
+
+ getZoomLevel() {
+ return webFrame.getZoomLevel();
+ },
+
+ /**
+ * @param {number} level
+ */
+ setZoomLevel(level) {
+ webFrame.setZoomLevel(level);
+ }
+ },
+
+ /**
+ * Support for methods of crashReporter type.
+ *
+ * @type {typeof import('../electron-sandbox/globals').crashReporter}
+ */
+ crashReporter: {
+
+ /**
+ * @param {Electron.CrashReporterStartOptions} options
+ */
+ start(options) {
+ crashReporter.start(options);
+ }
+ }
+ };
+
+ //#region Utilities
+
+ /**
+ * @param {string} channel
+ */
+ function validateIPC(channel) {
+ if (!channel || !channel.startsWith('vscode:')) {
+ throw new Error(`Unsupported event IPC channel '${channel}'`);
+ }
+ }
+
+ //#endregion
+}());
diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts
new file mode 100644
index 00000000000..0cd04e036d4
--- /dev/null
+++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts
@@ -0,0 +1,93 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/common/electronTypes';
+
+export const ipcRenderer = (window as any).vscode.ipcRenderer as {
+
+ /**
+ * Listens to `channel`, when a new message arrives `listener` would be called with
+ * `listener(event, args...)`.
+ */
+ on(channel: string, listener: (event: unknown, ...args: any[]) => void): void;
+
+ /**
+ * Adds a one time `listener` function for the event. This `listener` is invoked
+ * only the next time a message is sent to `channel`, after which it is removed.
+ */
+ once(channel: string, listener: (event: unknown, ...args: any[]) => void): void;
+
+ /**
+ * Removes the specified `listener` from the listener array for the specified
+ * `channel`.
+ */
+ removeListener(channel: string, listener: (event: unknown, ...args: any[]) => void): void;
+
+ /**
+ * Send an asynchronous message to the main process via `channel`, along with
+ * arguments. Arguments will be serialized with the Structured Clone Algorithm,
+ * just like `postMessage`, so prototype chains will not be included. Sending
+ * Functions, Promises, Symbols, WeakMaps, or WeakSets will throw an exception.
+ *
+ * > **NOTE**: Sending non-standard JavaScript types such as DOM objects or special
+ * Electron objects is deprecated, and will begin throwing an exception starting
+ * with Electron 9.
+ *
+ * The main process handles it by listening for `channel` with the `ipcMain`
+ * module.
+ */
+ send(channel: string, ...args: any[]): void;
+};
+
+export const webFrame = (window as any).vscode.webFrame as {
+
+ /**
+ * The current zoom factor.
+ */
+ getZoomFactor(): number;
+
+ /**
+ * The current zoom level.
+ */
+ getZoomLevel(): number;
+
+ /**
+ * Changes the zoom level to the specified level. The original size is 0 and each
+ * increment above or below represents zooming 20% larger or smaller to default
+ * limits of 300% and 50% of original size, respectively.
+ */
+ setZoomLevel(level: number): void;
+};
+
+export const crashReporter = (window as any).vscode.crashReporter as {
+
+ /**
+ * You are required to call this method before using any other `crashReporter` APIs
+ * and in each process (main/renderer) from which you want to collect crash
+ * reports. You can pass different options to `crashReporter.start` when calling
+ * from different processes.
+ *
+ * **Note** Child processes created via the `child_process` module will not have
+ * access to the Electron modules. Therefore, to collect crash reports from them,
+ * use `process.crashReporter.start` instead. Pass the same options as above along
+ * with an additional one called `crashesDirectory` that should point to a
+ * directory to store the crash reports temporarily. You can test this out by
+ * calling `process.crash()` to crash the child process.
+ *
+ * **Note:** If you need send additional/updated `extra` parameters after your
+ * first call `start` you can call `addExtraParameter` on macOS or call `start`
+ * again with the new/updated `extra` parameters on Linux and Windows.
+ *
+ * **Note:** On macOS and windows, Electron uses a new `crashpad` client for crash
+ * collection and reporting. If you want to enable crash reporting, initializing
+ * `crashpad` from the main process using `crashReporter.start` is required
+ * regardless of which process you want to collect crashes from. Once initialized
+ * this way, the crashpad handler collects crashes from all processes. You still
+ * have to call `crashReporter.start` from the renderer or child process, otherwise
+ * crashes from them will get reported without `companyName`, `productName` or any
+ * of the `extra` information.
+ */
+ start(options: CrashReporterStartOptions): void;
+};
diff --git a/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts b/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts
new file mode 100644
index 00000000000..ac3e6511710
--- /dev/null
+++ b/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts
@@ -0,0 +1,15 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as assert from 'assert';
+import { ipcRenderer, crashReporter, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals';
+
+suite('Sandbox', () => {
+ test('globals', () => {
+ assert.ok(ipcRenderer);
+ assert.ok(crashReporter);
+ assert.ok(webFrame);
+ });
+});
diff --git a/src/vs/base/test/common/extpath.test.ts b/src/vs/base/test/common/extpath.test.ts
index 02aa3a96377..0760e2c8b91 100644
--- a/src/vs/base/test/common/extpath.test.ts
+++ b/src/vs/base/test/common/extpath.test.ts
@@ -130,4 +130,41 @@ suite('Paths', () => {
assert.equal(extpath.indexOfPath('/some/long/path', '/some/long', false), 0);
assert.equal(extpath.indexOfPath('/some/long/path', '/PATH', true), 10);
});
+
+ test('parseLineAndColumnAware', () => {
+ let res = extpath.parseLineAndColumnAware('/foo/bar');
+ assert.equal(res.path, '/foo/bar');
+ assert.equal(res.line, undefined);
+ assert.equal(res.column, undefined);
+
+ res = extpath.parseLineAndColumnAware('/foo/bar:33');
+ assert.equal(res.path, '/foo/bar');
+ assert.equal(res.line, 33);
+ assert.equal(res.column, 1);
+
+ res = extpath.parseLineAndColumnAware('/foo/bar:33:34');
+ assert.equal(res.path, '/foo/bar');
+ assert.equal(res.line, 33);
+ assert.equal(res.column, 34);
+
+ res = extpath.parseLineAndColumnAware('C:\\foo\\bar');
+ assert.equal(res.path, 'C:\\foo\\bar');
+ assert.equal(res.line, undefined);
+ assert.equal(res.column, undefined);
+
+ res = extpath.parseLineAndColumnAware('C:\\foo\\bar:33');
+ assert.equal(res.path, 'C:\\foo\\bar');
+ assert.equal(res.line, 33);
+ assert.equal(res.column, 1);
+
+ res = extpath.parseLineAndColumnAware('C:\\foo\\bar:33:34');
+ assert.equal(res.path, 'C:\\foo\\bar');
+ assert.equal(res.line, 33);
+ assert.equal(res.column, 34);
+
+ res = extpath.parseLineAndColumnAware('/foo/bar:abb');
+ assert.equal(res.path, '/foo/bar:abb');
+ assert.equal(res.line, undefined);
+ assert.equal(res.column, undefined);
+ });
});
diff --git a/src/vs/workbench/test/browser/api/mock.ts b/src/vs/base/test/common/mock.ts
similarity index 100%
rename from src/vs/workbench/test/browser/api/mock.ts
rename to src/vs/base/test/common/mock.ts
diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts
index 0d26e8f04db..1e0d4496e26 100644
--- a/src/vs/base/test/common/resources.test.ts
+++ b/src/vs/base/test/common/resources.test.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
-import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath, addTrailingPathSeparator, getComparisonKey, compare } from 'vs/base/common/resources';
+import { dirname, basename, distinctParents, joinPath, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath, addTrailingPathSeparator, extUri, extUriIgnorePathCase } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { isWindows } from 'vs/base/common/platform';
import { toSlashes } from 'vs/base/common/extpath';
@@ -235,16 +235,19 @@ suite('Resources', () => {
}
});
- function assertEqualURI(actual: URI, expected: URI, message?: string) {
- if (!isEqual(expected, actual, undefined, false)) {
+ function assertEqualURI(actual: URI, expected: URI, message?: string, ignoreCase?: boolean) {
+ let util = ignoreCase ? extUriIgnorePathCase : extUri;
+ if (!util.isEqual(expected, actual)) {
assert.equal(actual.toString(), expected.toString(), message);
}
}
function assertRelativePath(u1: URI, u2: URI, expectedPath: string | undefined, ignoreJoin?: boolean, ignoreCase?: boolean) {
- assert.equal(relativePath(u1, u2, ignoreCase), expectedPath, `from ${u1.toString()} to ${u2.toString()}`);
+ let util = ignoreCase ? extUriIgnorePathCase : extUri;
+
+ assert.equal(util.relativePath(u1, u2), expectedPath, `from ${u1.toString()} to ${u2.toString()}`);
if (expectedPath !== undefined && !ignoreJoin) {
- assertEqualURI(removeTrailingPathSeparator(joinPath(u1, expectedPath)), removeTrailingPathSeparator(u2), 'joinPath on relativePath should be equal');
+ assertEqualURI(removeTrailingPathSeparator(joinPath(u1, expectedPath)), removeTrailingPathSeparator(u2), 'joinPath on relativePath should be equal', ignoreCase);
}
}
@@ -347,10 +350,16 @@ suite('Resources', () => {
});
function assertIsEqual(u1: URI, u2: URI, ignoreCase: boolean | undefined, expected: boolean) {
- assert.equal(isEqual(u1, u2, ignoreCase), expected, `${u1.toString()}${expected ? '===' : '!=='}${u2.toString()}`);
- assert.equal(compare(u1, u2, ignoreCase) === 0, expected);
- assert.equal(getComparisonKey(u1, ignoreCase) === getComparisonKey(u2, ignoreCase), expected, `comparison keys ${u1.toString()}, ${u2.toString()}`);
- assert.equal(isEqualOrParent(u1, u2, ignoreCase), expected, `isEqualOrParent ${u1.toString()}, ${u2.toString()}`);
+
+ let util = ignoreCase ? extUriIgnorePathCase : extUri;
+
+ assert.equal(util.isEqual(u1, u2), expected, `${u1.toString()}${expected ? '===' : '!=='}${u2.toString()}`);
+ assert.equal(util.compare(u1, u2) === 0, expected);
+ assert.equal(util.getComparisonKey(u1) === util.getComparisonKey(u2), expected, `comparison keys ${u1.toString()}, ${u2.toString()}`);
+ assert.equal(util.isEqualOrParent(u1, u2), expected, `isEqualOrParent ${u1.toString()}, ${u2.toString()}`);
+ if (!ignoreCase) {
+ assert.equal(u1.toString() === u2.toString(), expected);
+ }
}
@@ -390,34 +399,35 @@ suite('Resources', () => {
});
test('isEqualOrParent', () => {
+
let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar');
let fileURI2 = isWindows ? URI.file('c:\\foo') : URI.file('/foo');
let fileURI2b = isWindows ? URI.file('C:\\Foo\\') : URI.file('/Foo/');
- assert.equal(isEqualOrParent(fileURI, fileURI, true), true, '1');
- assert.equal(isEqualOrParent(fileURI, fileURI, false), true, '2');
- assert.equal(isEqualOrParent(fileURI, fileURI2, true), true, '3');
- assert.equal(isEqualOrParent(fileURI, fileURI2, false), true, '4');
- assert.equal(isEqualOrParent(fileURI, fileURI2b, true), true, '5');
- assert.equal(isEqualOrParent(fileURI, fileURI2b, false), false, '6');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI), true, '1');
+ assert.equal(extUri.isEqualOrParent(fileURI, fileURI), true, '2');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI2), true, '3');
+ assert.equal(extUri.isEqualOrParent(fileURI, fileURI2), true, '4');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI2b), true, '5');
+ assert.equal(extUri.isEqualOrParent(fileURI, fileURI2b), false, '6');
- assert.equal(isEqualOrParent(fileURI2, fileURI, false), false, '7');
- assert.equal(isEqualOrParent(fileURI2b, fileURI2, true), true, '8');
+ assert.equal(extUri.isEqualOrParent(fileURI2, fileURI), false, '7');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI2b, fileURI2), true, '8');
let fileURI3 = URI.parse('foo://server:453/foo/bar/goo');
let fileURI4 = URI.parse('foo://server:453/foo/');
let fileURI5 = URI.parse('foo://server:453/foo');
- assert.equal(isEqualOrParent(fileURI3, fileURI3, true), true, '11');
- assert.equal(isEqualOrParent(fileURI3, fileURI3, false), true, '12');
- assert.equal(isEqualOrParent(fileURI3, fileURI4, true), true, '13');
- assert.equal(isEqualOrParent(fileURI3, fileURI4, false), true, '14');
- assert.equal(isEqualOrParent(fileURI3, fileURI, true), false, '15');
- assert.equal(isEqualOrParent(fileURI5, fileURI5, true), true, '16');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI3, true), true, '11');
+ assert.equal(extUri.isEqualOrParent(fileURI3, fileURI3), true, '12');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI4, true), true, '13');
+ assert.equal(extUri.isEqualOrParent(fileURI3, fileURI4), true, '14');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI, true), false, '15');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI5, fileURI5, true), true, '16');
let fileURI6 = URI.parse('foo://server:453/foo?q=1');
let fileURI7 = URI.parse('foo://server:453/foo/bar?q=1');
- assert.equal(isEqualOrParent(fileURI6, fileURI5, true), false, '17');
- assert.equal(isEqualOrParent(fileURI6, fileURI6, true), true, '18');
- assert.equal(isEqualOrParent(fileURI7, fileURI6, true), true, '19');
- assert.equal(isEqualOrParent(fileURI7, fileURI5, true), false, '20');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI5), false, '17');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI6), true, '18');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI6), true, '19');
+ assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI5), false, '20');
});
});
diff --git a/src/vs/base/test/common/skipList.test.ts b/src/vs/base/test/common/skipList.test.ts
new file mode 100644
index 00000000000..f6a083e3cb2
--- /dev/null
+++ b/src/vs/base/test/common/skipList.test.ts
@@ -0,0 +1,218 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as assert from 'assert';
+import { SkipList } from 'vs/base/common/skipList';
+import { StopWatch } from 'vs/base/common/stopwatch';
+import { binarySearch } from 'vs/base/common/arrays';
+
+
+suite('SkipList', function () {
+
+ function assertValues(list: SkipList, expected: V[]) {
+ assert.equal(list.size, expected.length);
+ assert.deepEqual([...list.values()], expected);
+
+ let valuesFromEntries = [...list.entries()].map(entry => entry[1]);
+ assert.deepEqual(valuesFromEntries, expected);
+
+ let valuesFromIter = [...list].map(entry => entry[1]);
+ assert.deepEqual(valuesFromIter, expected);
+
+ let i = 0;
+ list.forEach((value, _key, map) => {
+ assert.ok(map === list);
+ assert.deepEqual(value, expected[i++]);
+ });
+ }
+
+ function assertKeys(list: SkipList, expected: K[]) {
+ assert.equal(list.size, expected.length);
+ assert.deepEqual([...list.keys()], expected);
+
+ let keysFromEntries = [...list.entries()].map(entry => entry[0]);
+ assert.deepEqual(keysFromEntries, expected);
+
+ let keysFromIter = [...list].map(entry => entry[0]);
+ assert.deepEqual(keysFromIter, expected);
+
+ let i = 0;
+ list.forEach((_value, key, map) => {
+ assert.ok(map === list);
+ assert.deepEqual(key, expected[i++]);
+ });
+ }
+
+ test('set/get/delete', function () {
+ let list = new SkipList((a, b) => a - b);
+
+ assert.equal(list.get(3), undefined);
+ list.set(3, 1);
+ assert.equal(list.get(3), 1);
+ assertValues(list, [1]);
+
+ list.set(3, 3);
+ assertValues(list, [3]);
+
+ list.set(1, 1);
+ list.set(4, 4);
+ assert.equal(list.get(3), 3);
+ assert.equal(list.get(1), 1);
+ assert.equal(list.get(4), 4);
+ assertValues(list, [1, 3, 4]);
+
+ assert.equal(list.delete(17), false);
+
+ assert.equal(list.delete(1), true);
+ assert.equal(list.get(1), undefined);
+ assert.equal(list.get(3), 3);
+ assert.equal(list.get(4), 4);
+
+ assertValues(list, [3, 4]);
+ });
+
+ test('Figure 3', function () {
+ let list = new SkipList((a, b) => a - b);
+ list.set(3, true);
+ list.set(6, true);
+ list.set(7, true);
+ list.set(9, true);
+ list.set(12, true);
+ list.set(19, true);
+ list.set(21, true);
+ list.set(25, true);
+
+ assertKeys(list, [3, 6, 7, 9, 12, 19, 21, 25]);
+
+ list.set(17, true);
+ assert.deepEqual(list.size, 9);
+ assertKeys(list, [3, 6, 7, 9, 12, 17, 19, 21, 25]);
+ });
+
+ test('capacity max', function () {
+ let list = new SkipList((a, b) => a - b, 10);
+ list.set(1, true);
+ list.set(2, true);
+ list.set(3, true);
+ list.set(4, true);
+ list.set(5, true);
+ list.set(6, true);
+ list.set(7, true);
+ list.set(8, true);
+ list.set(9, true);
+ list.set(10, true);
+ list.set(11, true);
+ list.set(12, true);
+
+ assertKeys(list, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
+ });
+
+ const cmp = (a: number, b: number): number => {
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+ };
+
+ function insertArraySorted(array: number[], element: number) {
+ let idx = binarySearch(array, element, cmp);
+ if (idx >= 0) {
+ array[idx] = element;
+ } else {
+ idx = ~idx;
+ // array = array.slice(0, idx).concat(element, array.slice(idx));
+ array.splice(idx, 0, element);
+ }
+ return array;
+ }
+
+ function delArraySorted(array: number[], element: number) {
+ let idx = binarySearch(array, element, cmp);
+ if (idx >= 0) {
+ // array = array.slice(0, idx).concat(array.slice(idx));
+ array.splice(idx, 1);
+ }
+ return array;
+ }
+
+
+ test('perf', function () {
+ this.skip();
+
+ // data
+ const max = 2 ** 16;
+ const values = new Set();
+ for (let i = 0; i < max; i++) {
+ let value = Math.floor(Math.random() * max);
+ values.add(value);
+ }
+ console.log(values.size);
+
+ // init
+ let list = new SkipList(cmp, max);
+ let sw = new StopWatch(true);
+ values.forEach(value => list.set(value, true));
+ sw.stop();
+ console.log(`[LIST] ${list.size} elements after ${sw.elapsed()}ms`);
+ let array: number[] = [];
+ sw = new StopWatch(true);
+ values.forEach(value => array = insertArraySorted(array, value));
+ sw.stop();
+ console.log(`[ARRAY] ${array.length} elements after ${sw.elapsed()}ms`);
+
+ // get
+ sw = new StopWatch(true);
+ let someValues = [...values].slice(0, values.size / 4);
+ someValues.forEach(key => {
+ let value = list.get(key); // find
+ console.assert(value, '[LIST] must have ' + key);
+ list.get(-key); // miss
+ });
+ sw.stop();
+ console.log(`[LIST] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
+ sw = new StopWatch(true);
+ someValues.forEach(key => {
+ let idx = binarySearch(array, key, cmp); // find
+ console.assert(idx >= 0, '[ARRAY] must have ' + key);
+ binarySearch(array, -key, cmp); // miss
+ });
+ sw.stop();
+ console.log(`[ARRAY] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
+
+
+ // insert
+ sw = new StopWatch(true);
+ someValues.forEach(key => {
+ list.set(-key, false);
+ });
+ sw.stop();
+ console.log(`[LIST] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
+ sw = new StopWatch(true);
+ someValues.forEach(key => {
+ array = insertArraySorted(array, -key);
+ });
+ sw.stop();
+ console.log(`[ARRAY] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
+
+ // delete
+ sw = new StopWatch(true);
+ someValues.forEach(key => {
+ list.delete(key); // find
+ list.delete(-key); // miss
+ });
+ sw.stop();
+ console.log(`[LIST] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
+ sw = new StopWatch(true);
+ someValues.forEach(key => {
+ array = delArraySorted(array, key); // find
+ array = delArraySorted(array, -key); // miss
+ });
+ sw.stop();
+ console.log(`[ARRAY] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
+ });
+});
diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html
index d7547c626a0..74ee36bca78 100644
--- a/src/vs/code/browser/workbench/workbench-dev.html
+++ b/src/vs/code/browser/workbench/workbench-dev.html
@@ -32,7 +32,6 @@
'xterm': `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-unicode11': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
- 'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'xterm-addon-webgl': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`,
}
diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html
index 07445237528..1825bcffec6 100644
--- a/src/vs/code/browser/workbench/workbench.html
+++ b/src/vs/code/browser/workbench/workbench.html
@@ -36,7 +36,6 @@
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-unicode11': `${window.location.origin}/static/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
- 'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'xterm-addon-webgl': `${window.location.origin}/static/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
}
diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts
index 24c2b5372ac..06797ea956c 100644
--- a/src/vs/code/electron-browser/issue/issueReporterMain.ts
+++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts
@@ -3,10 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { clipboard, ipcRenderer, shell, webFrame } from 'electron';
+import 'vs/css!./media/issueReporter';
+import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
+import { ipcRenderer, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import * as os from 'os';
import * as browser from 'vs/base/browser/browser';
-import { $ } from 'vs/base/browser/dom';
+import { $, windowOpenNoOpener } from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
@@ -15,21 +17,19 @@ import { debounce } from 'vs/base/common/decorators';
import { Disposable } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import { escape } from 'vs/base/common/strings';
-import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc';
-import { createChannelSender } from 'vs/base/parts/ipc/node/ipc';
+import { getDelayedChannel, createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net';
import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil';
import { IssueReporterData as IssueReporterModelData, IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel';
import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage';
-import 'vs/css!./media/issueReporter';
import { localize } from 'vs/nls';
import { isRemoteDiagnosticError, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics';
import { EnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
-import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
+import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
-import { ISettingsSearchIssueReporterData, IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/node/issue';
+import { ISettingsSearchIssueReporterData, IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/common/issue';
import { getLogLevel, ILogService } from 'vs/platform/log/common/log';
import { FollowerLogService, LoggerChannelClient } from 'vs/platform/log/common/logIpc';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
@@ -64,6 +64,7 @@ export function startup(configuration: IssueReporterConfiguration) {
export class IssueReporter extends Disposable {
private environmentService!: INativeEnvironmentService;
+ private electronService!: IElectronService;
private telemetryService!: ITelemetryService;
private logService!: ILogService;
private readonly issueReporterModel: IssueReporterModel;
@@ -324,6 +325,9 @@ export class IssueReporter extends Disposable {
const mainProcessService = new MainProcessService(configuration.windowId);
serviceCollection.set(IMainProcessService, mainProcessService);
+ this.electronService = new ElectronService(configuration.windowId, mainProcessService) as IElectronService;
+ serviceCollection.set(IElectronService, this.electronService);
+
this.environmentService = new EnvironmentService(configuration, configuration.execPath);
const logService = new SpdLogService(`issuereporter${configuration.windowId}`, this.environmentService.logsPath, getLogLevel(this.environmentService));
@@ -462,7 +466,7 @@ export class IssueReporter extends Disposable {
this.addEventListener('extensionBugsLink', 'click', (e: Event) => {
const url = (e.target).innerText;
- shell.openExternal(url);
+ windowOpenNoOpener(url);
});
this.addEventListener('disableExtensions', 'keydown', (e: Event) => {
@@ -941,9 +945,9 @@ export class IssueReporter extends Disposable {
private async writeToClipboard(baseUrl: string, issueBody: string): Promise {
return new Promise((resolve, reject) => {
- ipcRenderer.once('vscode:issueReporterClipboardResponse', (_: unknown, shouldWrite: boolean) => {
+ ipcRenderer.once('vscode:issueReporterClipboardResponse', async (event: unknown, shouldWrite: boolean) => {
if (shouldWrite) {
- clipboard.writeText(issueBody);
+ await this.electronService.writeClipboardText(issueBody);
resolve(baseUrl + `&body=${encodeURIComponent(localize('pasteData', "We have written the needed data into your clipboard because it was too large to send. Please paste."))}`);
} else {
reject();
@@ -1194,7 +1198,7 @@ export class IssueReporter extends Disposable {
event.stopPropagation();
// Exclude right click
if (event.which < 3) {
- shell.openExternal((event.target).href);
+ windowOpenNoOpener((event.target).href);
this.telemetryService.publicLog2('issueReporterViewSimilarIssue');
}
}
diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts
index 96bae9110ee..47059817368 100644
--- a/src/vs/code/electron-browser/issue/issueReporterModel.ts
+++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { assign } from 'vs/base/common/objects';
-import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/node/issue';
+import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue';
import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
export interface IssueReporterData {
diff --git a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts b/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts
index 2dda07e6fed..e7ce5b7e463 100644
--- a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts
+++ b/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts
@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel';
import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil';
-import { IssueType } from 'vs/platform/issue/node/issue';
+import { IssueType } from 'vs/platform/issue/common/issue';
suite('IssueReporter', () => {
diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts
index 9684cbd4c20..250f09adab6 100644
--- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts
+++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts
@@ -4,16 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/processExplorer';
-import { webFrame, ipcRenderer, clipboard } from 'electron';
+import { clipboard } from 'electron';
+import { webFrame, ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { repeat } from 'vs/base/common/strings';
import { totalmem } from 'os';
import product from 'vs/platform/product/common/product';
import { localize } from 'vs/nls';
-import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/node/issue';
+import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/common/issue';
import * as browser from 'vs/base/browser/browser';
import * as platform from 'vs/base/common/platform';
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
-import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu';
+import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu';
import { ProcessItem } from 'vs/base/common/processes';
import { addDisposableListener } from 'vs/base/browser/dom';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -369,7 +370,7 @@ function requestProcessList(totalWaitTime: number): void {
// Wait at least a second between requests.
if (waited > 1000) {
- ipcRenderer.send('windowsInfoRequest');
+ ipcRenderer.send('vscode:windowsInfoRequest');
ipcRenderer.send('vscode:listProcesses');
} else {
requestProcessList(waited);
@@ -393,18 +394,18 @@ export function startup(data: ProcessExplorerData): void {
createCloseListener();
// Map window process pids to titles, annotate process names with this when rendering to distinguish between them
- ipcRenderer.on('vscode:windowsInfoResponse', (_event: unknown, windows: any[]) => {
+ ipcRenderer.on('vscode:windowsInfoResponse', (event: unknown, windows: any[]) => {
mapPidToWindowTitle = new Map();
windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title));
});
- ipcRenderer.on('vscode:listProcessesResponse', (_event: Event, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => {
+ ipcRenderer.on('vscode:listProcessesResponse', (event: unknown, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => {
updateProcessInfo(processRoots);
requestProcessList(0);
});
lastRequestTime = Date.now();
- ipcRenderer.send('windowsInfoRequest');
+ ipcRenderer.send('vscode:windowsInfoRequest');
ipcRenderer.send('vscode:listProcesses');
document.onkeydown = (e: KeyboardEvent) => {
diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
index 927e10e7577..850ae498d0c 100644
--- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
+++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
@@ -27,7 +27,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
-import { ipcRenderer } from 'electron';
+import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { ILogService, LogLevel, ILoggerService } from 'vs/platform/log/common/log';
import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
@@ -35,26 +35,25 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza
import { combinedDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { DownloadService } from 'vs/platform/download/common/downloadService';
import { IDownloadService } from 'vs/platform/download/common/download';
-import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
-import { createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/node/ipc';
+import { IChannel, IServerChannel, StaticRouter, createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/common/ipc';
import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner';
import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner';
import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner';
import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner';
-import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
+import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { DiagnosticsService, IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { DiagnosticsChannel } from 'vs/platform/diagnostics/node/diagnosticsIpc';
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
-import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
+import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
-import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, StorageKeysSyncRegistryChannelClient } from 'vs/platform/userDataSync/common/userDataSyncIpc';
-import { IElectronService } from 'vs/platform/electron/node/electron';
+import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, StorageKeysSyncRegistryChannelClient, UserDataSyncMachinesServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
+import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
@@ -66,10 +65,11 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService';
import { IAuthenticationTokenService, AuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
-import { AuthenticationTokenServiceChannel } from 'vs/platform/authentication/common/authenticationIpc';
+import { AuthenticationTokenServiceChannel } from 'vs/platform/authentication/electron-browser/authenticationIpc';
import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { ExtensionTipsService } from 'vs/platform/extensionManagement/node/extensionTipsService';
+import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
export interface ISharedProcessConfiguration {
readonly machineId: string;
@@ -89,7 +89,11 @@ interface ISharedProcessInitData {
const eventPrefix = 'monacoworkbench';
class MainProcessService implements IMainProcessService {
- constructor(private server: Server, private mainRouter: StaticRouter) { }
+
+ constructor(
+ private server: Server,
+ private mainRouter: StaticRouter
+ ) { }
_serviceBrand: undefined;
@@ -109,7 +113,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const onExit = () => disposables.dispose();
process.once('exit', onExit);
- ipcRenderer.once('electron-main->shared-process: exit', onExit);
+ ipcRenderer.once('vscode:electron-main->shared-process=exit', onExit);
disposables.add(server);
@@ -200,6 +204,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService));
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
+ services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService));
services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService));
services.set(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
@@ -225,12 +230,16 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const extensionTipsChannel = new ExtensionTipsChannel(extensionTipsService);
server.registerChannel('extensionTipsService', extensionTipsChannel);
+ const userDataSyncMachinesService = accessor.get(IUserDataSyncMachinesService);
+ const userDataSyncMachineChannel = new UserDataSyncMachinesServiceChannel(userDataSyncMachinesService);
+ server.registerChannel('userDataSyncMachines', userDataSyncMachineChannel);
+
const authTokenService = accessor.get(IAuthenticationTokenService);
const authTokenChannel = new AuthenticationTokenServiceChannel(authTokenService);
server.registerChannel('authToken', authTokenChannel);
const userDataSyncService = accessor.get(IUserDataSyncService);
- const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService);
+ const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService, logService);
server.registerChannel('userDataSync', userDataSyncChannel);
const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSyncService);
@@ -292,17 +301,17 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise(c => {
- ipcRenderer.once('electron-main->shared-process: payload', (_: any, r: ISharedProcessInitData) => c(r));
+ ipcRenderer.once('vscode:electron-main->shared-process=payload', (event: unknown, r: ISharedProcessInitData) => c(r));
// tell electron-main we are ready to receive payload
- ipcRenderer.send('shared-process->electron-main: ready-for-payload');
+ ipcRenderer.send('vscode:shared-process->electron-main=ready-for-payload');
});
// await IPC connection and signal this back to electron-main
const server = await setupIPC(data.sharedIPCHandle);
- ipcRenderer.send('shared-process->electron-main: ipc-ready');
+ ipcRenderer.send('vscode:shared-process->electron-main=ipc-ready');
// await initialization and signal this back to electron-main
await main(server, data, configuration);
- ipcRenderer.send('shared-process->electron-main: init-done');
+ ipcRenderer.send('vscode:shared-process->electron-main=init-done');
}
diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts
index fd287c25045..de09ab8f939 100644
--- a/src/vs/code/electron-main/app.ts
+++ b/src/vs/code/electron-main/app.ts
@@ -8,7 +8,6 @@ import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/plat
import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { OpenContext } from 'vs/platform/windows/node/window';
-import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker';
import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { getShellEnvironment } from 'vs/code/node/shellEnv';
import { IUpdateService } from 'vs/platform/update/common/update';
@@ -32,12 +31,12 @@ import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
-import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
-import { createChannelReceiver } from 'vs/base/parts/ipc/node/ipc';
+import { getDelayedChannel, StaticRouter, createChannelReceiver } from 'vs/base/parts/ipc/common/ipc';
import product from 'vs/platform/product/common/product';
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
import { Disposable } from 'vs/base/common/lifecycle';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
+import { ActiveWindowManager } from 'vs/platform/windows/electron-main/windowTracker';
import { URI } from 'vs/base/common/uri';
import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { WorkspacesService } from 'vs/platform/workspaces/electron-main/workspacesService';
@@ -45,14 +44,12 @@ import { getMachineId } from 'vs/base/node/id';
import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32';
import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux';
import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin';
-import { IIssueService } from 'vs/platform/issue/node/issue';
-import { IssueMainService } from 'vs/platform/issue/electron-main/issueMainService';
+import { IssueMainService, IIssueMainService } from 'vs/platform/issue/electron-main/issueMainService';
import { LoggerChannel } from 'vs/platform/log/common/logIpc';
import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors';
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
-import { IMenubarService } from 'vs/platform/menubar/node/menubar';
-import { MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService';
+import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService';
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
import { homedir } from 'os';
@@ -65,7 +62,7 @@ import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIp
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
-import { URLService } from 'vs/platform/url/node/urlService';
+import { NativeURLService } from 'vs/platform/url/common/urlService';
import { WorkspacesMainService, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { statSync } from 'fs';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';
@@ -81,6 +78,8 @@ import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common
import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels';
+import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService';
+import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
export class CodeApplication extends Disposable {
private windowsMainService: IWindowsMainService | undefined;
@@ -467,10 +466,11 @@ export class CodeApplication extends Disposable {
const diagnosticsChannel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics')));
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel]));
- services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv]));
+ services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv]));
services.set(IElectronMainService, new SyncDescriptor(ElectronMainService));
+ services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService));
services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService));
- services.set(IMenubarService, new SyncDescriptor(MenubarMainService));
+ services.set(IMenubarMainService, new SyncDescriptor(MenubarMainService));
const storageMainService = new StorageMainService(this.logService, this.environmentService);
services.set(IStorageMainService, storageMainService);
@@ -480,7 +480,7 @@ export class CodeApplication extends Disposable {
services.set(IBackupMainService, backupMainService);
services.set(IWorkspacesHistoryMainService, new SyncDescriptor(WorkspacesHistoryMainService));
- services.set(IURLService, new SyncDescriptor(URLService));
+ services.set(IURLService, new SyncDescriptor(NativeURLService));
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
// Telemetry
@@ -551,8 +551,8 @@ export class CodeApplication extends Disposable {
const updateChannel = new UpdateChannel(updateService);
electronIpcServer.registerChannel('update', updateChannel);
- const issueService = accessor.get(IIssueService);
- const issueChannel = createChannelReceiver(issueService);
+ const issueMainService = accessor.get(IIssueMainService);
+ const issueChannel = createChannelReceiver(issueMainService);
electronIpcServer.registerChannel('issue', issueChannel);
const electronMainService = accessor.get(IElectronMainService);
@@ -568,14 +568,18 @@ export class CodeApplication extends Disposable {
const workspacesChannel = createChannelReceiver(workspacesService);
electronIpcServer.registerChannel('workspaces', workspacesChannel);
- const menubarService = accessor.get(IMenubarService);
- const menubarChannel = createChannelReceiver(menubarService);
+ const menubarMainService = accessor.get(IMenubarMainService);
+ const menubarChannel = createChannelReceiver(menubarMainService);
electronIpcServer.registerChannel('menubar', menubarChannel);
const urlService = accessor.get(IURLService);
const urlChannel = createChannelReceiver(urlService);
electronIpcServer.registerChannel('url', urlChannel);
+ const webviewManagerService = accessor.get(IWebviewManagerService);
+ const webviewChannel = createChannelReceiver(webviewManagerService);
+ electronIpcServer.registerChannel('webview', webviewChannel);
+
const storageMainService = accessor.get(IStorageMainService);
const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService));
electronIpcServer.registerChannel('storage', storageChannel);
diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts
index b74e1c4e9c1..5e09e170c84 100644
--- a/src/vs/code/electron-main/auth.ts
+++ b/src/vs/code/electron-main/auth.ts
@@ -59,7 +59,8 @@ export class ProxyAuthHandler extends Disposable {
title: 'VS Code',
webPreferences: {
nodeIntegration: true,
- webviewTag: true
+ webviewTag: true,
+ enableWebSQL: false
}
};
diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts
index 650d665d2ca..744bfea9530 100644
--- a/src/vs/code/electron-main/main.ts
+++ b/src/vs/code/electron-main/main.ts
@@ -13,7 +13,7 @@ import { mkdirp } from 'vs/base/node/pfs';
import { validatePaths } from 'vs/code/node/paths';
import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net';
-import { createChannelSender } from 'vs/base/parts/ipc/node/ipc';
+import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts
index 782cb228ea4..7ace2583d75 100644
--- a/src/vs/code/electron-main/sharedProcess.ts
+++ b/src/vs/code/electron-main/sharedProcess.ts
@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import { URI } from 'vs/base/common/uri';
import { memoize } from 'vs/base/common/decorators';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { BrowserWindow, ipcMain, WebContents, Event as ElectronEvent } from 'electron';
@@ -32,7 +33,7 @@ export class SharedProcess implements ISharedProcess {
@IThemeMainService private readonly themeMainService: IThemeMainService
) {
// overall ready promise when shared process signals initialization is done
- this._whenReady = new Promise(c => ipcMain.once('shared-process->electron-main: init-done', () => c(undefined)));
+ this._whenReady = new Promise(c => ipcMain.once('vscode:shared-process->electron-main=init-done', () => c(undefined)));
}
@memoize
@@ -41,9 +42,11 @@ export class SharedProcess implements ISharedProcess {
show: false,
backgroundColor: this.themeMainService.getBackgroundColor(),
webPreferences: {
+ preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath,
images: false,
nodeIntegration: true,
webgl: false,
+ enableWebSQL: false,
disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer
}
});
@@ -104,18 +107,18 @@ export class SharedProcess implements ISharedProcess {
return new Promise(c => {
// send payload once shared process is ready to receive it
- disposables.add(Event.once(Event.fromNodeEventEmitter(ipcMain, 'shared-process->electron-main: ready-for-payload', ({ sender }: { sender: WebContents }) => sender))(sender => {
- sender.send('electron-main->shared-process: payload', {
+ disposables.add(Event.once(Event.fromNodeEventEmitter(ipcMain, 'vscode:shared-process->electron-main=ready-for-payload', ({ sender }: { sender: WebContents }) => sender))(sender => {
+ sender.send('vscode:electron-main->shared-process=payload', {
sharedIPCHandle: this.environmentService.sharedIPCHandle,
args: this.environmentService.args,
logLevel: this.logService.getLevel()
});
// signal exit to shared process when we get disposed
- disposables.add(toDisposable(() => sender.send('electron-main->shared-process: exit')));
+ disposables.add(toDisposable(() => sender.send('vscode:electron-main->shared-process=exit')));
// complete IPC-ready promise when shared process signals this to us
- ipcMain.once('shared-process->electron-main: ipc-ready', () => c(undefined));
+ ipcMain.once('vscode:shared-process->electron-main=ipc-ready', () => c(undefined));
}));
});
}
diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts
index 2cdb6b90d6d..78095162798 100644
--- a/src/vs/code/electron-main/window.ts
+++ b/src/vs/code/electron-main/window.ts
@@ -163,9 +163,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
show: !isFullscreenOrMaximized,
title: product.nameLong,
webPreferences: {
+ preload: URI.parse(this.doGetPreloadUrl()).fsPath,
nodeIntegration: true,
nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER,
- webviewTag: true
+ webviewTag: true,
+ enableWebSQL: false
}
};
@@ -599,9 +601,12 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
// Do not set to empty configuration at startup if setting is empty to not override configuration through CLI options:
const env = process.env;
- const newHttpProxy = (this.configurationService.getValue('http.proxy') || '').trim()
+ let newHttpProxy = (this.configurationService.getValue('http.proxy') || '').trim()
|| (env.https_proxy || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.HTTP_PROXY || '').trim() // Not standardized.
|| undefined;
+ if (newHttpProxy?.endsWith('/')) {
+ newHttpProxy = newHttpProxy.substr(0, newHttpProxy.length - 1);
+ }
const newNoProxy = (env.no_proxy || env.NO_PROXY || '').trim() || undefined; // Not standardized.
if ((newHttpProxy || '').indexOf('@') === -1 && (newHttpProxy !== this.currentHttpProxy || newNoProxy !== this.currentNoProxy)) {
this.currentHttpProxy = newHttpProxy;
@@ -776,6 +781,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`;
}
+ private doGetPreloadUrl(): string {
+ return require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js');
+ }
+
serializeWindowState(): IWindowState {
if (!this._win) {
return defaultWindowState();
diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts
index ea0f62156a2..e68a950da08 100644
--- a/src/vs/code/node/cliProcessMain.ts
+++ b/src/vs/code/node/cliProcessMain.ts
@@ -86,7 +86,7 @@ export class Main {
} else if (argv['list-extensions']) {
await this.listExtensions(!!argv['show-versions'], argv['category']);
} else if (argv['install-extension']) {
- await this.installExtensions(argv['install-extension'], !!argv['force']);
+ await this.installExtensions(argv['install-extension'], !!argv['force'], !!argv['donot-sync']);
} else if (argv['uninstall-extension']) {
await this.uninstallExtension(argv['uninstall-extension']);
} else if (argv['locate-extension']) {
@@ -126,7 +126,7 @@ export class Main {
extensions.forEach(e => console.log(getId(e.manifest, showVersions)));
}
- private async installExtensions(extensions: string[], force: boolean): Promise {
+ private async installExtensions(extensions: string[], force: boolean, donotSync: boolean): Promise {
const failed: string[] = [];
const installedExtensionsManifests: IExtensionManifest[] = [];
if (extensions.length) {
@@ -135,7 +135,7 @@ export class Main {
for (const extension of extensions) {
try {
- const manifest = await this.installExtension(extension, force);
+ const manifest = await this.installExtension(extension, force, donotSync);
if (manifest) {
installedExtensionsManifests.push(manifest);
}
@@ -150,7 +150,7 @@ export class Main {
return failed.length ? Promise.reject(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))) : Promise.resolve();
}
- private async installExtension(extension: string, force: boolean): Promise {
+ private async installExtension(extension: string, force: boolean, donotSync: boolean): Promise {
if (/\.vsix$/i.test(extension)) {
extension = path.isAbsolute(extension) ? extension : path.join(process.cwd(), extension);
@@ -158,7 +158,7 @@ export class Main {
const valid = await this.validate(manifest, force);
if (valid) {
- return this.extensionManagementService.install(URI.file(extension)).then(id => {
+ return this.extensionManagementService.install(URI.file(extension), donotSync).then(id => {
console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed.", getBaseLabel(extension)));
return manifest;
}, error => {
@@ -205,7 +205,7 @@ export class Main {
}
console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, extension.version));
}
- await this.installFromGallery(id, extension);
+ await this.installFromGallery(id, extension, donotSync);
return manifest;
}));
}
@@ -227,11 +227,11 @@ export class Main {
return true;
}
- private async installFromGallery(id: string, extension: IGalleryExtension): Promise {
+ private async installFromGallery(id: string, extension: IGalleryExtension, donotSync: boolean): Promise {
console.log(localize('installing', "Installing extension '{0}' v{1}...", id, extension.version));
try {
- await this.extensionManagementService.installFromGallery(extension);
+ await this.extensionManagementService.installFromGallery(extension, donotSync);
console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, extension.version));
} catch (error) {
if (isPromiseCanceledError(error)) {
diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts
index 24c2529c587..b2912383c3c 100644
--- a/src/vs/code/node/paths.ts
+++ b/src/vs/code/node/paths.ts
@@ -33,9 +33,9 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] {
const result = args.map(arg => {
let pathCandidate = String(arg);
- let parsedPath: IPathWithLineAndColumn | undefined = undefined;
+ let parsedPath: extpath.IPathWithLineAndColumn | undefined = undefined;
if (gotoLineMode) {
- parsedPath = parseLineAndColumnAware(pathCandidate);
+ parsedPath = extpath.parseLineAndColumnAware(pathCandidate);
pathCandidate = parsedPath.path;
}
@@ -87,42 +87,7 @@ function preparePath(cwd: string, p: string): string {
return p;
}
-export interface IPathWithLineAndColumn {
- path: string;
- line?: number;
- column?: number;
-}
-
-export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn {
- const segments = rawPath.split(':'); // C:\file.txt::
-
- let path: string | null = null;
- let line: number | null = null;
- let column: number | null = null;
-
- segments.forEach(segment => {
- const segmentAsNumber = Number(segment);
- if (!types.isNumber(segmentAsNumber)) {
- path = !!path ? [path, segment].join(':') : segment; // a colon can well be part of a path (e.g. C:\...)
- } else if (line === null) {
- line = segmentAsNumber;
- } else if (column === null) {
- column = segmentAsNumber;
- }
- });
-
- if (!path) {
- throw new Error('Format for `--goto` should be: `FILE:LINE(:COLUMN)`');
- }
-
- return {
- path: path,
- line: line !== null ? line : undefined,
- column: column !== null ? column : line !== null ? 1 : undefined // if we have a line, make sure column is also set
- };
-}
-
-function toPath(p: IPathWithLineAndColumn): string {
+function toPath(p: extpath.IPathWithLineAndColumn): string {
const segments = [p.path];
if (types.isNumber(p.line)) {
diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts
index 6b28b324b2f..3de78f2018c 100644
--- a/src/vs/editor/browser/controller/coreCommands.ts
+++ b/src/vs/editor/browser/controller/coreCommands.ts
@@ -10,7 +10,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Command, EditorCommand, ICommandOptions, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ColumnSelection, IColumnSelectResult } from 'vs/editor/common/controller/cursorColumnSelection';
-import { CursorContext, CursorState, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon';
+import { CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from 'vs/editor/common/controller/cursorCommon';
import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { CursorMove as CursorMove_, CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands';
@@ -32,29 +32,15 @@ const CORE_WEIGHT = KeybindingWeight.EditorCore;
export abstract class CoreEditorCommand extends EditorCommand {
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
- const cursors = editor._getCursors();
- if (!cursors) {
- // the editor has no view => has no cursors
- return;
- }
- this.runCoreEditorCommand(cursors, args || {});
- }
-
- public abstract runCoreEditorCommand(cursors: ICursors, args: any): void;
-}
-
-export abstract class CoreEditorCommand2 extends EditorCommand {
- public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
- const cursors = editor._getCursors();
const viewModel = editor._getViewModel();
- if (!cursors || !viewModel) {
+ if (!viewModel) {
// the editor has no view => has no cursors
return;
}
- this.runCoreEditorCommand(editor, viewModel, cursors, args || {});
+ this.runCoreEditorCommand(viewModel, args || {});
}
- public abstract runCoreEditorCommand(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, args: any): void;
+ public abstract runCoreEditorCommand(viewModel: IViewModel, args: any): void;
}
export namespace EditorScroll_ {
@@ -301,16 +287,16 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
- CursorMoveCommands.moveTo(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition)
+ CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition)
]
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
}
@@ -327,21 +313,25 @@ export namespace CoreNavigationCommands {
}));
abstract class ColumnSelectCommand extends CoreEditorCommand {
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- const result = this._getColumnSelectResult(cursors.context, cursors.getPrimaryCursor(), cursors.getColumnSelectData(), args);
- cursors.setStates(args.source, CursorChangeReason.Explicit, result.viewStates.map((viewState) => CursorState.fromViewState(viewState)));
- cursors.setColumnSelectData({
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ const result = this._getColumnSelectResult(viewModel, viewModel.getPrimaryCursorState(), viewModel.getCursorColumnSelectData(), args);
+ viewModel.setCursorStates(args.source, CursorChangeReason.Explicit, result.viewStates.map((viewState) => CursorState.fromViewState(viewState)));
+ viewModel.setCursorColumnSelectData({
isReal: true,
fromViewLineNumber: result.fromLineNumber,
fromViewVisualColumn: result.fromVisualColumn,
toViewLineNumber: result.toLineNumber,
toViewVisualColumn: result.toVisualColumn
});
- cursors.reveal(args.source, true, (result.reversed ? RevealTarget.TopMost : RevealTarget.BottomMost), ScrollType.Smooth);
+ if (result.reversed) {
+ viewModel.revealTopMostCursor(args.source);
+ } else {
+ viewModel.revealBottomMostCursor(args.source);
+ }
}
- protected abstract _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult;
+ protected abstract _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult;
}
@@ -353,15 +343,15 @@ export namespace CoreNavigationCommands {
});
}
- protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
+ protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
// validate `args`
- const validatedPosition = context.model.validatePosition(args.position);
- const validatedViewPosition = context.coordinatesConverter.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition);
+ const validatedPosition = viewModel.model.validatePosition(args.position);
+ const validatedViewPosition = viewModel.coordinatesConverter.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition);
let fromViewLineNumber = args.doColumnSelect ? prevColumnSelectData.fromViewLineNumber : validatedViewPosition.lineNumber;
let fromViewVisualColumn = args.doColumnSelect ? prevColumnSelectData.fromViewVisualColumn : args.mouseColumn - 1;
- return ColumnSelection.columnSelect(context.config, context.viewModel, fromViewLineNumber, fromViewVisualColumn, validatedViewPosition.lineNumber, args.mouseColumn - 1);
+ return ColumnSelection.columnSelect(viewModel.cursorConfig, viewModel, fromViewLineNumber, fromViewVisualColumn, validatedViewPosition.lineNumber, args.mouseColumn - 1);
}
});
@@ -379,8 +369,8 @@ export namespace CoreNavigationCommands {
});
}
- protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
- return ColumnSelection.columnSelectLeft(context.config, context.viewModel, prevColumnSelectData);
+ protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
+ return ColumnSelection.columnSelectLeft(viewModel.cursorConfig, viewModel, prevColumnSelectData);
}
});
@@ -398,8 +388,8 @@ export namespace CoreNavigationCommands {
});
}
- protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
- return ColumnSelection.columnSelectRight(context.config, context.viewModel, prevColumnSelectData);
+ protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
+ return ColumnSelection.columnSelectRight(viewModel.cursorConfig, viewModel, prevColumnSelectData);
}
});
@@ -412,8 +402,8 @@ export namespace CoreNavigationCommands {
this._isPaged = opts.isPaged;
}
- protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
- return ColumnSelection.columnSelectUp(context.config, context.viewModel, prevColumnSelectData, this._isPaged);
+ protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
+ return ColumnSelection.columnSelectUp(viewModel.cursorConfig, viewModel, prevColumnSelectData, this._isPaged);
}
}
@@ -450,8 +440,8 @@ export namespace CoreNavigationCommands {
this._isPaged = opts.isPaged;
}
- protected _getColumnSelectResult(context: CursorContext, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
- return ColumnSelection.columnSelectDown(context.config, context.viewModel, prevColumnSelectData, this._isPaged);
+ protected _getColumnSelectResult(viewModel: IViewModel, primary: CursorState, prevColumnSelectData: IColumnSelectData, args: any): IColumnSelectResult {
+ return ColumnSelection.columnSelectDown(viewModel.cursorConfig, viewModel, prevColumnSelectData, this._isPaged);
}
}
@@ -479,7 +469,7 @@ export namespace CoreNavigationCommands {
}
}));
- export class CursorMoveImpl extends CoreEditorCommand2 {
+ export class CursorMoveImpl extends CoreEditorCommand {
constructor() {
super({
id: 'cursorMove',
@@ -488,26 +478,26 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, args: any): void {
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
const parsed = CursorMove_.parse(args);
if (!parsed) {
// illegal arguments
return;
}
- this._runCursorMove(editor, viewModel, cursors, args.source, parsed);
+ this._runCursorMove(viewModel, args.source, parsed);
}
- private _runCursorMove(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, source: string | null | undefined, args: CursorMove_.ParsedArguments): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ private _runCursorMove(viewModel: IViewModel, source: string | null | undefined, args: CursorMove_.ParsedArguments): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
source,
CursorChangeReason.Explicit,
- CursorMoveImpl._move(editor, viewModel, cursors.context, cursors.getAll(), args)
+ CursorMoveImpl._move(viewModel, viewModel.getCursorStates(), args)
);
- cursors.reveal(source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(source, true);
}
- private static _move(editor: ICodeEditor, viewModel: IViewModel, context: CursorContext, cursors: CursorState[], args: CursorMove_.ParsedArguments): PartialCursorState[] | null {
+ private static _move(viewModel: IViewModel, cursors: CursorState[], args: CursorMove_.ParsedArguments): PartialCursorState[] | null {
const inSelectionMode = args.select;
const value = args.value;
@@ -521,13 +511,13 @@ export namespace CoreNavigationCommands {
case CursorMove_.Direction.WrappedLineColumnCenter:
case CursorMove_.Direction.WrappedLineEnd:
case CursorMove_.Direction.WrappedLineLastNonWhitespaceCharacter:
- return CursorMoveCommands.simpleMove(context, cursors, args.direction, inSelectionMode, value, args.unit);
+ return CursorMoveCommands.simpleMove(viewModel, cursors, args.direction, inSelectionMode, value, args.unit);
case CursorMove_.Direction.ViewPortTop:
case CursorMove_.Direction.ViewPortBottom:
case CursorMove_.Direction.ViewPortCenter:
case CursorMove_.Direction.ViewPortIfOutside:
- return CursorMoveCommands.viewportMove(viewModel, context, cursors, args.direction, inSelectionMode, value);
+ return CursorMoveCommands.viewportMove(viewModel, cursors, args.direction, inSelectionMode, value);
}
return null;
@@ -549,7 +539,7 @@ export namespace CoreNavigationCommands {
this._staticArgs = opts.args;
}
- public runCoreEditorCommand(cursors: ICursors, dynamicArgs: any): void {
+ public runCoreEditorCommand(viewModel: IViewModel, dynamicArgs: any): void {
let args = this._staticArgs;
if (this._staticArgs.value === Constants.PAGE_SIZE_MARKER) {
// -1 is a marker for page size
@@ -557,17 +547,17 @@ export namespace CoreNavigationCommands {
direction: this._staticArgs.direction,
unit: this._staticArgs.unit,
select: this._staticArgs.select,
- value: cursors.context.config.pageSize
+ value: viewModel.cursorConfig.pageSize
};
}
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
dynamicArgs.source,
CursorChangeReason.Explicit,
- CursorMoveCommands.simpleMove(cursors.context, cursors.getAll(), args.direction, args.select, args.value, args.unit)
+ CursorMoveCommands.simpleMove(viewModel, viewModel.getCursorStates(), args.direction, args.select, args.value, args.unit)
);
- cursors.reveal(dynamicArgs.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(dynamicArgs.source, true);
}
}
@@ -781,17 +771,15 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- const context = cursors.context;
-
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
let newState: PartialCursorState;
if (args.wholeLine) {
- newState = CursorMoveCommands.line(context, cursors.getPrimaryCursor(), false, args.position, args.viewPosition);
+ newState = CursorMoveCommands.line(viewModel, viewModel.getPrimaryCursorState(), false, args.position, args.viewPosition);
} else {
- newState = CursorMoveCommands.moveTo(context, cursors.getPrimaryCursor(), false, args.position, args.viewPosition);
+ newState = CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), false, args.position, args.viewPosition);
}
- const states: PartialCursorState[] = cursors.getAll();
+ const states: PartialCursorState[] = viewModel.getCursorStates();
// Check if we should remove a cursor (sort of like a toggle)
if (states.length > 1) {
@@ -812,8 +800,8 @@ export namespace CoreNavigationCommands {
// => Remove the cursor
states.splice(i, 1);
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
states
@@ -825,8 +813,8 @@ export namespace CoreNavigationCommands {
// => Add the new cursor
states.push(newState);
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
states
@@ -842,17 +830,15 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- const context = cursors.context;
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex();
- const lastAddedCursorIndex = cursors.getLastAddedCursorIndex();
-
- const states = cursors.getAll();
+ const states = viewModel.getCursorStates();
const newStates: PartialCursorState[] = states.slice(0);
- newStates[lastAddedCursorIndex] = CursorMoveCommands.moveTo(context, states[lastAddedCursorIndex], true, args.position, args.viewPosition);
+ newStates[lastAddedCursorIndex] = CursorMoveCommands.moveTo(viewModel, states[lastAddedCursorIndex], true, args.position, args.viewPosition);
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
newStates
@@ -869,14 +855,14 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
- CursorMoveCommands.moveToBeginningOfLine(cursors.context, cursors.getAll(), this._inSelectionMode)
+ CursorMoveCommands.moveToBeginningOfLine(viewModel, viewModel.getCursorStates(), this._inSelectionMode)
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
}
@@ -913,17 +899,17 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
- this._exec(cursors.context, cursors.getAll())
+ this._exec(viewModel.getCursorStates())
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
- private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] {
+ private _exec(cursors: CursorState[]): PartialCursorState[] {
const result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
@@ -967,14 +953,14 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
- CursorMoveCommands.moveToEndOfLine(cursors.context, cursors.getAll(), this._inSelectionMode)
+ CursorMoveCommands.moveToEndOfLine(viewModel, viewModel.getCursorStates(), this._inSelectionMode)
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
}
@@ -1011,22 +997,22 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
- this._exec(cursors.context, cursors.getAll())
+ this._exec(viewModel, viewModel.getCursorStates())
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
- private _exec(context: CursorContext, cursors: CursorState[]): PartialCursorState[] {
+ private _exec(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] {
const result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const lineNumber = cursor.modelState.position.lineNumber;
- const maxColumn = context.model.getLineMaxColumn(lineNumber);
+ const maxColumn = viewModel.model.getLineMaxColumn(lineNumber);
result[i] = CursorState.fromModelState(cursor.modelState.move(this._inSelectionMode, lineNumber, maxColumn, 0));
}
return result;
@@ -1066,14 +1052,14 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
- CursorMoveCommands.moveToBeginningOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode)
+ CursorMoveCommands.moveToBeginningOfBuffer(viewModel, viewModel.getCursorStates(), this._inSelectionMode)
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
}
@@ -1110,14 +1096,14 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
- CursorMoveCommands.moveToEndOfBuffer(cursors.context, cursors.getAll(), this._inSelectionMode)
+ CursorMoveCommands.moveToEndOfBuffer(viewModel, viewModel.getCursorStates(), this._inSelectionMode)
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
}
@@ -1145,7 +1131,7 @@ export namespace CoreNavigationCommands {
}
}));
- export class EditorScrollImpl extends CoreEditorCommand2 {
+ export class EditorScrollImpl extends CoreEditorCommand {
constructor() {
super({
id: 'editorScroll',
@@ -1154,40 +1140,40 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, args: any): void {
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
const parsed = EditorScroll_.parse(args);
if (!parsed) {
// illegal arguments
return;
}
- this._runEditorScroll(editor, viewModel, cursors, args.source, parsed);
+ this._runEditorScroll(viewModel, args.source, parsed);
}
- _runEditorScroll(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, source: string | null | undefined, args: EditorScroll_.ParsedArguments): void {
+ _runEditorScroll(viewModel: IViewModel, source: string | null | undefined, args: EditorScroll_.ParsedArguments): void {
- const desiredScrollTop = this._computeDesiredScrollTop(editor, viewModel, cursors.context, args);
+ const desiredScrollTop = this._computeDesiredScrollTop(viewModel, args);
if (args.revealCursor) {
// must ensure cursor is in new visible range
const desiredVisibleViewRange = viewModel.getCompletelyVisibleViewRangeAtScrollTop(desiredScrollTop);
- cursors.setStates(
+ viewModel.setCursorStates(
source,
CursorChangeReason.Explicit,
[
- CursorMoveCommands.findPositionInViewportIfOutside(cursors.context, cursors.getPrimaryCursor(), desiredVisibleViewRange, args.select)
+ CursorMoveCommands.findPositionInViewportIfOutside(viewModel, viewModel.getPrimaryCursorState(), desiredVisibleViewRange, args.select)
]
);
}
- editor.setScrollTop(desiredScrollTop, ScrollType.Smooth);
+ viewModel.setScrollTop(desiredScrollTop, ScrollType.Smooth);
}
- private _computeDesiredScrollTop(editor: ICodeEditor, viewModel: IViewModel, context: CursorContext, args: EditorScroll_.ParsedArguments): number {
+ private _computeDesiredScrollTop(viewModel: IViewModel, args: EditorScroll_.ParsedArguments): number {
if (args.unit === EditorScroll_.Unit.Line) {
// scrolling by model lines
const visibleViewRange = viewModel.getCompletelyVisibleViewRange();
- const visibleModelRange = context.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
+ const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
let desiredTopModelLineNumber: number;
if (args.direction === EditorScroll_.Direction.Up) {
@@ -1195,28 +1181,29 @@ export namespace CoreNavigationCommands {
desiredTopModelLineNumber = Math.max(1, visibleModelRange.startLineNumber - args.value);
} else {
// must go x model lines down
- desiredTopModelLineNumber = Math.min(context.model.getLineCount(), visibleModelRange.startLineNumber + args.value);
+ desiredTopModelLineNumber = Math.min(viewModel.model.getLineCount(), visibleModelRange.startLineNumber + args.value);
}
- return editor.getTopForLineNumber(desiredTopModelLineNumber);
+ const viewPosition = viewModel.coordinatesConverter.convertModelPositionToViewPosition(new Position(desiredTopModelLineNumber, 1));
+ return viewModel.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
}
let noOfLines: number;
if (args.unit === EditorScroll_.Unit.Page) {
- noOfLines = context.config.pageSize * args.value;
+ noOfLines = viewModel.cursorConfig.pageSize * args.value;
} else if (args.unit === EditorScroll_.Unit.HalfPage) {
- noOfLines = Math.round(context.config.pageSize / 2) * args.value;
+ noOfLines = Math.round(viewModel.cursorConfig.pageSize / 2) * args.value;
} else {
noOfLines = args.value;
}
const deltaLines = (args.direction === EditorScroll_.Direction.Up ? -1 : 1) * noOfLines;
- return editor.getScrollTop() + deltaLines * context.config.lineHeight;
+ return viewModel.getScrollTop() + deltaLines * viewModel.cursorConfig.lineHeight;
}
}
export const EditorScroll: EditorScrollImpl = registerEditorCommand(new EditorScrollImpl());
- export const ScrollLineUp: CoreEditorCommand2 = registerEditorCommand(new class extends CoreEditorCommand2 {
+ export const ScrollLineUp: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand {
constructor() {
super({
id: 'scrollLineUp',
@@ -1230,8 +1217,8 @@ export namespace CoreNavigationCommands {
});
}
- runCoreEditorCommand(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, args: any): void {
- EditorScroll._runEditorScroll(editor, viewModel, cursors, args.source, {
+ runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ EditorScroll._runEditorScroll(viewModel, args.source, {
direction: EditorScroll_.Direction.Up,
unit: EditorScroll_.Unit.WrappedLine,
value: 1,
@@ -1241,7 +1228,7 @@ export namespace CoreNavigationCommands {
}
});
- export const ScrollPageUp: CoreEditorCommand2 = registerEditorCommand(new class extends CoreEditorCommand2 {
+ export const ScrollPageUp: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand {
constructor() {
super({
id: 'scrollPageUp',
@@ -1256,8 +1243,8 @@ export namespace CoreNavigationCommands {
});
}
- runCoreEditorCommand(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, args: any): void {
- EditorScroll._runEditorScroll(editor, viewModel, cursors, args.source, {
+ runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ EditorScroll._runEditorScroll(viewModel, args.source, {
direction: EditorScroll_.Direction.Up,
unit: EditorScroll_.Unit.Page,
value: 1,
@@ -1267,7 +1254,7 @@ export namespace CoreNavigationCommands {
}
});
- export const ScrollLineDown: CoreEditorCommand2 = registerEditorCommand(new class extends CoreEditorCommand2 {
+ export const ScrollLineDown: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand {
constructor() {
super({
id: 'scrollLineDown',
@@ -1281,8 +1268,8 @@ export namespace CoreNavigationCommands {
});
}
- runCoreEditorCommand(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, args: any): void {
- EditorScroll._runEditorScroll(editor, viewModel, cursors, args.source, {
+ runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ EditorScroll._runEditorScroll(viewModel, args.source, {
direction: EditorScroll_.Direction.Down,
unit: EditorScroll_.Unit.WrappedLine,
value: 1,
@@ -1292,7 +1279,7 @@ export namespace CoreNavigationCommands {
}
});
- export const ScrollPageDown: CoreEditorCommand2 = registerEditorCommand(new class extends CoreEditorCommand2 {
+ export const ScrollPageDown: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand {
constructor() {
super({
id: 'scrollPageDown',
@@ -1307,8 +1294,8 @@ export namespace CoreNavigationCommands {
});
}
- runCoreEditorCommand(editor: ICodeEditor, viewModel: IViewModel, cursors: ICursors, args: any): void {
- EditorScroll._runEditorScroll(editor, viewModel, cursors, args.source, {
+ runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ EditorScroll._runEditorScroll(viewModel, args.source, {
direction: EditorScroll_.Direction.Down,
unit: EditorScroll_.Unit.Page,
value: 1,
@@ -1327,16 +1314,16 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
- CursorMoveCommands.word(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position)
+ CursorMoveCommands.word(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position)
]
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
}
@@ -1360,18 +1347,16 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- const context = cursors.context;
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex();
- const lastAddedCursorIndex = cursors.getLastAddedCursorIndex();
-
- const states = cursors.getAll();
+ const states = viewModel.getCursorStates();
const newStates: PartialCursorState[] = states.slice(0);
const lastAddedState = states[lastAddedCursorIndex];
- newStates[lastAddedCursorIndex] = CursorMoveCommands.word(context, lastAddedState, lastAddedState.modelState.hasSelection(), args.position);
+ newStates[lastAddedCursorIndex] = CursorMoveCommands.word(viewModel, lastAddedState, lastAddedState.modelState.hasSelection(), args.position);
- context.model.pushStackElement();
- cursors.setStates(
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
newStates
@@ -1387,16 +1372,16 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
- CursorMoveCommands.line(cursors.context, cursors.getPrimaryCursor(), this._inSelectionMode, args.position, args.viewPosition)
+ CursorMoveCommands.line(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition)
]
);
- cursors.reveal(args.source, false, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, false);
}
}
@@ -1420,15 +1405,15 @@ export namespace CoreNavigationCommands {
this._inSelectionMode = opts.inSelectionMode;
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- const lastAddedCursorIndex = cursors.getLastAddedCursorIndex();
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ const lastAddedCursorIndex = viewModel.getLastAddedCursorIndex();
- const states = cursors.getAll();
+ const states = viewModel.getCursorStates();
const newStates: PartialCursorState[] = states.slice(0);
- newStates[lastAddedCursorIndex] = CursorMoveCommands.line(cursors.context, states[lastAddedCursorIndex], this._inSelectionMode, args.position, args.viewPosition);
+ newStates[lastAddedCursorIndex] = CursorMoveCommands.line(viewModel, states[lastAddedCursorIndex], this._inSelectionMode, args.position, args.viewPosition);
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
newStates
@@ -1461,14 +1446,14 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
- CursorMoveCommands.expandLineSelection(cursors.context, cursors.getAll())
+ CursorMoveCommands.expandLineSelection(viewModel, viewModel.getCursorStates())
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
});
@@ -1487,16 +1472,16 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
- CursorMoveCommands.cancelSelection(cursors.context, cursors.getPrimaryCursor())
+ CursorMoveCommands.cancelSelection(viewModel, viewModel.getPrimaryCursorState())
]
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
});
@@ -1514,16 +1499,16 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
- cursors.getPrimaryCursor()
+ viewModel.getPrimaryCursorState()
]
);
- cursors.reveal(args.source, true, RevealTarget.Primary, ScrollType.Smooth);
+ viewModel.revealPrimaryCursor(args.source, true);
}
});
@@ -1536,20 +1521,20 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
const revealLineArg = args;
let lineNumber = (revealLineArg.lineNumber || 0) + 1;
if (lineNumber < 1) {
lineNumber = 1;
}
- const lineCount = cursors.context.model.getLineCount();
+ const lineCount = viewModel.model.getLineCount();
if (lineNumber > lineCount) {
lineNumber = lineCount;
}
const range = new Range(
lineNumber, 1,
- lineNumber, cursors.context.model.getLineMaxColumn(lineNumber)
+ lineNumber, viewModel.model.getLineMaxColumn(lineNumber)
);
let revealAt = VerticalRevealType.Simple;
@@ -1569,9 +1554,9 @@ export namespace CoreNavigationCommands {
}
}
- const viewRange = cursors.context.coordinatesConverter.convertModelRangeToViewRange(range);
+ const viewRange = viewModel.coordinatesConverter.convertModelRangeToViewRange(range);
- cursors.revealRange(args.source, false, viewRange, revealAt, ScrollType.Smooth);
+ viewModel.revealRange(args.source, false, viewRange, revealAt, ScrollType.Smooth);
}
});
@@ -1583,13 +1568,13 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
- CursorMoveCommands.selectAll(cursors.context, cursors.getPrimaryCursor())
+ CursorMoveCommands.selectAll(viewModel, viewModel.getPrimaryCursorState())
]
);
}
@@ -1603,9 +1588,9 @@ export namespace CoreNavigationCommands {
});
}
- public runCoreEditorCommand(cursors: ICursors, args: any): void {
- cursors.context.model.pushStackElement();
- cursors.setStates(
+ public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
+ viewModel.model.pushStackElement();
+ viewModel.setCursorStates(
args.source,
CursorChangeReason.Explicit,
[
@@ -1736,15 +1721,15 @@ export namespace CoreEditingCommands {
export abstract class CoreEditingCommand extends EditorCommand {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
- const cursors = editor._getCursors();
- if (!cursors) {
+ const viewModel = editor._getViewModel();
+ if (!viewModel) {
// the editor has no view => has no cursors
return;
}
- this.runCoreEditingCommand(editor, cursors, args || {});
+ this.runCoreEditingCommand(editor, viewModel, args || {});
}
- public abstract runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void;
+ public abstract runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void;
}
export const LineBreakInsert: EditorCommand = registerEditorCommand(new class extends CoreEditingCommand {
@@ -1761,9 +1746,9 @@ export namespace CoreEditingCommands {
});
}
- public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void {
+ public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void {
editor.pushUndoStop();
- editor.executeCommands(this.id, TypeOperations.lineBreakInsert(cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection)));
+ editor.executeCommands(this.id, TypeOperations.lineBreakInsert(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)));
}
});
@@ -1783,9 +1768,9 @@ export namespace CoreEditingCommands {
});
}
- public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void {
+ public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void {
editor.pushUndoStop();
- editor.executeCommands(this.id, TypeOperations.outdent(cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection)));
+ editor.executeCommands(this.id, TypeOperations.outdent(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)));
editor.pushUndoStop();
}
});
@@ -1806,9 +1791,9 @@ export namespace CoreEditingCommands {
});
}
- public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void {
+ public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void {
editor.pushUndoStop();
- editor.executeCommands(this.id, TypeOperations.tab(cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection)));
+ editor.executeCommands(this.id, TypeOperations.tab(viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)));
editor.pushUndoStop();
}
});
@@ -1828,13 +1813,13 @@ export namespace CoreEditingCommands {
});
}
- public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void {
- const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(cursors.getPrevEditOperationType(), cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection));
+ public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void {
+ const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection));
if (shouldPushStackElementBefore) {
editor.pushUndoStop();
}
editor.executeCommands(this.id, commands);
- cursors.setPrevEditOperationType(EditOperationType.DeletingLeft);
+ viewModel.setPrevEditOperationType(EditOperationType.DeletingLeft);
}
});
@@ -1852,13 +1837,13 @@ export namespace CoreEditingCommands {
});
}
- public runCoreEditingCommand(editor: ICodeEditor, cursors: ICursors, args: any): void {
- const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(cursors.getPrevEditOperationType(), cursors.context.config, cursors.context.model, cursors.getAll().map(s => s.modelState.selection));
+ public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void {
+ const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection));
if (shouldPushStackElementBefore) {
editor.pushUndoStop();
}
editor.executeCommands(this.id, commands);
- cursors.setPrevEditOperationType(EditOperationType.DeletingRight);
+ viewModel.setPrevEditOperationType(EditOperationType.DeletingRight);
}
});
diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts
index f0ad0cf79dc..6123c7971ae 100644
--- a/src/vs/editor/browser/controller/mouseTarget.ts
+++ b/src/vs/editor/browser/controller/mouseTarget.ts
@@ -420,7 +420,7 @@ class HitTestRequest extends BareHitTestRequest {
let mouseColumn = this.mouseColumn;
if (position && position.column < this._ctx.model.getLineMaxColumn(position.lineNumber)) {
// Most likely, the line contains foreign decorations...
- mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getOptions().tabSize) + 1;
+ mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getTextModelOptions().tabSize) + 1;
}
return new MouseTarget(this.target, type, mouseColumn, position, range, detail);
}
diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts
index ffe55acb07c..2b930bb8461 100644
--- a/src/vs/editor/browser/controller/pointerHandler.ts
+++ b/src/vs/editor/browser/controller/pointerHandler.ts
@@ -100,7 +100,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable {
}
private _onGestureChange(e: IThrottledGestureEvent): void {
- this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY);
+ this._context.model.deltaScrollNow(-e.translationX, -e.translationY);
}
public dispose(): void {
@@ -177,7 +177,7 @@ export class PointerEventHandler extends MouseHandler {
private onChange(e: GestureEvent): void {
if (this._lastPointerType === 'touch') {
- this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY);
+ this._context.model.deltaScrollNow(-e.translationX, -e.translationY);
}
}
@@ -215,7 +215,7 @@ class TouchHandler extends MouseHandler {
}
private onChange(e: GestureEvent): void {
- this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY);
+ this._context.model.deltaScrollNow(-e.translationX, -e.translationY);
}
}
diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts
index b07d751625d..9eee0dfe476 100644
--- a/src/vs/editor/browser/controller/textAreaHandler.ts
+++ b/src/vs/editor/browser/controller/textAreaHandler.ts
@@ -258,14 +258,13 @@ export class TextAreaHandler extends ViewPart {
const lineNumber = this._selections[0].startLineNumber;
const column = this._selections[0].startColumn - (e.moveOneCharacterLeft ? 1 : 0);
- this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent(
+ this._context.model.revealRange(
'keyboard',
- new Range(lineNumber, column, lineNumber, column),
- null,
- viewEvents.VerticalRevealType.Simple,
true,
+ new Range(lineNumber, column, lineNumber, column),
+ viewEvents.VerticalRevealType.Simple,
ScrollType.Immediate
- ));
+ );
// Find range pixel position
const visibleRange = this._viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column);
@@ -307,11 +306,11 @@ export class TextAreaHandler extends ViewPart {
}));
this._register(this._textAreaInput.onFocus(() => {
- this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(true));
+ this._context.model.setHasFocus(true);
}));
this._register(this._textAreaInput.onBlur(() => {
- this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(false));
+ this._context.model.setHasFocus(false);
}));
}
diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts
index 980e6f30af2..d9c89ccef50 100644
--- a/src/vs/editor/browser/editorBrowser.ts
+++ b/src/vs/editor/browser/editorBrowser.ts
@@ -7,7 +7,6 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { IDisposable } from 'vs/base/common/lifecycle';
import { OverviewRulerPosition, ConfigurationChangedEvent, EditorLayoutInfo, IComputedEditorOptions, EditorOption, FindComputedEditorOptionValueById, IEditorOptions, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
-import { ICursors } from 'vs/editor/common/controller/cursorCommon';
import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
@@ -663,11 +662,6 @@ export interface ICodeEditor extends editorCommon.IEditor {
*/
executeCommands(source: string | null | undefined, commands: (editorCommon.ICommand | null)[]): void;
- /**
- * @internal
- */
- _getCursors(): ICursors | null;
-
/**
* @internal
*/
@@ -861,11 +855,6 @@ export interface IActiveCodeEditor extends ICodeEditor {
*/
getModel(): ITextModel;
- /**
- * @internal
- */
- _getCursors(): ICursors;
-
/**
* @internal
*/
diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts
index 3ec0408abfd..6fc163d9c36 100644
--- a/src/vs/editor/browser/view/viewController.ts
+++ b/src/vs/editor/browser/view/viewController.ts
@@ -6,7 +6,7 @@
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { CoreEditorCommand, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
import { IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
-import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
+import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { IConfiguration } from 'vs/editor/common/editorCommon';
@@ -40,8 +40,8 @@ export interface ICommandDelegate {
paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void;
type(text: string): void;
replacePreviousChar(text: string, replaceCharCnt: number): void;
- compositionStart(): void;
- compositionEnd(): void;
+ startComposition(): void;
+ endComposition(): void;
cut(): void;
}
@@ -49,18 +49,18 @@ export class ViewController {
private readonly configuration: IConfiguration;
private readonly viewModel: IViewModel;
- private readonly outgoingEvents: ViewOutgoingEvents;
+ private readonly userInputEvents: ViewUserInputEvents;
private readonly commandDelegate: ICommandDelegate;
constructor(
configuration: IConfiguration,
viewModel: IViewModel,
- outgoingEvents: ViewOutgoingEvents,
+ userInputEvents: ViewUserInputEvents,
commandDelegate: ICommandDelegate
) {
this.configuration = configuration;
this.viewModel = viewModel;
- this.outgoingEvents = outgoingEvents;
+ this.userInputEvents = userInputEvents;
this.commandDelegate = commandDelegate;
}
@@ -82,11 +82,11 @@ export class ViewController {
}
public compositionStart(): void {
- this.commandDelegate.compositionStart();
+ this.commandDelegate.startComposition();
}
public compositionEnd(): void {
- this.commandDelegate.compositionEnd();
+ this.commandDelegate.endComposition();
}
public cut(): void {
@@ -289,42 +289,42 @@ export class ViewController {
}
public emitKeyDown(e: IKeyboardEvent): void {
- this.outgoingEvents.emitKeyDown(e);
+ this.userInputEvents.emitKeyDown(e);
}
public emitKeyUp(e: IKeyboardEvent): void {
- this.outgoingEvents.emitKeyUp(e);
+ this.userInputEvents.emitKeyUp(e);
}
public emitContextMenu(e: IEditorMouseEvent): void {
- this.outgoingEvents.emitContextMenu(e);
+ this.userInputEvents.emitContextMenu(e);
}
public emitMouseMove(e: IEditorMouseEvent): void {
- this.outgoingEvents.emitMouseMove(e);
+ this.userInputEvents.emitMouseMove(e);
}
public emitMouseLeave(e: IPartialEditorMouseEvent): void {
- this.outgoingEvents.emitMouseLeave(e);
+ this.userInputEvents.emitMouseLeave(e);
}
public emitMouseUp(e: IEditorMouseEvent): void {
- this.outgoingEvents.emitMouseUp(e);
+ this.userInputEvents.emitMouseUp(e);
}
public emitMouseDown(e: IEditorMouseEvent): void {
- this.outgoingEvents.emitMouseDown(e);
+ this.userInputEvents.emitMouseDown(e);
}
public emitMouseDrag(e: IEditorMouseEvent): void {
- this.outgoingEvents.emitMouseDrag(e);
+ this.userInputEvents.emitMouseDrag(e);
}
public emitMouseDrop(e: IPartialEditorMouseEvent): void {
- this.outgoingEvents.emitMouseDrop(e);
+ this.userInputEvents.emitMouseDrop(e);
}
public emitMouseWheel(e: IMouseWheelEvent): void {
- this.outgoingEvents.emitMouseWheel(e);
+ this.userInputEvents.emitMouseWheel(e);
}
}
diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts
index ca608d35b5c..1e156722710 100644
--- a/src/vs/editor/browser/view/viewImpl.ts
+++ b/src/vs/editor/browser/view/viewImpl.ts
@@ -14,7 +14,7 @@ import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler';
import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler';
import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor, IEditorAriaOptions } from 'vs/editor/browser/editorBrowser';
import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController';
-import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
+import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays';
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/contentWidgets';
@@ -42,7 +42,6 @@ import { Range } from 'vs/editor/common/core/range';
import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon';
import { RenderingContext } from 'vs/editor/common/view/renderingContext';
import { ViewContext } from 'vs/editor/common/view/viewContext';
-import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
@@ -64,31 +63,27 @@ export interface IOverlayWidgetData {
export class View extends ViewEventHandler {
- private readonly eventDispatcher: ViewEventDispatcher;
-
- private _scrollbar: EditorScrollbar;
+ private readonly _scrollbar: EditorScrollbar;
private readonly _context: ViewContext;
private _selections: Selection[];
// The view lines
- private viewLines: ViewLines;
+ private readonly _viewLines: ViewLines;
// These are parts, but we must do some API related calls on them, so we keep a reference
- private viewZones: ViewZones;
- private contentWidgets: ViewContentWidgets;
- private overlayWidgets: ViewOverlayWidgets;
- private viewCursors: ViewCursors;
- private viewParts: ViewPart[];
+ private readonly _viewZones: ViewZones;
+ private readonly _contentWidgets: ViewContentWidgets;
+ private readonly _overlayWidgets: ViewOverlayWidgets;
+ private readonly _viewCursors: ViewCursors;
+ private readonly _viewParts: ViewPart[];
private readonly _textAreaHandler: TextAreaHandler;
- private readonly pointerHandler: PointerHandler;
-
- private readonly outgoingEvents: ViewOutgoingEvents;
+ private readonly _pointerHandler: PointerHandler;
// Dom nodes
- private linesContent: FastDomNode;
- public domNode: FastDomNode;
- private overflowGuardContainer: FastDomNode;
+ private readonly _linesContent: FastDomNode;
+ public readonly domNode: FastDomNode;
+ private readonly _overflowGuardContainer: FastDomNode;
// Actual mutable state
private _renderAnimationFrame: IDisposable | null;
@@ -98,77 +93,73 @@ export class View extends ViewEventHandler {
configuration: IConfiguration,
themeService: IThemeService,
model: IViewModel,
- outgoingEvents: ViewOutgoingEvents
+ userInputEvents: ViewUserInputEvents
) {
super();
this._selections = [new Selection(1, 1, 1, 1)];
this._renderAnimationFrame = null;
- this.outgoingEvents = outgoingEvents;
- const viewController = new ViewController(configuration, model, this.outgoingEvents, commandDelegate);
-
- // The event dispatcher will always go through _renderOnce before dispatching any events
- this.eventDispatcher = new ViewEventDispatcher((callback: () => void) => this._renderOnce(callback));
-
- // Ensure the view is the first event handler in order to update the layout
- this.eventDispatcher.addEventHandler(this);
+ const viewController = new ViewController(configuration, model, userInputEvents, commandDelegate);
// The view context is passed on to most classes (basically to reduce param. counts in ctors)
- this._context = new ViewContext(configuration, themeService.getColorTheme(), model, this.eventDispatcher);
+ this._context = new ViewContext(configuration, themeService.getColorTheme(), model);
+
+ // Ensure the view is the first event handler in order to update the layout
+ this._context.addEventHandler(this);
this._register(themeService.onDidColorThemeChange(theme => {
this._context.theme.update(theme);
- this.eventDispatcher.emit(new viewEvents.ViewThemeChangedEvent());
+ this._context.model.onDidColorThemeChange();
this.render(true, false);
}));
- this.viewParts = [];
+ this._viewParts = [];
// Keyboard handler
- this._textAreaHandler = new TextAreaHandler(this._context, viewController, this.createTextAreaHandlerHelper());
- this.viewParts.push(this._textAreaHandler);
+ this._textAreaHandler = new TextAreaHandler(this._context, viewController, this._createTextAreaHandlerHelper());
+ this._viewParts.push(this._textAreaHandler);
// These two dom nodes must be constructed up front, since references are needed in the layout provider (scrolling & co.)
- this.linesContent = createFastDomNode(document.createElement('div'));
- this.linesContent.setClassName('lines-content' + ' monaco-editor-background');
- this.linesContent.setPosition('absolute');
+ this._linesContent = createFastDomNode(document.createElement('div'));
+ this._linesContent.setClassName('lines-content' + ' monaco-editor-background');
+ this._linesContent.setPosition('absolute');
this.domNode = createFastDomNode(document.createElement('div'));
- this.domNode.setClassName(this.getEditorClassName());
+ this.domNode.setClassName(this._getEditorClassName());
// Set role 'code' for better screen reader support https://github.com/microsoft/vscode/issues/93438
this.domNode.setAttribute('role', 'code');
- this.overflowGuardContainer = createFastDomNode(document.createElement('div'));
- PartFingerprints.write(this.overflowGuardContainer, PartFingerprint.OverflowGuard);
- this.overflowGuardContainer.setClassName('overflow-guard');
+ this._overflowGuardContainer = createFastDomNode(document.createElement('div'));
+ PartFingerprints.write(this._overflowGuardContainer, PartFingerprint.OverflowGuard);
+ this._overflowGuardContainer.setClassName('overflow-guard');
- this._scrollbar = new EditorScrollbar(this._context, this.linesContent, this.domNode, this.overflowGuardContainer);
- this.viewParts.push(this._scrollbar);
+ this._scrollbar = new EditorScrollbar(this._context, this._linesContent, this.domNode, this._overflowGuardContainer);
+ this._viewParts.push(this._scrollbar);
// View Lines
- this.viewLines = new ViewLines(this._context, this.linesContent);
+ this._viewLines = new ViewLines(this._context, this._linesContent);
// View Zones
- this.viewZones = new ViewZones(this._context);
- this.viewParts.push(this.viewZones);
+ this._viewZones = new ViewZones(this._context);
+ this._viewParts.push(this._viewZones);
// Decorations overview ruler
const decorationsOverviewRuler = new DecorationsOverviewRuler(this._context);
- this.viewParts.push(decorationsOverviewRuler);
+ this._viewParts.push(decorationsOverviewRuler);
const scrollDecoration = new ScrollDecorationViewPart(this._context);
- this.viewParts.push(scrollDecoration);
+ this._viewParts.push(scrollDecoration);
const contentViewOverlays = new ContentViewOverlays(this._context);
- this.viewParts.push(contentViewOverlays);
+ this._viewParts.push(contentViewOverlays);
contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context));
contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context));
contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context));
contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context));
const marginViewOverlays = new MarginViewOverlays(this._context);
- this.viewParts.push(marginViewOverlays);
+ this._viewParts.push(marginViewOverlays);
marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context));
marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context));
marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context));
@@ -176,26 +167,26 @@ export class View extends ViewEventHandler {
marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context));
const margin = new Margin(this._context);
- margin.getDomNode().appendChild(this.viewZones.marginDomNode);
+ margin.getDomNode().appendChild(this._viewZones.marginDomNode);
margin.getDomNode().appendChild(marginViewOverlays.getDomNode());
- this.viewParts.push(margin);
+ this._viewParts.push(margin);
// Content widgets
- this.contentWidgets = new ViewContentWidgets(this._context, this.domNode);
- this.viewParts.push(this.contentWidgets);
+ this._contentWidgets = new ViewContentWidgets(this._context, this.domNode);
+ this._viewParts.push(this._contentWidgets);
- this.viewCursors = new ViewCursors(this._context);
- this.viewParts.push(this.viewCursors);
+ this._viewCursors = new ViewCursors(this._context);
+ this._viewParts.push(this._viewCursors);
// Overlay widgets
- this.overlayWidgets = new ViewOverlayWidgets(this._context);
- this.viewParts.push(this.overlayWidgets);
+ this._overlayWidgets = new ViewOverlayWidgets(this._context);
+ this._viewParts.push(this._overlayWidgets);
const rulers = new Rulers(this._context);
- this.viewParts.push(rulers);
+ this._viewParts.push(rulers);
const minimap = new Minimap(this._context);
- this.viewParts.push(minimap);
+ this._viewParts.push(minimap);
// -------------- Wire dom nodes up
@@ -204,78 +195,74 @@ export class View extends ViewEventHandler {
overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore);
}
- this.linesContent.appendChild(contentViewOverlays.getDomNode());
- this.linesContent.appendChild(rulers.domNode);
- this.linesContent.appendChild(this.viewZones.domNode);
- this.linesContent.appendChild(this.viewLines.getDomNode());
- this.linesContent.appendChild(this.contentWidgets.domNode);
- this.linesContent.appendChild(this.viewCursors.getDomNode());
- this.overflowGuardContainer.appendChild(margin.getDomNode());
- this.overflowGuardContainer.appendChild(this._scrollbar.getDomNode());
- this.overflowGuardContainer.appendChild(scrollDecoration.getDomNode());
- this.overflowGuardContainer.appendChild(this._textAreaHandler.textArea);
- this.overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover);
- this.overflowGuardContainer.appendChild(this.overlayWidgets.getDomNode());
- this.overflowGuardContainer.appendChild(minimap.getDomNode());
- this.domNode.appendChild(this.overflowGuardContainer);
- this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode);
+ this._linesContent.appendChild(contentViewOverlays.getDomNode());
+ this._linesContent.appendChild(rulers.domNode);
+ this._linesContent.appendChild(this._viewZones.domNode);
+ this._linesContent.appendChild(this._viewLines.getDomNode());
+ this._linesContent.appendChild(this._contentWidgets.domNode);
+ this._linesContent.appendChild(this._viewCursors.getDomNode());
+ this._overflowGuardContainer.appendChild(margin.getDomNode());
+ this._overflowGuardContainer.appendChild(this._scrollbar.getDomNode());
+ this._overflowGuardContainer.appendChild(scrollDecoration.getDomNode());
+ this._overflowGuardContainer.appendChild(this._textAreaHandler.textArea);
+ this._overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover);
+ this._overflowGuardContainer.appendChild(this._overlayWidgets.getDomNode());
+ this._overflowGuardContainer.appendChild(minimap.getDomNode());
+ this.domNode.appendChild(this._overflowGuardContainer);
+ this.domNode.appendChild(this._contentWidgets.overflowingContentWidgetsDomNode);
this._applyLayout();
// Pointer handler
- this.pointerHandler = this._register(new PointerHandler(this._context, viewController, this.createPointerHandlerHelper()));
-
- this._register(model.addViewEventListener((events: viewEvents.ViewEvent[]) => {
- this.eventDispatcher.emitMany(events);
- }));
+ this._pointerHandler = this._register(new PointerHandler(this._context, viewController, this._createPointerHandlerHelper()));
}
private _flushAccumulatedAndRenderNow(): void {
this._renderNow();
}
- private createPointerHandlerHelper(): IPointerHandlerHelper {
+ private _createPointerHandlerHelper(): IPointerHandlerHelper {
return {
viewDomNode: this.domNode.domNode,
- linesContentDomNode: this.linesContent.domNode,
+ linesContentDomNode: this._linesContent.domNode,
focusTextArea: () => {
this.focus();
},
getLastRenderData: (): PointerHandlerLastRenderData => {
- const lastViewCursorsRenderData = this.viewCursors.getLastRenderData() || [];
+ const lastViewCursorsRenderData = this._viewCursors.getLastRenderData() || [];
const lastTextareaPosition = this._textAreaHandler.getLastRenderData();
return new PointerHandlerLastRenderData(lastViewCursorsRenderData, lastTextareaPosition);
},
shouldSuppressMouseDownOnViewZone: (viewZoneId: string) => {
- return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);
+ return this._viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);
},
shouldSuppressMouseDownOnWidget: (widgetId: string) => {
- return this.contentWidgets.shouldSuppressMouseDownOnWidget(widgetId);
+ return this._contentWidgets.shouldSuppressMouseDownOnWidget(widgetId);
},
getPositionFromDOMInfo: (spanNode: HTMLElement, offset: number) => {
this._flushAccumulatedAndRenderNow();
- return this.viewLines.getPositionFromDOMInfo(spanNode, offset);
+ return this._viewLines.getPositionFromDOMInfo(spanNode, offset);
},
visibleRangeForPosition: (lineNumber: number, column: number) => {
this._flushAccumulatedAndRenderNow();
- return this.viewLines.visibleRangeForPosition(new Position(lineNumber, column));
+ return this._viewLines.visibleRangeForPosition(new Position(lineNumber, column));
},
getLineWidth: (lineNumber: number) => {
this._flushAccumulatedAndRenderNow();
- return this.viewLines.getLineWidth(lineNumber);
+ return this._viewLines.getLineWidth(lineNumber);
}
};
}
- private createTextAreaHandlerHelper(): ITextAreaHandlerHelper {
+ private _createTextAreaHandlerHelper(): ITextAreaHandlerHelper {
return {
visibleRangeForPositionRelativeToEditor: (lineNumber: number, column: number) => {
this._flushAccumulatedAndRenderNow();
- return this.viewLines.visibleRangeForPosition(new Position(lineNumber, column));
+ return this._viewLines.visibleRangeForPosition(new Position(lineNumber, column));
}
};
}
@@ -287,27 +274,26 @@ export class View extends ViewEventHandler {
this.domNode.setWidth(layoutInfo.width);
this.domNode.setHeight(layoutInfo.height);
- this.overflowGuardContainer.setWidth(layoutInfo.width);
- this.overflowGuardContainer.setHeight(layoutInfo.height);
+ this._overflowGuardContainer.setWidth(layoutInfo.width);
+ this._overflowGuardContainer.setHeight(layoutInfo.height);
- this.linesContent.setWidth(1000000);
- this.linesContent.setHeight(1000000);
+ this._linesContent.setWidth(1000000);
+ this._linesContent.setHeight(1000000);
}
- private getEditorClassName() {
+ private _getEditorClassName() {
const focused = this._textAreaHandler.isFocused() ? ' focused' : '';
return this._context.configuration.options.get(EditorOption.editorClassName) + ' ' + getThemeTypeSelector(this._context.theme.type) + focused;
}
// --- begin event handlers
-
- public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
- this.domNode.setClassName(this.getEditorClassName());
- this._applyLayout();
- return false;
+ public handleEvents(events: viewEvents.ViewEvent[]): void {
+ super.handleEvents(events);
+ this._scheduleRender();
}
- public onContentSizeChanged(e: viewEvents.ViewContentSizeChangedEvent): boolean {
- this.outgoingEvents.emitContentSizeChange(e);
+ public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
+ this.domNode.setClassName(this._getEditorClassName());
+ this._applyLayout();
return false;
}
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
@@ -315,21 +301,11 @@ export class View extends ViewEventHandler {
return false;
}
public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean {
- this.domNode.setClassName(this.getEditorClassName());
- this._context.model.setHasFocus(e.isFocused);
- if (e.isFocused) {
- this.outgoingEvents.emitViewFocusGained();
- } else {
- this.outgoingEvents.emitViewFocusLost();
- }
- return false;
- }
- public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {
- this.outgoingEvents.emitScrollChanged(e);
+ this.domNode.setClassName(this._getEditorClassName());
return false;
}
public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean {
- this.domNode.setClassName(this.getEditorClassName());
+ this.domNode.setClassName(this._getEditorClassName());
return false;
}
@@ -341,26 +317,18 @@ export class View extends ViewEventHandler {
this._renderAnimationFrame = null;
}
- this.eventDispatcher.removeEventHandler(this);
- this.outgoingEvents.dispose();
+ this._context.removeEventHandler(this);
- this.viewLines.dispose();
+ this._viewLines.dispose();
// Destroy view parts
- for (let i = 0, len = this.viewParts.length; i < len; i++) {
- this.viewParts[i].dispose();
+ for (let i = 0, len = this._viewParts.length; i < len; i++) {
+ this._viewParts[i].dispose();
}
- this.viewParts = [];
super.dispose();
}
- private _renderOnce(callback: () => T): T {
- const r = safeInvokeNoArg(callback);
- this._scheduleRender();
- return r;
- }
-
private _scheduleRender(): void {
if (this._renderAnimationFrame === null) {
this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100);
@@ -378,8 +346,8 @@ export class View extends ViewEventHandler {
private _getViewPartsToRender(): ViewPart[] {
let result: ViewPart[] = [], resultLen = 0;
- for (let i = 0, len = this.viewParts.length; i < len; i++) {
- const viewPart = this.viewParts[i];
+ for (let i = 0, len = this._viewParts.length; i < len; i++) {
+ const viewPart = this._viewParts[i];
if (viewPart.shouldRender()) {
result[resultLen++] = viewPart;
}
@@ -394,7 +362,7 @@ export class View extends ViewEventHandler {
let viewPartsToRender = this._getViewPartsToRender();
- if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) {
+ if (!this._viewLines.shouldRender() && viewPartsToRender.length === 0) {
// Nothing to render
return;
}
@@ -409,20 +377,20 @@ export class View extends ViewEventHandler {
this._context.model
);
- if (this.contentWidgets.shouldRender()) {
+ if (this._contentWidgets.shouldRender()) {
// Give the content widgets a chance to set their max width before a possible synchronous layout
- this.contentWidgets.onBeforeRender(viewportData);
+ this._contentWidgets.onBeforeRender(viewportData);
}
- if (this.viewLines.shouldRender()) {
- this.viewLines.renderText(viewportData);
- this.viewLines.onDidRender();
+ if (this._viewLines.shouldRender()) {
+ this._viewLines.renderText(viewportData);
+ this._viewLines.onDidRender();
// Rendering of viewLines might cause scroll events to occur, so collect view parts to render again
viewPartsToRender = this._getViewPartsToRender();
}
- const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this.viewLines);
+ const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this._viewLines);
// Render the rest of the parts
for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
@@ -444,11 +412,11 @@ export class View extends ViewEventHandler {
}
public restoreState(scrollPosition: { scrollLeft: number; scrollTop: number; }): void {
- this._context.viewLayout.setScrollPosition({ scrollTop: scrollPosition.scrollTop }, ScrollType.Immediate);
+ this._context.model.setScrollPosition({ scrollTop: scrollPosition.scrollTop }, ScrollType.Immediate);
this._context.model.tokenizeViewport();
this._renderNow();
- this.viewLines.updateLineWidths();
- this._context.viewLayout.setScrollPosition({ scrollLeft: scrollPosition.scrollLeft }, ScrollType.Immediate);
+ this._viewLines.updateLineWidths();
+ this._context.model.setScrollPosition({ scrollLeft: scrollPosition.scrollLeft }, ScrollType.Immediate);
}
public getOffsetForColumn(modelLineNumber: number, modelColumn: number): number {
@@ -458,7 +426,7 @@ export class View extends ViewEventHandler {
});
const viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
this._flushAccumulatedAndRenderNow();
- const visibleRange = this.viewLines.visibleRangeForPosition(new Position(viewPosition.lineNumber, viewPosition.column));
+ const visibleRange = this._viewLines.visibleRangeForPosition(new Position(viewPosition.lineNumber, viewPosition.column));
if (!visibleRange) {
return -1;
}
@@ -466,34 +434,28 @@ export class View extends ViewEventHandler {
}
public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null {
- const mouseTarget = this.pointerHandler.getTargetAtClientPoint(clientX, clientY);
+ const mouseTarget = this._pointerHandler.getTargetAtClientPoint(clientX, clientY);
if (!mouseTarget) {
return null;
}
- return ViewOutgoingEvents.convertViewToModelMouseTarget(mouseTarget, this._context.model.coordinatesConverter);
+ return ViewUserInputEvents.convertViewToModelMouseTarget(mouseTarget, this._context.model.coordinatesConverter);
}
public createOverviewRuler(cssClassName: string): OverviewRuler {
return new OverviewRuler(this._context, cssClassName);
}
- public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean {
- return this._renderOnce(() => {
- const zonesHaveChanged = this.viewZones.changeViewZones(callback);
- if (zonesHaveChanged) {
- this._context.viewLayout.onHeightMaybeChanged();
- this._context.privateViewEventBus.emit(new viewEvents.ViewZonesChangedEvent());
- }
- return zonesHaveChanged;
- });
+ public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): void {
+ this._viewZones.changeViewZones(callback);
+ this._scheduleRender();
}
public render(now: boolean, everything: boolean): void {
if (everything) {
// Force everything to render...
- this.viewLines.forceShouldRender();
- for (let i = 0, len = this.viewParts.length; i < len; i++) {
- const viewPart = this.viewParts[i];
+ this._viewLines.forceShouldRender();
+ for (let i = 0, len = this._viewParts.length; i < len; i++) {
+ const viewPart = this._viewParts[i];
viewPart.forceShouldRender();
}
}
@@ -521,7 +483,7 @@ export class View extends ViewEventHandler {
}
public addContentWidget(widgetData: IContentWidgetData): void {
- this.contentWidgets.addWidget(widgetData.widget);
+ this._contentWidgets.addWidget(widgetData.widget);
this.layoutContentWidget(widgetData);
this._scheduleRender();
}
@@ -535,31 +497,31 @@ export class View extends ViewEventHandler {
}
}
const newPreference = widgetData.position ? widgetData.position.preference : null;
- this.contentWidgets.setWidgetPosition(widgetData.widget, newRange, newPreference);
+ this._contentWidgets.setWidgetPosition(widgetData.widget, newRange, newPreference);
this._scheduleRender();
}
public removeContentWidget(widgetData: IContentWidgetData): void {
- this.contentWidgets.removeWidget(widgetData.widget);
+ this._contentWidgets.removeWidget(widgetData.widget);
this._scheduleRender();
}
public addOverlayWidget(widgetData: IOverlayWidgetData): void {
- this.overlayWidgets.addWidget(widgetData.widget);
+ this._overlayWidgets.addWidget(widgetData.widget);
this.layoutOverlayWidget(widgetData);
this._scheduleRender();
}
public layoutOverlayWidget(widgetData: IOverlayWidgetData): void {
const newPreference = widgetData.position ? widgetData.position.preference : null;
- const shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference);
+ const shouldRender = this._overlayWidgets.setWidgetPosition(widgetData.widget, newPreference);
if (shouldRender) {
this._scheduleRender();
}
}
public removeOverlayWidget(widgetData: IOverlayWidgetData): void {
- this.overlayWidgets.removeWidget(widgetData.widget);
+ this._overlayWidgets.removeWidget(widgetData.widget);
this._scheduleRender();
}
diff --git a/src/vs/editor/browser/view/viewOutgoingEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts
similarity index 76%
rename from src/vs/editor/browser/view/viewOutgoingEvents.ts
rename to src/vs/editor/browser/view/viewUserInputEvents.ts
index 6ba6ff3f123..22906bda60e 100644
--- a/src/vs/editor/browser/view/viewOutgoingEvents.ts
+++ b/src/vs/editor/browser/view/viewUserInputEvents.ts
@@ -4,66 +4,34 @@
*--------------------------------------------------------------------------------------------*/
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
-import { Disposable } from 'vs/base/common/lifecycle';
import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget';
import { IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
-import { IScrollEvent, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
-import * as viewEvents from 'vs/editor/common/view/viewEvents';
-import { IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
+import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
export interface EventCallback {
(event: T): void;
}
-export class ViewOutgoingEvents extends Disposable {
+export class ViewUserInputEvents {
- public onDidContentSizeChange: EventCallback | null = null;
- public onDidScroll: EventCallback | null = null;
- public onDidGainFocus: EventCallback | null = null;
- public onDidLoseFocus: EventCallback | null = null;
public onKeyDown: EventCallback | null = null;
public onKeyUp: EventCallback | null = null;
public onContextMenu: EventCallback | null = null;
public onMouseMove: EventCallback | null = null;
public onMouseLeave: EventCallback | null = null;
- public onMouseUp: EventCallback | null = null;
public onMouseDown: EventCallback | null = null;
+ public onMouseUp: EventCallback | null = null;
public onMouseDrag: EventCallback | null = null;
public onMouseDrop: EventCallback | null = null;
public onMouseWheel: EventCallback | null = null;
- private readonly _viewModel: IViewModel;
+ private readonly _coordinatesConverter: ICoordinatesConverter;
- constructor(viewModel: IViewModel) {
- super();
- this._viewModel = viewModel;
- }
-
- public emitContentSizeChange(e: viewEvents.ViewContentSizeChangedEvent): void {
- if (this.onDidContentSizeChange) {
- this.onDidContentSizeChange(e);
- }
- }
-
- public emitScrollChanged(e: viewEvents.ViewScrollChangedEvent): void {
- if (this.onDidScroll) {
- this.onDidScroll(e);
- }
- }
-
- public emitViewFocusGained(): void {
- if (this.onDidGainFocus) {
- this.onDidGainFocus(undefined);
- }
- }
-
- public emitViewFocusLost(): void {
- if (this.onDidLoseFocus) {
- this.onDidLoseFocus(undefined);
- }
+ constructor(coordinatesConverter: ICoordinatesConverter) {
+ this._coordinatesConverter = coordinatesConverter;
}
public emitKeyDown(e: IKeyboardEvent): void {
@@ -96,18 +64,18 @@ export class ViewOutgoingEvents extends Disposable {
}
}
- public emitMouseUp(e: IEditorMouseEvent): void {
- if (this.onMouseUp) {
- this.onMouseUp(this._convertViewToModelMouseEvent(e));
- }
- }
-
public emitMouseDown(e: IEditorMouseEvent): void {
if (this.onMouseDown) {
this.onMouseDown(this._convertViewToModelMouseEvent(e));
}
}
+ public emitMouseUp(e: IEditorMouseEvent): void {
+ if (this.onMouseUp) {
+ this.onMouseUp(this._convertViewToModelMouseEvent(e));
+ }
+ }
+
public emitMouseDrag(e: IEditorMouseEvent): void {
if (this.onMouseDrag) {
this.onMouseDrag(this._convertViewToModelMouseEvent(e));
@@ -139,7 +107,7 @@ export class ViewOutgoingEvents extends Disposable {
}
private _convertViewToModelMouseTarget(target: IMouseTarget): IMouseTarget {
- return ViewOutgoingEvents.convertViewToModelMouseTarget(target, this._viewModel.coordinatesConverter);
+ return ViewUserInputEvents.convertViewToModelMouseTarget(target, this._coordinatesConverter);
}
public static convertViewToModelMouseTarget(target: IMouseTarget, coordinatesConverter: ICoordinatesConverter): IMouseTarget {
diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts
index 419287e19c1..21193bac130 100644
--- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts
+++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts
@@ -88,7 +88,7 @@ export class EditorScrollbar extends ViewPart {
}
}
- this._context.viewLayout.setScrollPosition(newScrollPosition, ScrollType.Immediate);
+ this._context.model.setScrollPosition(newScrollPosition, ScrollType.Immediate);
};
// I've seen this happen both on the view dom node & on the lines content dom node.
diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts
index 33979994f9d..f94c06247b4 100644
--- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts
+++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts
@@ -112,7 +112,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay {
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
- const { indentSize } = this._context.model.getOptions();
+ const { indentSize } = this._context.model.getTextModelOptions();
const indentWidth = indentSize * this._spaceWidth;
const scrollWidth = ctx.scrollWidth;
const lineHeight = this._lineHeight;
diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts
index 7ee3267d063..6e92c558e11 100644
--- a/src/vs/editor/browser/viewParts/lines/viewLines.ts
+++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts
@@ -282,7 +282,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost,
const scrollTopDelta = Math.abs(this._context.viewLayout.getCurrentScrollTop() - newScrollPosition.scrollTop);
const scrollType = (scrollTopDelta <= this._lineHeight ? ScrollType.Immediate : e.scrollType);
- this._context.viewLayout.setScrollPosition(newScrollPosition, scrollType);
+ this._context.model.setScrollPosition(newScrollPosition, scrollType);
return true;
}
@@ -307,7 +307,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost,
return this._visibleLines.onTokensChanged(e);
}
public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean {
- this._context.viewLayout.onMaxLineWidthChanged(this._maxLineWidth);
+ this._context.model.setMaxLineWidth(this._maxLineWidth);
return this._visibleLines.onZonesChanged(e);
}
public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean {
@@ -587,7 +587,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost,
this._ensureMaxLineWidth(newScrollLeft.maxHorizontalOffset);
}
// set `scrollLeft`
- this._context.viewLayout.setScrollPosition({
+ this._context.model.setScrollPosition({
scrollLeft: newScrollLeft.scrollLeft
}, horizontalRevealRequest.scrollType);
}
@@ -626,7 +626,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost,
const iLineWidth = Math.ceil(lineWidth);
if (this._maxLineWidth < iLineWidth) {
this._maxLineWidth = iLineWidth;
- this._context.viewLayout.onMaxLineWidthChanged(this._maxLineWidth);
+ this._context.model.setMaxLineWidth(this._maxLineWidth);
}
}
diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts
index 72961f79a18..1be53aad7a3 100644
--- a/src/vs/editor/browser/viewParts/minimap/minimap.ts
+++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts
@@ -1004,25 +1004,24 @@ export class Minimap extends ViewPart implements IMinimapModel {
}
public getOptions(): TextModelResolvedOptions {
- return this._context.model.getOptions();
+ return this._context.model.getTextModelOptions();
}
public revealLineNumber(lineNumber: number): void {
if (this._samplingState) {
lineNumber = this._samplingState.minimapLines[lineNumber - 1];
}
- this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent(
+ this._context.model.revealRange(
'mouse',
- new Range(lineNumber, 1, lineNumber, 1),
- null,
- viewEvents.VerticalRevealType.Center,
false,
+ new Range(lineNumber, 1, lineNumber, 1),
+ viewEvents.VerticalRevealType.Center,
ScrollType.Smooth
- ));
+ );
}
public setScrollTop(scrollTop: number): void {
- this._context.viewLayout.setScrollPosition({
+ this._context.model.setScrollPosition({
scrollTop: scrollTop
}, ScrollType.Immediate);
}
diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.ts b/src/vs/editor/browser/viewParts/rulers/rulers.ts
index 68621081a8e..696e088de58 100644
--- a/src/vs/editor/browser/viewParts/rulers/rulers.ts
+++ b/src/vs/editor/browser/viewParts/rulers/rulers.ts
@@ -64,7 +64,7 @@ export class Rulers extends ViewPart {
}
if (currentCount < desiredCount) {
- const { tabSize } = this._context.model.getOptions();
+ const { tabSize } = this._context.model.getTextModelOptions();
const rulerWidth = tabSize;
let addCount = desiredCount - currentCount;
while (addCount > 0) {
diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts
index 5e28b7785ec..a73026a3f9b 100644
--- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts
+++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts
@@ -79,9 +79,8 @@ export class ViewZones extends ViewPart {
for (const whitespace of whitespaces) {
oldWhitespaces.set(whitespace.id, whitespace);
}
- return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => {
- let hadAChange = false;
-
+ let hadAChange = false;
+ this._context.model.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => {
const keys = Object.keys(this._zones);
for (let i = 0, len = keys.length; i < len; i++) {
const id = keys[i];
@@ -94,9 +93,8 @@ export class ViewZones extends ViewPart {
hadAChange = true;
}
}
-
- return hadAChange;
});
+ return hadAChange;
}
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
@@ -115,11 +113,7 @@ export class ViewZones extends ViewPart {
}
public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean {
- const hadAChange = this._recomputeWhitespacesProps();
- if (hadAChange) {
- this._context.viewLayout.onHeightMaybeChanged();
- }
- return hadAChange;
+ return this._recomputeWhitespacesProps();
}
public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean {
@@ -199,9 +193,9 @@ export class ViewZones extends ViewPart {
}
public changeViewZones(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean {
+ let zonesHaveChanged = false;
- return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => {
- let zonesHaveChanged = false;
+ this._context.model.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => {
const changeAccessor: IViewZoneChangeAccessor = {
addZone: (zone: IViewZone): string => {
@@ -228,9 +222,9 @@ export class ViewZones extends ViewPart {
changeAccessor.addZone = invalidFunc;
changeAccessor.removeZone = invalidFunc;
changeAccessor.layoutZone = invalidFunc;
-
- return zonesHaveChanged;
});
+
+ return zonesHaveChanged;
}
private _addZone(whitespaceAccessor: IWhitespaceChangeAccessor, zone: IViewZone): string {
diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts
index adf1ef5a746..f57ab7693d6 100644
--- a/src/vs/editor/browser/widget/codeEditorWidget.ts
+++ b/src/vs/editor/browser/widget/codeEditorWidget.ts
@@ -21,10 +21,10 @@ import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/edi
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICommandDelegate } from 'vs/editor/browser/view/viewController';
import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl';
-import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
+import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
-import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor';
-import { CursorColumns, ICursors } from 'vs/editor/common/controller/cursorCommon';
+import { Cursor } from 'vs/editor/common/controller/cursor';
+import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
@@ -54,6 +54,7 @@ import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/m
import { DOMLineBreaksComputerFactory } from 'vs/editor/browser/view/domLineBreaksComputer';
import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
+import { OutgoingViewModelEventKind } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
let EDITOR_ID = 0;
@@ -527,7 +528,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (!this._modelData) {
return null;
}
- return this._modelData.viewModel.cursor.getPosition();
+ return this._modelData.viewModel.getPosition();
}
public setPosition(position: IPosition): void {
@@ -537,7 +538,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (!Position.isIPosition(position)) {
throw new Error('Invalid arguments');
}
- this._modelData.viewModel.cursor.setSelections('api', [{
+ this._modelData.viewModel.setSelections('api', [{
selectionStartLineNumber: position.lineNumber,
selectionStartColumn: position.column,
positionLineNumber: position.lineNumber,
@@ -555,7 +556,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
const validatedModelRange = this._modelData.model.validateRange(modelRange);
const viewRange = this._modelData.viewModel.coordinatesConverter.convertModelRangeToViewRange(validatedModelRange);
- this._modelData.viewModel.cursor.emitCursorRevealRange('api', viewRange, null, verticalType, revealHorizontal, scrollType);
+ this._modelData.viewModel.revealRange('api', revealHorizontal, viewRange, verticalType, scrollType);
}
public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
@@ -640,14 +641,14 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (!this._modelData) {
return null;
}
- return this._modelData.viewModel.cursor.getSelection();
+ return this._modelData.viewModel.getSelection();
}
public getSelections(): Selection[] | null {
if (!this._modelData) {
return null;
}
- return this._modelData.viewModel.cursor.getSelections();
+ return this._modelData.viewModel.getSelections();
}
public setSelection(range: IRange): void;
@@ -681,7 +682,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return;
}
const selection = new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn);
- this._modelData.viewModel.cursor.setSelections('api', [selection]);
+ this._modelData.viewModel.setSelections('api', [selection]);
}
public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void {
@@ -812,7 +813,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
throw new Error('Invalid arguments');
}
}
- this._modelData.viewModel.cursor.setSelections(source, ranges);
+ this._modelData.viewModel.setSelections(source, ranges);
}
public getContentWidth(): number {
@@ -862,7 +863,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (typeof newScrollLeft !== 'number') {
throw new Error('Invalid arguments');
}
- this._modelData.viewModel.viewLayout.setScrollPosition({
+ this._modelData.viewModel.setScrollPosition({
scrollLeft: newScrollLeft
}, scrollType);
}
@@ -873,7 +874,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (typeof newScrollTop !== 'number') {
throw new Error('Invalid arguments');
}
- this._modelData.viewModel.viewLayout.setScrollPosition({
+ this._modelData.viewModel.setScrollPosition({
scrollTop: newScrollTop
}, scrollType);
}
@@ -881,7 +882,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (!this._modelData) {
return;
}
- this._modelData.viewModel.viewLayout.setScrollPosition(position, scrollType);
+ this._modelData.viewModel.setScrollPosition(position, scrollType);
}
public saveViewState(): editorCommon.ICodeEditorViewState | null {
@@ -898,7 +899,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
}
}
- const cursorState = this._modelData.viewModel.cursor.saveState();
+ const cursorState = this._modelData.viewModel.saveCursorState();
const viewState = this._modelData.viewModel.saveState();
return {
cursorState: cursorState,
@@ -915,10 +916,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (codeEditorState && codeEditorState.cursorState && codeEditorState.viewState) {
const cursorState = codeEditorState.cursorState;
if (Array.isArray(cursorState)) {
- this._modelData.viewModel.cursor.restoreState(cursorState);
+ this._modelData.viewModel.restoreCursorState(cursorState);
} else {
// Backwards compatibility
- this._modelData.viewModel.cursor.restoreState([cursorState]);
+ this._modelData.viewModel.restoreCursorState([cursorState]);
}
const contributionsState = codeEditorState.contributionsState || {};
@@ -976,40 +977,31 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
public trigger(source: string | null | undefined, handlerId: string, payload: any): void {
payload = payload || {};
- // Special case for typing
- if (handlerId === editorCommon.Handler.Type) {
- if (!this._modelData || typeof payload.text !== 'string' || payload.text.length === 0) {
- // nothing to do
+ switch (handlerId) {
+ case editorCommon.Handler.CompositionStart:
+ this._startComposition();
+ return;
+ case editorCommon.Handler.CompositionEnd:
+ this._endComposition(source);
+ return;
+ case editorCommon.Handler.Type: {
+ const args = >payload;
+ this._type(source, args.text || '');
return;
}
- if (source === 'keyboard') {
- this._onWillType.fire(payload.text);
- }
- this._modelData.viewModel.cursor.trigger(source, handlerId, payload);
- if (source === 'keyboard') {
- this._onDidType.fire(payload.text);
- }
- return;
- }
-
- // Special case for pasting
- if (handlerId === editorCommon.Handler.Paste) {
- if (!this._modelData || typeof payload.text !== 'string' || payload.text.length === 0) {
- // nothing to do
+ case editorCommon.Handler.ReplacePreviousChar: {
+ const args = >payload;
+ this._replacePreviousChar(source, args.text || '', args.replaceCharCnt || 0);
return;
}
- const startPosition = this._modelData.viewModel.cursor.getSelection().getStartPosition();
- this._modelData.viewModel.cursor.trigger(source, handlerId, payload);
- const endPosition = this._modelData.viewModel.cursor.getSelection().getStartPosition();
- if (source === 'keyboard') {
- this._onDidPaste.fire(
- {
- range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column),
- mode: payload.mode
- }
- );
+ case editorCommon.Handler.Paste: {
+ const args = >payload;
+ this._paste(source, args.text || '', args.pasteOnNewLine || false, args.multicursorText || null, args.mode || null);
+ return;
}
- return;
+ case editorCommon.Handler.Cut:
+ this._cut(source);
+ return;
}
const action = this.getAction(handlerId);
@@ -1025,15 +1017,64 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (this._triggerEditorCommand(source, handlerId, payload)) {
return;
}
+ }
- this._modelData.viewModel.cursor.trigger(source, handlerId, payload);
+ private _startComposition(): void {
+ if (!this._modelData) {
+ return;
+ }
+ this._modelData.viewModel.startComposition();
+ this._onDidCompositionStart.fire();
+ }
- if (handlerId === editorCommon.Handler.CompositionStart) {
- this._onDidCompositionStart.fire();
+ private _endComposition(source: string | null | undefined): void {
+ if (!this._modelData) {
+ return;
}
- if (handlerId === editorCommon.Handler.CompositionEnd) {
- this._onDidCompositionEnd.fire();
+ this._modelData.viewModel.endComposition(source);
+ this._onDidCompositionEnd.fire();
+ }
+
+ private _type(source: string | null | undefined, text: string): void {
+ if (!this._modelData || text.length === 0) {
+ return;
}
+ if (source === 'keyboard') {
+ this._onWillType.fire(text);
+ }
+ this._modelData.viewModel.type(text, source);
+ if (source === 'keyboard') {
+ this._onDidType.fire(text);
+ }
+ }
+
+ private _replacePreviousChar(source: string | null | undefined, text: string, replaceCharCnt: number): void {
+ if (!this._modelData) {
+ return;
+ }
+ this._modelData.viewModel.replacePreviousChar(text, replaceCharCnt, source);
+ }
+
+ private _paste(source: string | null | undefined, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void {
+ if (!this._modelData || text.length === 0) {
+ return;
+ }
+ const startPosition = this._modelData.viewModel.getSelection().getStartPosition();
+ this._modelData.viewModel.paste(text, pasteOnNewLine, multicursorText, source);
+ const endPosition = this._modelData.viewModel.getSelection().getStartPosition();
+ if (source === 'keyboard') {
+ this._onDidPaste.fire({
+ range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column),
+ mode: mode
+ });
+ }
+ }
+
+ private _cut(source: string | null | undefined): void {
+ if (!this._modelData) {
+ return;
+ }
+ this._modelData.viewModel.cut(source);
}
private _triggerEditorCommand(source: string | null | undefined, handlerId: string, payload: any): boolean {
@@ -1050,13 +1091,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return false;
}
- public _getCursors(): ICursors | null {
- if (!this._modelData) {
- return null;
- }
- return this._modelData.viewModel.cursor;
- }
-
public _getViewModel(): IViewModel | null {
if (!this._modelData) {
return null;
@@ -1094,7 +1128,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
cursorStateComputer = endCursorState;
}
- this._modelData.viewModel.cursor.executeEdits(source, edits, cursorStateComputer);
+ this._modelData.viewModel.executeEdits(source, edits, cursorStateComputer);
return true;
}
@@ -1102,14 +1136,14 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (!this._modelData) {
return;
}
- this._modelData.viewModel.cursor.trigger(source, editorCommon.Handler.ExecuteCommand, command);
+ this._modelData.viewModel.executeCommand(command, source);
}
public executeCommands(source: string | null | undefined, commands: editorCommon.ICommand[]): void {
if (!this._modelData) {
return;
}
- this._modelData.viewModel.cursor.trigger(source, editorCommon.Handler.ExecuteCommands, commands);
+ this._modelData.viewModel.executeCommands(commands, source);
}
public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any {
@@ -1351,10 +1385,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (!this._modelData || !this._modelData.hasRealView) {
return;
}
- const hasChanges = this._modelData.view.change(callback);
- if (hasChanges) {
- this._onDidChangeViewZones.fire();
- }
+ this._modelData.view.change(callback);
}
public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null {
@@ -1442,38 +1473,56 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
// Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model
listenersToRemove.push(model.onWillDispose(() => this.setModel(null)));
- listenersToRemove.push(viewModel.cursor.onDidReachMaxCursorCount(() => {
- this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT));
- }));
+ listenersToRemove.push(viewModel.onEvent((e) => {
+ switch (e.kind) {
+ case OutgoingViewModelEventKind.ContentSizeChanged:
+ this._onDidContentSizeChange.fire(e);
+ break;
+ case OutgoingViewModelEventKind.FocusChanged:
+ this._editorTextFocus.setValue(e.hasFocus);
+ break;
+ case OutgoingViewModelEventKind.ScrollChanged:
+ this._onDidScrollChange.fire(e);
+ break;
+ case OutgoingViewModelEventKind.ViewZonesChanged:
+ this._onDidChangeViewZones.fire();
+ break;
+ case OutgoingViewModelEventKind.ReadOnlyEditAttempt:
+ this._onDidAttemptReadOnlyEdit.fire();
+ break;
+ case OutgoingViewModelEventKind.CursorStateChanged: {
+ if (e.reachedMaxCursorCount) {
+ this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT));
+ }
- listenersToRemove.push(viewModel.cursor.onDidAttemptReadOnlyEdit(() => {
- this._onDidAttemptReadOnlyEdit.fire(undefined);
- }));
+ const positions: Position[] = [];
+ for (let i = 0, len = e.selections.length; i < len; i++) {
+ positions[i] = e.selections[i].getPosition();
+ }
+
+ const e1: ICursorPositionChangedEvent = {
+ position: positions[0],
+ secondaryPositions: positions.slice(1),
+ reason: e.reason,
+ source: e.source
+ };
+ this._onDidChangeCursorPosition.fire(e1);
+
+ const e2: ICursorSelectionChangedEvent = {
+ selection: e.selections[0],
+ secondarySelections: e.selections.slice(1),
+ modelVersionId: e.modelVersionId,
+ oldSelections: e.oldSelections,
+ oldModelVersionId: e.oldModelVersionId,
+ source: e.source,
+ reason: e.reason
+ };
+ this._onDidChangeCursorSelection.fire(e2);
+
+ break;
+ }
- listenersToRemove.push(viewModel.cursor.onDidChange((e: CursorStateChangedEvent) => {
- const positions: Position[] = [];
- for (let i = 0, len = e.selections.length; i < len; i++) {
- positions[i] = e.selections[i].getPosition();
}
-
- const e1: ICursorPositionChangedEvent = {
- position: positions[0],
- secondaryPositions: positions.slice(1),
- reason: e.reason,
- source: e.source
- };
- this._onDidChangeCursorPosition.fire(e1);
-
- const e2: ICursorSelectionChangedEvent = {
- selection: e.selections[0],
- secondarySelections: e.selections.slice(1),
- modelVersionId: e.modelVersionId,
- oldSelections: e.oldSelections,
- oldModelVersionId: e.oldModelVersionId,
- source: e.source,
- reason: e.reason
- };
- this._onDidChangeCursorSelection.fire(e2);
}));
const [view, hasRealView] = this._createView(viewModel);
@@ -1504,55 +1553,48 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
if (this.isSimpleWidget) {
commandDelegate = {
executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => {
- editorCommand.runCoreEditorCommand(viewModel.cursor, args);
+ editorCommand.runCoreEditorCommand(viewModel, args);
},
paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => {
- this.trigger('keyboard', editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText, mode });
+ this._paste('keyboard', text, pasteOnNewLine, multicursorText, mode);
},
type: (text: string) => {
- this.trigger('keyboard', editorCommon.Handler.Type, { text });
+ this._type('keyboard', text);
},
replacePreviousChar: (text: string, replaceCharCnt: number) => {
- this.trigger('keyboard', editorCommon.Handler.ReplacePreviousChar, { text, replaceCharCnt });
+ this._replacePreviousChar('keyboard', text, replaceCharCnt);
},
- compositionStart: () => {
- this.trigger('keyboard', editorCommon.Handler.CompositionStart, undefined);
+ startComposition: () => {
+ this._startComposition();
},
- compositionEnd: () => {
- this.trigger('keyboard', editorCommon.Handler.CompositionEnd, undefined);
+ endComposition: () => {
+ this._endComposition('keyboard');
},
cut: () => {
- this.trigger('keyboard', editorCommon.Handler.Cut, undefined);
+ this._cut('keyboard');
}
};
} else {
commandDelegate = {
executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => {
- editorCommand.runCoreEditorCommand(viewModel.cursor, args);
+ editorCommand.runCoreEditorCommand(viewModel, args);
},
paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => {
- this._commandService.executeCommand(editorCommon.Handler.Paste, {
- text: text,
- pasteOnNewLine: pasteOnNewLine,
- multicursorText: multicursorText,
- mode
- });
+ const payload: editorCommon.PastePayload = { text, pasteOnNewLine, multicursorText, mode };
+ this._commandService.executeCommand(editorCommon.Handler.Paste, payload);
},
type: (text: string) => {
- this._commandService.executeCommand(editorCommon.Handler.Type, {
- text: text
- });
+ const payload: editorCommon.TypePayload = { text };
+ this._commandService.executeCommand(editorCommon.Handler.Type, payload);
},
replacePreviousChar: (text: string, replaceCharCnt: number) => {
- this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, {
- text: text,
- replaceCharCnt: replaceCharCnt
- });
+ const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt };
+ this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload);
},
- compositionStart: () => {
+ startComposition: () => {
this._commandService.executeCommand(editorCommon.Handler.CompositionStart, {});
},
- compositionEnd: () => {
+ endComposition: () => {
this._commandService.executeCommand(editorCommon.Handler.CompositionEnd, {});
},
cut: () => {
@@ -1561,35 +1603,24 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
};
}
- const onDidChangeTextFocus = (textFocus: boolean) => {
- if (this._modelData) {
- this._modelData.viewModel.cursor.setHasFocus(textFocus);
- }
- this._editorTextFocus.setValue(textFocus);
- };
-
- const viewOutgoingEvents = new ViewOutgoingEvents(viewModel);
- viewOutgoingEvents.onDidContentSizeChange = (e) => this._onDidContentSizeChange.fire(e);
- viewOutgoingEvents.onDidScroll = (e) => this._onDidScrollChange.fire(e);
- viewOutgoingEvents.onDidGainFocus = () => onDidChangeTextFocus(true);
- viewOutgoingEvents.onDidLoseFocus = () => onDidChangeTextFocus(false);
- viewOutgoingEvents.onContextMenu = (e) => this._onContextMenu.fire(e);
- viewOutgoingEvents.onMouseDown = (e) => this._onMouseDown.fire(e);
- viewOutgoingEvents.onMouseUp = (e) => this._onMouseUp.fire(e);
- viewOutgoingEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e);
- viewOutgoingEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e);
- viewOutgoingEvents.onKeyUp = (e) => this._onKeyUp.fire(e);
- viewOutgoingEvents.onMouseMove = (e) => this._onMouseMove.fire(e);
- viewOutgoingEvents.onMouseLeave = (e) => this._onMouseLeave.fire(e);
- viewOutgoingEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e);
- viewOutgoingEvents.onKeyDown = (e) => this._onKeyDown.fire(e);
+ const viewUserInputEvents = new ViewUserInputEvents(viewModel.coordinatesConverter);
+ viewUserInputEvents.onKeyDown = (e) => this._onKeyDown.fire(e);
+ viewUserInputEvents.onKeyUp = (e) => this._onKeyUp.fire(e);
+ viewUserInputEvents.onContextMenu = (e) => this._onContextMenu.fire(e);
+ viewUserInputEvents.onMouseMove = (e) => this._onMouseMove.fire(e);
+ viewUserInputEvents.onMouseLeave = (e) => this._onMouseLeave.fire(e);
+ viewUserInputEvents.onMouseDown = (e) => this._onMouseDown.fire(e);
+ viewUserInputEvents.onMouseUp = (e) => this._onMouseUp.fire(e);
+ viewUserInputEvents.onMouseDrag = (e) => this._onMouseDrag.fire(e);
+ viewUserInputEvents.onMouseDrop = (e) => this._onMouseDrop.fire(e);
+ viewUserInputEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e);
const view = new View(
commandDelegate,
this._configuration,
this._themeService,
viewModel,
- viewOutgoingEvents
+ viewUserInputEvents
);
return [view, true];
diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts
index c32c43fef19..db4e550e94f 100644
--- a/src/vs/editor/browser/widget/diffReview.ts
+++ b/src/vs/editor/browser/widget/diffReview.ts
@@ -269,6 +269,7 @@ export class DiffReview extends Disposable {
private hide(): void {
this._isVisible = false;
+ this._diffEditor.updateOptions({ readOnly: false });
this._diffEditor.focus();
this._diffEditor.doLayout();
this._render();
@@ -541,6 +542,7 @@ export class DiffReview extends Disposable {
return;
}
+ this._diffEditor.updateOptions({ readOnly: true });
const diffIndex = this._findDiffIndex(this._diffEditor.getPosition()!);
if (this._diffs[diffIndex] === this._currentDiff) {
diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts
index 609f97f351d..954af2fa4f2 100644
--- a/src/vs/editor/common/config/commonEditorConfig.ts
+++ b/src/vs/editor/common/config/commonEditorConfig.ts
@@ -283,6 +283,9 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
private _onDidChange = this._register(new Emitter());
public readonly onDidChange: Event = this._onDidChange.event;
+ private _onDidChangeFast = this._register(new Emitter());
+ public readonly onDidChangeFast: Event = this._onDidChangeFast.event;
+
public readonly isSimpleWidget: boolean;
private _computeOptionsMemory: ComputeOptionsMemory;
public options!: ComputedEditorOptions;
@@ -334,6 +337,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
}
this.options = newOptions;
+ this._onDidChangeFast.fire(changeEvent);
this._onDidChange.fire(changeEvent);
}
}
diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts
index a802a723b50..bb0e19e5d9e 100644
--- a/src/vs/editor/common/config/editorOptions.ts
+++ b/src/vs/editor/common/config/editorOptions.ts
@@ -95,9 +95,9 @@ export interface IEditorOptions {
renderFinalNewline?: boolean;
/**
* Remove unusual line terminators like LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS), NEXT LINE (NEL).
- * Defaults to true.
+ * Defaults to 'prompt'.
*/
- removeUnusualLineTerminators?: boolean;
+ unusualLineTerminators?: 'off' | 'prompt' | 'auto';
/**
* Should the corresponding line be selected when clicking on the line number?
* Defaults to true.
@@ -3552,7 +3552,6 @@ export const enum EditorOption {
quickSuggestions,
quickSuggestionsDelay,
readOnly,
- removeUnusualLineTerminators,
renameOnType,
renderControlCharacters,
renderIndentGuides,
@@ -3582,6 +3581,7 @@ export const enum EditorOption {
suggestOnTriggerCharacters,
suggestSelection,
tabCompletion,
+ unusualLineTerminators,
useTabStops,
wordSeparators,
wordWrap,
@@ -3971,10 +3971,6 @@ export const EditorOptions = {
readOnly: register(new EditorBooleanOption(
EditorOption.readOnly, 'readOnly', false,
)),
- removeUnusualLineTerminators: register(new EditorBooleanOption(
- EditorOption.removeUnusualLineTerminators, 'removeUnusualLineTerminators', true,
- { description: nls.localize('removeUnusualLineTerminators', "Remove unusual line terminators that might cause problems.") }
- )),
renameOnType: register(new EditorBooleanOption(
EditorOption.renameOnType, 'renameOnType', false,
{ description: nls.localize('renameOnType', "Controls whether the editor auto renames on type.") }
@@ -4144,6 +4140,19 @@ export const EditorOptions = {
description: nls.localize('tabCompletion', "Enables tab completions.")
}
)),
+ unusualLineTerminators: register(new EditorStringEnumOption(
+ EditorOption.unusualLineTerminators, 'unusualLineTerminators',
+ 'prompt' as 'off' | 'prompt' | 'auto',
+ ['off', 'prompt', 'auto'] as const,
+ {
+ enumDescriptions: [
+ nls.localize('unusualLineTerminators.off', "Unusual line terminators are ignored."),
+ nls.localize('unusualLineTerminators.prompt', "Unusual line terminators prompt to be removed."),
+ nls.localize('unusualLineTerminators.auto', "Unusual line terminators are automatically removed."),
+ ],
+ description: nls.localize('unusualLineTerminators', "Remove unusual line terminators that might cause problems.")
+ }
+ )),
useTabStops: register(new EditorBooleanOption(
EditorOption.useTabStops, 'useTabStops', true,
{ description: nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops.") }
diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts
index 11e0462b5a0..7a966ed223e 100644
--- a/src/vs/editor/common/controller/cursor.ts
+++ b/src/vs/editor/common/controller/cursor.ts
@@ -4,10 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
-import { Emitter, Event } from 'vs/base/common/event';
import * as strings from 'vs/base/common/strings';
import { CursorCollection } from 'vs/editor/common/controller/cursorCollection';
-import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon';
+import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, PartialCursorState, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon';
import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { TypeOperations, TypeWithAutoClosingCommand } from 'vs/editor/common/controller/cursorTypeOperations';
@@ -16,48 +15,11 @@ import { Range, IRange } from 'vs/editor/common/core/range';
import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from 'vs/editor/common/model';
-import { RawContentChangedType, ModelRawContentChangedEvent, IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents';
-import * as viewEvents from 'vs/editor/common/view/viewEvents';
-import { dispose } from 'vs/base/common/lifecycle';
-import { EditorOption, ConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
+import { RawContentChangedType, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
+import { VerticalRevealType, ViewCursorStateChangedEvent, ViewRevealRangeRequestEvent } from 'vs/editor/common/view/viewEvents';
+import { dispose, Disposable } from 'vs/base/common/lifecycle';
import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
-
-export class CursorStateChangedEvent {
- /**
- * The new selections.
- * The primary selection is always at index 0.
- */
- readonly selections: Selection[];
- /**
- * The new model version id that `selections` apply to.
- */
- readonly modelVersionId: number;
- /**
- * The old selections.
- */
- readonly oldSelections: Selection[] | null;
- /**
- * The model version id the that `oldSelections` apply to.
- */
- readonly oldModelVersionId: number;
- /**
- * Source of the call that caused the event.
- */
- readonly source: string;
- /**
- * Reason.
- */
- readonly reason: CursorChangeReason;
-
- constructor(selections: Selection[], modelVersionId: number, oldSelections: Selection[] | null, oldModelVersionId: number, source: string, reason: CursorChangeReason) {
- this.selections = selections;
- this.modelVersionId = modelVersionId;
- this.oldSelections = oldSelections;
- this.oldModelVersionId = oldModelVersionId;
- this.source = source;
- this.reason = reason;
- }
-}
+import { CursorStateChangedEvent, ViewModelEventsCollector } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
/**
* A snapshot of the cursor and the model state
@@ -69,7 +31,7 @@ export class CursorModelState {
constructor(model: ITextModel, cursor: Cursor) {
this.modelVersionId = model.getVersionId();
- this.cursorState = cursor.getAll();
+ this.cursorState = cursor.getCursorStates();
}
public equals(other: CursorModelState | null): boolean {
@@ -157,20 +119,10 @@ class AutoClosedAction {
}
}
-export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
+export class Cursor extends Disposable {
public static readonly MAX_CURSOR_COUNT = 10000;
- private readonly _onDidReachMaxCursorCount: Emitter = this._register(new Emitter());
- public readonly onDidReachMaxCursorCount: Event = this._onDidReachMaxCursorCount.event;
-
- private readonly _onDidAttemptReadOnlyEdit: Emitter = this._register(new Emitter());
- public readonly onDidAttemptReadOnlyEdit: Event = this._onDidAttemptReadOnlyEdit.event;
-
- private readonly _onDidChange: Emitter = this._register(new Emitter());
- public readonly onDidChange: Event = this._onDidChange.event;
-
- private readonly _configuration: editorCommon.IConfiguration;
private readonly _model: ITextModel;
private _knownModelVersionId: number;
private readonly _viewModel: ICursorSimpleModel;
@@ -186,14 +138,13 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
private _autoClosedActions: AutoClosedAction[];
private _prevEditOperationType: EditOperationType;
- constructor(configuration: editorCommon.IConfiguration, model: ITextModel, viewModel: ICursorSimpleModel, coordinatesConverter: ICoordinatesConverter) {
+ constructor(model: ITextModel, viewModel: ICursorSimpleModel, coordinatesConverter: ICoordinatesConverter, cursorConfig: CursorConfiguration) {
super();
- this._configuration = configuration;
this._model = model;
this._knownModelVersionId = this._model.getVersionId();
this._viewModel = viewModel;
this._coordinatesConverter = coordinatesConverter;
- this.context = new CursorContext(this._configuration, this._model, this._viewModel, this._coordinatesConverter);
+ this.context = new CursorContext(this._model, this._coordinatesConverter, cursorConfig);
this._cursors = new CursorCollection(this.context);
this._hasFocus = false;
@@ -211,12 +162,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
super.dispose();
}
- private _updateCursorContext(): void {
- this.context = new CursorContext(this._configuration, this._model, this._viewModel, this._coordinatesConverter);
+ public updateConfiguration(cursorConfig: CursorConfiguration): void {
+ this.context = new CursorContext(this._model, this._coordinatesConverter, cursorConfig);
this._cursors.updateContext(this.context);
}
- public onLineMappingChanged(): void {
+ public onLineMappingChanged(eventsCollector: ViewModelEventsCollector): void {
if (this._knownModelVersionId !== this._model.getVersionId()) {
// There are model change events that I didn't yet receive.
//
@@ -228,25 +179,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return;
}
// Ensure valid state
- this.setStates('viewModel', CursorChangeReason.NotSet, this.getAll());
- }
-
- public onDidChangeModelLanguage(e: IModelLanguageChangedEvent): void {
- this._updateCursorContext();
- }
-
- public onDidChangeModelLanguageConfiguration(): void {
- this._updateCursorContext();
- }
-
- public onDidChangeModelOptions(): void {
- this._updateCursorContext();
- }
-
- public onDidChangeConfiguration(e: ConfigurationChangedEvent): void {
- if (CursorConfiguration.shouldRecreate(e)) {
- this._updateCursorContext();
- }
+ this.setStates(eventsCollector, 'viewModel', CursorChangeReason.NotSet, this.getCursorStates());
}
public setHasFocus(hasFocus: boolean): void {
@@ -269,7 +202,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// ------ some getters/setters
- public getPrimaryCursor(): CursorState {
+ public getPrimaryCursorState(): CursorState {
return this._cursors.getPrimaryCursor();
}
@@ -277,14 +210,15 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._cursors.getLastAddedCursorIndex();
}
- public getAll(): CursorState[] {
+ public getCursorStates(): CursorState[] {
return this._cursors.getAll();
}
- public setStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean {
+ public setStates(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean {
+ let reachedMaxCursorCount = false;
if (states !== null && states.length > Cursor.MAX_CURSOR_COUNT) {
states = states.slice(0, Cursor.MAX_CURSOR_COUNT);
- this._onDidReachMaxCursorCount.fire(undefined);
+ reachedMaxCursorCount = true;
}
const oldState = new CursorModelState(this._model, this);
@@ -295,19 +229,38 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._validateAutoClosedActions();
- return this._emitStateChangedIfNecessary(source, reason, oldState);
+ return this._emitStateChangedIfNecessary(eventsCollector, source, reason, oldState, reachedMaxCursorCount);
}
- public setColumnSelectData(columnSelectData: IColumnSelectData): void {
+ public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void {
this._columnSelectData = columnSelectData;
}
- public reveal(source: string | null | undefined, horizontal: boolean, target: RevealTarget, scrollType: editorCommon.ScrollType): void {
- this._revealRange(source, target, viewEvents.VerticalRevealType.Simple, horizontal, scrollType);
+ public revealPrimary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
+ const viewPositions = this._cursors.getViewPositions();
+ if (viewPositions.length > 1) {
+ this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), VerticalRevealType.Simple, revealHorizontal, scrollType);
+ return;
+ } else {
+ const viewPosition = viewPositions[0];
+ const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
+ this._emitCursorRevealRange(eventsCollector, source, viewRange, null, VerticalRevealType.Simple, revealHorizontal, scrollType);
+ }
}
- public revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: editorCommon.ScrollType) {
- this.emitCursorRevealRange(source, viewRange, null, verticalType, revealHorizontal, scrollType);
+ private _revealPrimaryCursor(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
+ const viewPositions = this._cursors.getViewPositions();
+ if (viewPositions.length > 1) {
+ this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), verticalType, revealHorizontal, scrollType);
+ } else {
+ const viewPosition = viewPositions[0];
+ const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
+ this._emitCursorRevealRange(eventsCollector, source, viewRange, null, verticalType, revealHorizontal, scrollType);
+ }
+ }
+
+ private _emitCursorRevealRange(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) {
+ eventsCollector.emitViewEvent(new ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType));
}
public saveState(): editorCommon.ICursorState[] {
@@ -334,7 +287,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return result;
}
- public restoreState(states: editorCommon.ICursorState[]): void {
+ public restoreState(eventsCollector: ViewModelEventsCollector, states: editorCommon.ICursorState[]): void {
let desiredSelections: ISelection[] = [];
@@ -371,11 +324,11 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
});
}
- this.setStates('restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections));
- this.reveal('restoreState', true, RevealTarget.Primary, editorCommon.ScrollType.Immediate);
+ this.setStates(eventsCollector, 'restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections));
+ this.revealPrimary(eventsCollector, 'restoreState', true, editorCommon.ScrollType.Immediate);
}
- public onModelContentChanged(e: ModelRawContentChangedEvent): void {
+ public onModelContentChanged(eventsCollector: ViewModelEventsCollector, e: ModelRawContentChangedEvent): void {
this._knownModelVersionId = e.versionId;
if (this._isHandling) {
@@ -390,16 +343,16 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._cursors.dispose();
this._cursors = new CursorCollection(this.context);
this._validateAutoClosedActions();
- this._emitStateChangedIfNecessary('model', CursorChangeReason.ContentFlush, null);
+ this._emitStateChangedIfNecessary(eventsCollector, 'model', CursorChangeReason.ContentFlush, null, false);
} else {
if (this._hasFocus && e.resultingSelection && e.resultingSelection.length > 0) {
const cursorState = CursorState.fromModelSelections(e.resultingSelection);
- if (this.setStates('modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState)) {
- this._revealRange('modelChange', RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
+ if (this.setStates(eventsCollector, 'modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState)) {
+ this._revealPrimaryCursor(eventsCollector, 'modelChange', VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
}
} else {
const selectionsFromMarkers = this._cursors.readSelectionFromMarkers();
- this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers));
+ this.setStates(eventsCollector, 'modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers));
}
}
}
@@ -408,7 +361,15 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._cursors.getPrimaryCursor().modelState.selection;
}
- public getColumnSelectData(): IColumnSelectData {
+ public getTopMostViewPosition(): Position {
+ return this._cursors.getTopMostViewPosition();
+ }
+
+ public getBottomMostViewPosition(): Position {
+ return this._cursors.getBottomMostViewPosition();
+ }
+
+ public getCursorColumnSelectData(): IColumnSelectData {
if (this._columnSelectData) {
return this._columnSelectData;
}
@@ -418,9 +379,9 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return {
isReal: false,
fromViewLineNumber: viewSelectionStart.lineNumber,
- fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewSelectionStart),
+ fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewSelectionStart),
toViewLineNumber: viewPosition.lineNumber,
- toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewPosition),
+ toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewPosition),
};
}
@@ -432,8 +393,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._cursors.getPrimaryCursor().modelState.position;
}
- public setSelections(source: string | null | undefined, selections: readonly ISelection[]): void {
- this.setStates(source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections));
+ public setSelections(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, selections: readonly ISelection[]): void {
+ this.setStates(eventsCollector, source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections));
}
public getPrevEditOperationType(): EditOperationType {
@@ -524,7 +485,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// -----------------------------------------------------------------------------------------------------------
// ----- emitting events
- private _emitStateChangedIfNecessary(source: string | null | undefined, reason: CursorChangeReason, oldState: CursorModelState | null): boolean {
+ private _emitStateChangedIfNecessary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, oldState: CursorModelState | null, reachedMaxCursorCount: boolean): boolean {
const newState = new CursorModelState(this._model, this);
if (newState.equals(oldState)) {
return false;
@@ -534,7 +495,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
const viewSelections = this._cursors.getViewSelections();
// Let the view get the event first.
- this._emitSingleViewEvent(new viewEvents.ViewCursorStateChangedEvent(viewSelections, selections));
+ eventsCollector.emitViewEvent(new ViewCursorStateChangedEvent(viewSelections, selections));
// Only after the view has been notified, let the rest of the world know...
if (!oldState
@@ -543,44 +504,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
) {
const oldSelections = oldState ? oldState.cursorState.map(s => s.modelState.selection) : null;
const oldModelVersionId = oldState ? oldState.modelVersionId : 0;
- this._onDidChange.fire(new CursorStateChangedEvent(selections, newState.modelVersionId, oldSelections, oldModelVersionId, source || 'keyboard', reason));
+ eventsCollector.emitOutgoingEvent(new CursorStateChangedEvent(oldSelections, selections, oldModelVersionId, newState.modelVersionId, source || 'keyboard', reason, reachedMaxCursorCount));
}
return true;
}
- private _revealRange(source: string | null | undefined, revealTarget: RevealTarget, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
- const viewPositions = this._cursors.getViewPositions();
-
- let viewPosition = viewPositions[0];
-
- if (revealTarget === RevealTarget.TopMost) {
- for (let i = 1; i < viewPositions.length; i++) {
- if (viewPositions[i].isBefore(viewPosition)) {
- viewPosition = viewPositions[i];
- }
- }
- } else if (revealTarget === RevealTarget.BottomMost) {
- for (let i = 1; i < viewPositions.length; i++) {
- if (viewPosition.isBeforeOrEqual(viewPositions[i])) {
- viewPosition = viewPositions[i];
- }
- }
- } else {
- if (viewPositions.length > 1) {
- this.emitCursorRevealRange(source, null, this._cursors.getViewSelections(), verticalType, revealHorizontal, scrollType);
- return;
- }
- }
-
- const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
- this.emitCursorRevealRange(source, viewRange, null, verticalType, revealHorizontal, scrollType);
- }
-
- public emitCursorRevealRange(source: string | null | undefined, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) {
- this._emitSingleViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType));
- }
-
// -----------------------------------------------------------------------------------------------------------
// ----- handlers beyond this point
@@ -602,7 +531,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
}
const closeChar = m[1];
- const autoClosingPairsCandidates = this.context.config.autoClosingPairsClose2.get(closeChar);
+ const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairsClose2.get(closeChar);
if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) {
return null;
}
@@ -620,7 +549,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return indices;
}
- public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
+ public executeEdits(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
let autoClosingIndices: [number, number][] | null = null;
if (source === 'snippet') {
autoClosingIndices = this._findAutoClosingPairs(edits);
@@ -655,146 +584,117 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
});
if (selections) {
this._isHandling = false;
- this.setSelections(source, selections);
+ this.setSelections(eventsCollector, source, selections);
}
if (autoClosedCharactersRanges.length > 0) {
this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges);
}
}
- public trigger(source: string | null | undefined, handlerId: string, payload: any): void {
- const H = editorCommon.Handler;
-
- if (handlerId === H.CompositionStart) {
- this._isDoingComposition = true;
- this._selectionsWhenCompositionStarted = this.getSelections().slice(0);
- return;
- }
-
- if (handlerId === H.CompositionEnd) {
- this._isDoingComposition = false;
- }
-
- if (this._configuration.options.get(EditorOption.readOnly)) {
- // All the remaining handlers will try to edit the model,
- // but we cannot edit when read only...
- this._onDidAttemptReadOnlyEdit.fire(undefined);
+ private _executeEdit(callback: () => void, eventsCollector: ViewModelEventsCollector, source: string | null | undefined, cursorChangeReason: CursorChangeReason = CursorChangeReason.NotSet): void {
+ if (this.context.cursorConfig.readOnly) {
+ // we cannot edit when read only...
return;
}
const oldState = new CursorModelState(this._model, this);
- let cursorChangeReason = CursorChangeReason.NotSet;
-
this._cursors.stopTrackingSelections();
-
- // ensure valid state on all cursors
- this._cursors.ensureValidState();
-
this._isHandling = true;
try {
- switch (handlerId) {
- case H.Type:
- this._type(source, payload.text);
- break;
-
- case H.ReplacePreviousChar:
- this._replacePreviousChar(payload.text, payload.replaceCharCnt);
- break;
-
- case H.Paste:
- cursorChangeReason = CursorChangeReason.Paste;
- this._paste(payload.text, payload.pasteOnNewLine, payload.multicursorText || []);
- break;
-
- case H.Cut:
- this._cut();
- break;
-
- case H.ExecuteCommand:
- this._externalExecuteCommand(payload);
- break;
-
- case H.ExecuteCommands:
- this._externalExecuteCommands(payload);
- break;
-
- case H.CompositionEnd:
- this._interpretCompositionEnd(source);
- break;
- }
+ this._cursors.ensureValidState();
+ callback();
} catch (err) {
onUnexpectedError(err);
}
this._isHandling = false;
-
this._cursors.startTrackingSelections();
-
this._validateAutoClosedActions();
-
- if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) {
- this._revealRange(source, RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
+ if (this._emitStateChangedIfNecessary(eventsCollector, source, cursorChangeReason, oldState, false)) {
+ this._revealPrimaryCursor(eventsCollector, source, VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
}
}
- private _interpretCompositionEnd(source: string | null | undefined) {
- if (!this._isDoingComposition && source === 'keyboard') {
- // composition finishes, let's check if we need to auto complete if necessary.
- const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
- this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters));
- this._selectionsWhenCompositionStarted = null;
- }
+ public setIsDoingComposition(isDoingComposition: boolean): void {
+ this._isDoingComposition = isDoingComposition;
}
- private _type(source: string | null | undefined, text: string): void {
- if (source === 'keyboard') {
- // If this event is coming straight from the keyboard, look for electric characters and enter
+ public startComposition(eventsCollector: ViewModelEventsCollector): void {
+ this._selectionsWhenCompositionStarted = this.getSelections().slice(0);
+ }
- const len = text.length;
- let offset = 0;
- while (offset < len) {
- const charLength = strings.nextCharLength(text, offset);
- const chr = text.substr(offset, charLength);
-
- // Here we must interpret each typed character individually
+ public endComposition(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void {
+ this._executeEdit(() => {
+ if (source === 'keyboard') {
+ // composition finishes, let's check if we need to auto complete if necessary.
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
- this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr));
-
- offset += charLength;
+ this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters));
+ this._selectionsWhenCompositionStarted = null;
}
-
- } else {
- this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text));
- }
+ }, eventsCollector, source);
}
- private _replacePreviousChar(text: string, replaceCharCnt: number): void {
- this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text, replaceCharCnt));
+ public type(eventsCollector: ViewModelEventsCollector, text: string, source?: string | null | undefined): void {
+ this._executeEdit(() => {
+ if (source === 'keyboard') {
+ // If this event is coming straight from the keyboard, look for electric characters and enter
+
+ const len = text.length;
+ let offset = 0;
+ while (offset < len) {
+ const charLength = strings.nextCharLength(text, offset);
+ const chr = text.substr(offset, charLength);
+
+ // Here we must interpret each typed character individually
+ const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
+ this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), autoClosedCharacters, chr));
+
+ offset += charLength;
+ }
+
+ } else {
+ this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text));
+ }
+ }, eventsCollector, source);
}
- private _paste(text: string, pasteOnNewLine: boolean, multicursorText: string[]): void {
- this._executeEditOperation(TypeOperations.paste(this.context.config, this.context.model, this.getSelections(), text, pasteOnNewLine, multicursorText));
+ public replacePreviousChar(eventsCollector: ViewModelEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void {
+ this._executeEdit(() => {
+ this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replaceCharCnt));
+ }, eventsCollector, source);
}
- private _cut(): void {
- this._executeEditOperation(DeleteOperations.cut(this.context.config, this.context.model, this.getSelections()));
+ public paste(eventsCollector: ViewModelEventsCollector, text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
+ this._executeEdit(() => {
+ this._executeEditOperation(TypeOperations.paste(this.context.cursorConfig, this._model, this.getSelections(), text, pasteOnNewLine, multicursorText || []));
+ }, eventsCollector, source, CursorChangeReason.Paste);
}
- private _externalExecuteCommand(command: editorCommon.ICommand): void {
- this._cursors.killSecondaryCursors();
-
- this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], {
- shouldPushStackElementBefore: false,
- shouldPushStackElementAfter: false
- }));
+ public cut(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void {
+ this._executeEdit(() => {
+ this._executeEditOperation(DeleteOperations.cut(this.context.cursorConfig, this._model, this.getSelections()));
+ }, eventsCollector, source);
}
- private _externalExecuteCommands(commands: editorCommon.ICommand[]): void {
- this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, {
- shouldPushStackElementBefore: false,
- shouldPushStackElementAfter: false
- }));
+ public executeCommand(eventsCollector: ViewModelEventsCollector, command: editorCommon.ICommand, source?: string | null | undefined): void {
+ this._executeEdit(() => {
+ this._cursors.killSecondaryCursors();
+
+ this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], {
+ shouldPushStackElementBefore: false,
+ shouldPushStackElementAfter: false
+ }));
+ }, eventsCollector, source);
+ }
+
+ public executeCommands(eventsCollector: ViewModelEventsCollector, commands: editorCommon.ICommand[], source?: string | null | undefined): void {
+ this._executeEdit(() => {
+ this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, {
+ shouldPushStackElementBefore: false,
+ shouldPushStackElementAfter: false
+ }));
+ }, eventsCollector, source);
}
}
diff --git a/src/vs/editor/common/controller/cursorCollection.ts b/src/vs/editor/common/controller/cursorCollection.ts
index 63cc38e4862..746039533ad 100644
--- a/src/vs/editor/common/controller/cursorCollection.ts
+++ b/src/vs/editor/common/controller/cursorCollection.ts
@@ -82,6 +82,28 @@ export class CursorCollection {
return result;
}
+ public getTopMostViewPosition(): Position {
+ let result = this.primaryCursor.viewState.position;
+ for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
+ const viewPosition = this.secondaryCursors[i].viewState.position;
+ if (viewPosition.isBefore(result)) {
+ result = viewPosition;
+ }
+ }
+ return result;
+ }
+
+ public getBottomMostViewPosition(): Position {
+ let result = this.primaryCursor.viewState.position;
+ for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
+ const viewPosition = this.secondaryCursors[i].viewState.position;
+ if (result.isBeforeOrEqual(viewPosition)) {
+ result = viewPosition;
+ }
+ }
+ return result;
+ }
+
public getSelections(): Selection[] {
let result: Selection[] = [];
result[0] = this.primaryCursor.modelState.selection;
@@ -204,7 +226,7 @@ export class CursorCollection {
const currentSelection = current.selection;
const nextSelection = next.selection;
- if (!this.context.config.multiCursorMergeOverlapping) {
+ if (!this.context.cursorConfig.multiCursorMergeOverlapping) {
continue;
}
diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts
index 4676e2d03e7..e49c7dea933 100644
--- a/src/vs/editor/common/controller/cursorCommon.ts
+++ b/src/vs/editor/common/controller/cursorCommon.ts
@@ -7,17 +7,15 @@ import { CharCode } from 'vs/base/common/charCode';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as strings from 'vs/base/common/strings';
import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingOvertypeStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions';
-import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
-import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCommon';
+import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon';
import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
-import { VerticalRevealType } from 'vs/editor/common/view/viewEvents';
import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
import { Constants } from 'vs/base/common/uint';
@@ -46,23 +44,6 @@ export const enum EditOperationType {
DeletingRight = 3
}
-export interface ICursors {
- readonly context: CursorContext;
- getPrimaryCursor(): CursorState;
- getLastAddedCursorIndex(): number;
- getAll(): CursorState[];
-
- getColumnSelectData(): IColumnSelectData;
- setColumnSelectData(columnSelectData: IColumnSelectData): void;
-
- setStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void;
- reveal(source: string | null | undefined, horizontal: boolean, target: RevealTarget, scrollType: ScrollType): void;
- revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void;
-
- getPrevEditOperationType(): EditOperationType;
- setPrevEditOperationType(type: EditOperationType): void;
-}
-
export interface CharacterMap {
[char: string]: string;
}
@@ -355,19 +336,13 @@ export class CursorContext {
_cursorContextBrand: void;
public readonly model: ITextModel;
- public readonly viewModel: ICursorSimpleModel;
public readonly coordinatesConverter: ICoordinatesConverter;
- public readonly config: CursorConfiguration;
+ public readonly cursorConfig: CursorConfiguration;
- constructor(configuration: IConfiguration, model: ITextModel, viewModel: ICursorSimpleModel, coordinatesConverter: ICoordinatesConverter) {
+ constructor(model: ITextModel, coordinatesConverter: ICoordinatesConverter, cursorConfig: CursorConfiguration) {
this.model = model;
- this.viewModel = viewModel;
this.coordinatesConverter = coordinatesConverter;
- this.config = new CursorConfiguration(
- this.model.getLanguageIdentifier(),
- this.model.getOptions(),
- configuration
- );
+ this.cursorConfig = cursorConfig;
}
}
diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts
index f1422f2df5d..0dd8834348f 100644
--- a/src/vs/editor/common/controller/cursorMoveCommands.ts
+++ b/src/vs/editor/common/controller/cursorMoveCommands.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as types from 'vs/base/common/types';
-import { CursorContext, CursorState, ICursorSimpleModel, PartialCursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon';
+import { CursorState, ICursorSimpleModel, PartialCursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon';
import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations';
import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations';
import { IPosition, Position } from 'vs/editor/common/core/position';
@@ -14,122 +14,122 @@ import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
export class CursorMoveCommands {
- public static addCursorDown(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
+ public static addCursorDown(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [], resultLen = 0;
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
if (useLogicalLine) {
- result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(context.config, context.model, cursor.modelState));
+ result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel.model, cursor.modelState));
} else {
- result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(context.config, context.viewModel, cursor.viewState));
+ result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel, cursor.viewState));
}
}
return result;
}
- public static addCursorUp(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
+ public static addCursorUp(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [], resultLen = 0;
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
if (useLogicalLine) {
- result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(context.config, context.model, cursor.modelState));
+ result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel.model, cursor.modelState));
} else {
- result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(context.config, context.viewModel, cursor.viewState));
+ result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel, cursor.viewState));
}
}
return result;
}
- public static moveToBeginningOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ public static moveToBeginningOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = this._moveToLineStart(context, cursor, inSelectionMode);
+ result[i] = this._moveToLineStart(viewModel, cursor, inSelectionMode);
}
return result;
}
- private static _moveToLineStart(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
+ private static _moveToLineStart(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
const currentViewStateColumn = cursor.viewState.position.column;
const currentModelStateColumn = cursor.modelState.position.column;
const isFirstLineOfWrappedLine = currentViewStateColumn === currentModelStateColumn;
const currentViewStatelineNumber = cursor.viewState.position.lineNumber;
- const firstNonBlankColumn = context.viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber);
+ const firstNonBlankColumn = viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber);
const isBeginningOfViewLine = currentViewStateColumn === firstNonBlankColumn;
if (!isFirstLineOfWrappedLine && !isBeginningOfViewLine) {
- return this._moveToLineStartByView(context, cursor, inSelectionMode);
+ return this._moveToLineStartByView(viewModel, cursor, inSelectionMode);
} else {
- return this._moveToLineStartByModel(context, cursor, inSelectionMode);
+ return this._moveToLineStartByModel(viewModel, cursor, inSelectionMode);
}
}
- private static _moveToLineStartByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
+ private static _moveToLineStartByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromViewState(
- MoveOperations.moveToBeginningOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode)
+ MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)
);
}
- private static _moveToLineStartByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
+ private static _moveToLineStartByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromModelState(
- MoveOperations.moveToBeginningOfLine(context.config, context.model, cursor.modelState, inSelectionMode)
+ MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)
);
}
- public static moveToEndOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ public static moveToEndOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = this._moveToLineEnd(context, cursor, inSelectionMode);
+ result[i] = this._moveToLineEnd(viewModel, cursor, inSelectionMode);
}
return result;
}
- private static _moveToLineEnd(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
+ private static _moveToLineEnd(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
const viewStatePosition = cursor.viewState.position;
- const viewModelMaxColumn = context.viewModel.getLineMaxColumn(viewStatePosition.lineNumber);
+ const viewModelMaxColumn = viewModel.getLineMaxColumn(viewStatePosition.lineNumber);
const isEndOfViewLine = viewStatePosition.column === viewModelMaxColumn;
const modelStatePosition = cursor.modelState.position;
- const modelMaxColumn = context.model.getLineMaxColumn(modelStatePosition.lineNumber);
+ const modelMaxColumn = viewModel.model.getLineMaxColumn(modelStatePosition.lineNumber);
const isEndLineOfWrappedLine = viewModelMaxColumn - viewStatePosition.column === modelMaxColumn - modelStatePosition.column;
if (isEndOfViewLine || isEndLineOfWrappedLine) {
- return this._moveToLineEndByModel(context, cursor, inSelectionMode);
+ return this._moveToLineEndByModel(viewModel, cursor, inSelectionMode);
} else {
- return this._moveToLineEndByView(context, cursor, inSelectionMode);
+ return this._moveToLineEndByView(viewModel, cursor, inSelectionMode);
}
}
- private static _moveToLineEndByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
+ private static _moveToLineEndByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromViewState(
- MoveOperations.moveToEndOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode)
+ MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)
);
}
- private static _moveToLineEndByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
+ private static _moveToLineEndByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromModelState(
- MoveOperations.moveToEndOfLine(context.config, context.model, cursor.modelState, inSelectionMode)
+ MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)
);
}
- public static expandLineSelection(context: CursorContext, cursors: CursorState[]): PartialCursorState[] {
+ public static expandLineSelection(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const startLineNumber = cursor.modelState.selection.startLineNumber;
- const lineCount = context.model.getLineCount();
+ const lineCount = viewModel.model.getLineCount();
let endLineNumber = cursor.modelState.selection.endLineNumber;
let endColumn: number;
if (endLineNumber === lineCount) {
- endColumn = context.model.getLineMaxColumn(lineCount);
+ endColumn = viewModel.model.getLineMaxColumn(lineCount);
} else {
endLineNumber++;
endColumn = 1;
@@ -143,27 +143,27 @@ export class CursorMoveCommands {
return result;
}
- public static moveToBeginningOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ public static moveToBeginningOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode));
+ result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));
}
return result;
}
- public static moveToEndOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ public static moveToEndOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode));
+ result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));
}
return result;
}
- public static selectAll(context: CursorContext, cursor: CursorState): PartialCursorState {
- const lineCount = context.model.getLineCount();
- const maxColumn = context.model.getLineMaxColumn(lineCount);
+ public static selectAll(viewModel: IViewModel, cursor: CursorState): PartialCursorState {
+ const lineCount = viewModel.model.getLineCount();
+ const maxColumn = viewModel.model.getLineMaxColumn(lineCount);
return CursorState.fromModelState(new SingleCursorState(
new Range(1, 1, 1, 1), 0,
@@ -171,23 +171,23 @@ export class CursorMoveCommands {
));
}
- public static line(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
- const position = context.model.validatePosition(_position);
+ public static line(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
+ const position = viewModel.model.validatePosition(_position);
const viewPosition = (
_viewPosition
- ? context.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
- : context.coordinatesConverter.convertModelPositionToViewPosition(position)
+ ? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
+ : viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)
);
if (!inSelectionMode || !cursor.modelState.hasSelection()) {
// Entering line selection for the first time
- const lineCount = context.model.getLineCount();
+ const lineCount = viewModel.model.getLineCount();
let selectToLineNumber = position.lineNumber + 1;
let selectToColumn = 1;
if (selectToLineNumber > lineCount) {
selectToLineNumber = lineCount;
- selectToColumn = context.model.getLineMaxColumn(selectToLineNumber);
+ selectToColumn = viewModel.model.getLineMaxColumn(selectToLineNumber);
}
return CursorState.fromModelState(new SingleCursorState(
@@ -207,13 +207,13 @@ export class CursorMoveCommands {
} else if (position.lineNumber > enteringLineNumber) {
- const lineCount = context.viewModel.getLineCount();
+ const lineCount = viewModel.getLineCount();
let selectToViewLineNumber = viewPosition.lineNumber + 1;
let selectToViewColumn = 1;
if (selectToViewLineNumber > lineCount) {
selectToViewLineNumber = lineCount;
- selectToViewColumn = context.viewModel.getLineMaxColumn(selectToViewLineNumber);
+ selectToViewColumn = viewModel.getLineMaxColumn(selectToViewLineNumber);
}
return CursorState.fromViewState(cursor.viewState.move(
@@ -230,12 +230,12 @@ export class CursorMoveCommands {
}
}
- public static word(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState {
- const position = context.model.validatePosition(_position);
- return CursorState.fromModelState(WordOperations.word(context.config, context.model, cursor.modelState, inSelectionMode, position));
+ public static word(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState {
+ const position = viewModel.model.validatePosition(_position);
+ return CursorState.fromModelState(WordOperations.word(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, position));
}
- public static cancelSelection(context: CursorContext, cursor: CursorState): PartialCursorState {
+ public static cancelSelection(viewModel: IViewModel, cursor: CursorState): PartialCursorState {
if (!cursor.modelState.hasSelection()) {
return new CursorState(cursor.modelState, cursor.viewState);
}
@@ -249,107 +249,107 @@ export class CursorMoveCommands {
));
}
- public static moveTo(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
- const position = context.model.validatePosition(_position);
+ public static moveTo(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
+ const position = viewModel.model.validatePosition(_position);
const viewPosition = (
_viewPosition
- ? context.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
- : context.coordinatesConverter.convertModelPositionToViewPosition(position)
+ ? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
+ : viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)
);
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0));
}
- public static simpleMove(context: CursorContext, cursors: CursorState[], direction: CursorMove.SimpleMoveDirection, inSelectionMode: boolean, value: number, unit: CursorMove.Unit): PartialCursorState[] | null {
+ public static simpleMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.SimpleMoveDirection, inSelectionMode: boolean, value: number, unit: CursorMove.Unit): PartialCursorState[] | null {
switch (direction) {
case CursorMove.Direction.Left: {
if (unit === CursorMove.Unit.HalfLine) {
// Move left by half the current line length
- return this._moveHalfLineLeft(context, cursors, inSelectionMode);
+ return this._moveHalfLineLeft(viewModel, cursors, inSelectionMode);
} else {
// Move left by `moveParams.value` columns
- return this._moveLeft(context, cursors, inSelectionMode, value);
+ return this._moveLeft(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.Right: {
if (unit === CursorMove.Unit.HalfLine) {
// Move right by half the current line length
- return this._moveHalfLineRight(context, cursors, inSelectionMode);
+ return this._moveHalfLineRight(viewModel, cursors, inSelectionMode);
} else {
// Move right by `moveParams.value` columns
- return this._moveRight(context, cursors, inSelectionMode, value);
+ return this._moveRight(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.Up: {
if (unit === CursorMove.Unit.WrappedLine) {
// Move up by view lines
- return this._moveUpByViewLines(context, cursors, inSelectionMode, value);
+ return this._moveUpByViewLines(viewModel, cursors, inSelectionMode, value);
} else {
// Move up by model lines
- return this._moveUpByModelLines(context, cursors, inSelectionMode, value);
+ return this._moveUpByModelLines(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.Down: {
if (unit === CursorMove.Unit.WrappedLine) {
// Move down by view lines
- return this._moveDownByViewLines(context, cursors, inSelectionMode, value);
+ return this._moveDownByViewLines(viewModel, cursors, inSelectionMode, value);
} else {
// Move down by model lines
- return this._moveDownByModelLines(context, cursors, inSelectionMode, value);
+ return this._moveDownByModelLines(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.WrappedLineStart: {
// Move to the beginning of the current view line
- return this._moveToViewMinColumn(context, cursors, inSelectionMode);
+ return this._moveToViewMinColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineFirstNonWhitespaceCharacter: {
// Move to the first non-whitespace column of the current view line
- return this._moveToViewFirstNonWhitespaceColumn(context, cursors, inSelectionMode);
+ return this._moveToViewFirstNonWhitespaceColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineColumnCenter: {
// Move to the "center" of the current view line
- return this._moveToViewCenterColumn(context, cursors, inSelectionMode);
+ return this._moveToViewCenterColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineEnd: {
// Move to the end of the current view line
- return this._moveToViewMaxColumn(context, cursors, inSelectionMode);
+ return this._moveToViewMaxColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineLastNonWhitespaceCharacter: {
// Move to the last non-whitespace column of the current view line
- return this._moveToViewLastNonWhitespaceColumn(context, cursors, inSelectionMode);
+ return this._moveToViewLastNonWhitespaceColumn(viewModel, cursors, inSelectionMode);
}
}
return null;
}
- public static viewportMove(viewModel: IViewModel, context: CursorContext, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null {
+ public static viewportMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null {
const visibleViewRange = viewModel.getCompletelyVisibleViewRange();
- const visibleModelRange = context.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
+ const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
switch (direction) {
case CursorMove.Direction.ViewPortTop: {
// Move to the nth line start in the viewport (from the top)
- const modelLineNumber = this._firstLineNumberInRange(context.model, visibleModelRange, value);
- const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
- return [this._moveToModelPosition(context, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
+ const modelLineNumber = this._firstLineNumberInRange(viewModel.model, visibleModelRange, value);
+ const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
+ return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
}
case CursorMove.Direction.ViewPortBottom: {
// Move to the nth line start in the viewport (from the bottom)
- const modelLineNumber = this._lastLineNumberInRange(context.model, visibleModelRange, value);
- const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
- return [this._moveToModelPosition(context, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
+ const modelLineNumber = this._lastLineNumberInRange(viewModel.model, visibleModelRange, value);
+ const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
+ return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
}
case CursorMove.Direction.ViewPortCenter: {
// Move to the line start in the viewport center
const modelLineNumber = Math.round((visibleModelRange.startLineNumber + visibleModelRange.endLineNumber) / 2);
- const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
- return [this._moveToModelPosition(context, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
+ const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
+ return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
}
case CursorMove.Direction.ViewPortIfOutside: {
// Move to a position inside the viewport
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = this.findPositionInViewportIfOutside(context, cursor, visibleViewRange, inSelectionMode);
+ result[i] = this.findPositionInViewportIfOutside(viewModel, cursor, visibleViewRange, inSelectionMode);
}
return result;
}
@@ -358,7 +358,7 @@ export class CursorMoveCommands {
return null;
}
- public static findPositionInViewportIfOutside(context: CursorContext, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState {
+ public static findPositionInViewportIfOutside(viewModel: IViewModel, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState {
let viewLineNumber = cursor.viewState.position.lineNumber;
if (visibleViewRange.startLineNumber <= viewLineNumber && viewLineNumber <= visibleViewRange.endLineNumber - 1) {
@@ -372,8 +372,8 @@ export class CursorMoveCommands {
if (viewLineNumber < visibleViewRange.startLineNumber) {
viewLineNumber = visibleViewRange.startLineNumber;
}
- const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
- return this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
+ const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
+ return this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
}
@@ -403,19 +403,20 @@ export class CursorMoveCommands {
return Math.max(startLineNumber, range.endLineNumber - count + 1);
}
- private static _moveLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
+ private static _moveLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
+ const hasMultipleCursors = (cursors.length > 1);
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
+ const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection();
+ let newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
- let newViewState = MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns);
-
- if (!cursor.viewState.hasSelection() && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
+ if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
// moved over to the previous view line
- const newViewModelPosition = context.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
+ const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) {
// stayed on the same model line => pass wrapping point where 2 view positions map to a single model position
- newViewState = MoveOperations.moveLeft(context.config, context.viewModel, newViewState, inSelectionMode, 1);
+ newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1);
}
}
@@ -424,29 +425,31 @@ export class CursorMoveCommands {
return result;
}
- private static _moveHalfLineLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ private static _moveHalfLineLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
- const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2);
- result[i] = CursorState.fromViewState(MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine));
+ const halfLine = Math.round(viewModel.getLineContent(viewLineNumber).length / 2);
+ result[i] = CursorState.fromViewState(MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));
}
return result;
}
- private static _moveRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
+ private static _moveRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
+ const hasMultipleCursors = (cursors.length > 1);
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- let newViewState = MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns);
+ const skipWrappingPointStop = hasMultipleCursors || !cursor.viewState.hasSelection();
+ let newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
- if (!cursor.viewState.hasSelection() && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
+ if (skipWrappingPointStop && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
// moved over to the next view line
- const newViewModelPosition = context.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
+ const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) {
// stayed on the same model line => pass wrapping point where 2 view positions map to a single model position
- newViewState = MoveOperations.moveRight(context.config, context.viewModel, newViewState, inSelectionMode, 1);
+ newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1);
}
}
@@ -455,112 +458,112 @@ export class CursorMoveCommands {
return result;
}
- private static _moveHalfLineRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ private static _moveHalfLineRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
- const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2);
- result[i] = CursorState.fromViewState(MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine));
+ const halfLine = Math.round(viewModel.getLineContent(viewLineNumber).length / 2);
+ result[i] = CursorState.fromViewState(MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));
}
return result;
}
- private static _moveDownByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
+ private static _moveDownByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = CursorState.fromViewState(MoveOperations.moveDown(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount));
+ result[i] = CursorState.fromViewState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));
}
return result;
}
- private static _moveDownByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
+ private static _moveDownByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = CursorState.fromModelState(MoveOperations.moveDown(context.config, context.model, cursor.modelState, inSelectionMode, linesCount));
+ result[i] = CursorState.fromModelState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));
}
return result;
}
- private static _moveUpByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
+ private static _moveUpByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = CursorState.fromViewState(MoveOperations.moveUp(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount));
+ result[i] = CursorState.fromViewState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));
}
return result;
}
- private static _moveUpByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
+ private static _moveUpByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
- result[i] = CursorState.fromModelState(MoveOperations.moveUp(context.config, context.model, cursor.modelState, inSelectionMode, linesCount));
+ result[i] = CursorState.fromModelState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));
}
return result;
}
- private static _moveToViewPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState {
+ private static _moveToViewPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState {
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, toViewLineNumber, toViewColumn, 0));
}
- private static _moveToModelPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState {
+ private static _moveToModelPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState {
return CursorState.fromModelState(cursor.modelState.move(inSelectionMode, toModelLineNumber, toModelColumn, 0));
}
- private static _moveToViewMinColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ private static _moveToViewMinColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
- const viewColumn = context.viewModel.getLineMinColumn(viewLineNumber);
- result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
+ const viewColumn = viewModel.getLineMinColumn(viewLineNumber);
+ result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
- private static _moveToViewFirstNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ private static _moveToViewFirstNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
- const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
- result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
+ const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
+ result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
- private static _moveToViewCenterColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ private static _moveToViewCenterColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
- const viewColumn = Math.round((context.viewModel.getLineMaxColumn(viewLineNumber) + context.viewModel.getLineMinColumn(viewLineNumber)) / 2);
- result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
+ const viewColumn = Math.round((viewModel.getLineMaxColumn(viewLineNumber) + viewModel.getLineMinColumn(viewLineNumber)) / 2);
+ result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
- private static _moveToViewMaxColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ private static _moveToViewMaxColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
- const viewColumn = context.viewModel.getLineMaxColumn(viewLineNumber);
- result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
+ const viewColumn = viewModel.getLineMaxColumn(viewLineNumber);
+ result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
- private static _moveToViewLastNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
+ private static _moveToViewLastNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
- const viewColumn = context.viewModel.getLineLastNonWhitespaceColumn(viewLineNumber);
- result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
+ const viewColumn = viewModel.getLineLastNonWhitespaceColumn(viewLineNumber);
+ result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts
index 6a24b832b9a..94c4a3c48df 100644
--- a/src/vs/editor/common/editorCommon.ts
+++ b/src/vs/editor/common/editorCommon.ts
@@ -150,6 +150,7 @@ export interface ILineChange extends IChange {
* @internal
*/
export interface IConfiguration extends IDisposable {
+ onDidChangeFast(listener: (e: ConfigurationChangedEvent) => void): IDisposable;
onDidChange(listener: (e: ConfigurationChangedEvent) => void): IDisposable;
readonly options: IComputedEditorOptions;
@@ -688,14 +689,36 @@ export const EditorType = {
* Built-in commands.
* @internal
*/
-export const Handler = {
- ExecuteCommand: 'executeCommand',
- ExecuteCommands: 'executeCommands',
+export const enum Handler {
+ CompositionStart = 'compositionStart',
+ CompositionEnd = 'compositionEnd',
+ Type = 'type',
+ ReplacePreviousChar = 'replacePreviousChar',
+ Paste = 'paste',
+ Cut = 'cut',
+}
- Type: 'type',
- ReplacePreviousChar: 'replacePreviousChar',
- CompositionStart: 'compositionStart',
- CompositionEnd: 'compositionEnd',
- Paste: 'paste',
- Cut: 'cut',
-};
+/**
+ * @internal
+ */
+export interface TypePayload {
+ text: string;
+}
+
+/**
+ * @internal
+ */
+export interface ReplacePreviousCharPayload {
+ text: string;
+ replaceCharCnt: number;
+}
+
+/**
+ * @internal
+ */
+export interface PastePayload {
+ text: string;
+ pasteOnNewLine: boolean;
+ multicursorText: string[] | null;
+ mode: string | null;
+}
diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts
index 471e2472b43..487e6d4f0fd 100644
--- a/src/vs/editor/common/editorContextKeys.ts
+++ b/src/vs/editor/common/editorContextKeys.ts
@@ -36,6 +36,8 @@ export namespace EditorContextKeys {
export const canUndo = new RawContextKey('canUndo', false);
export const canRedo = new RawContextKey('canRedo', false);
+ export const hoverVisible = new RawContextKey('editorHoverVisible', false);
+
/**
* A context key that is set when an editor is part of a larger editor, like notebooks or
* (future) a diff editor
diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts
index 24c38f88141..17801ba86e1 100644
--- a/src/vs/editor/common/model/editStack.ts
+++ b/src/vs/editor/common/model/editStack.ts
@@ -12,6 +12,7 @@ import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorks
import { URI } from 'vs/base/common/uri';
import { TextChange, compressConsecutiveTextChanges } from 'vs/editor/common/model/textChange';
import * as buffer from 'vs/base/common/buffer';
+import { IDisposable } from 'vs/base/common/lifecycle';
function uriGetComparisonKey(resource: URI): string {
return resource.toString();
@@ -138,6 +139,10 @@ class SingleModelEditStackData {
}
}
+export interface IUndoRedoDelegate {
+ prepareUndoRedo(element: MultiModelEditStackElement): Promise | IDisposable | void;
+}
+
export class SingleModelEditStackElement implements IResourceUndoRedoElement {
public model: ITextModel | URI;
@@ -163,6 +168,11 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement {
this._data = SingleModelEditStackData.create(model, beforeCursorState);
}
+ public matchesResource(resource: URI): boolean {
+ const uri = (URI.isUri(this.model) ? this.model : this.model.uri);
+ return (uri.toString() === resource.toString());
+ }
+
public setModel(model: ITextModel | URI): void {
this.model = model;
}
@@ -224,6 +234,8 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
private readonly _editStackElementsArr: SingleModelEditStackElement[];
private readonly _editStackElementsMap: Map;
+ private _delegate: IUndoRedoDelegate | null;
+
public get resources(): readonly URI[] {
return this._editStackElementsArr.map(editStackElement => editStackElement.resource);
}
@@ -240,6 +252,32 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
const key = uriGetComparisonKey(editStackElement.resource);
this._editStackElementsMap.set(key, editStackElement);
}
+ this._delegate = null;
+ }
+
+ public setDelegate(delegate: IUndoRedoDelegate): void {
+ this._delegate = delegate;
+ }
+
+ public prepareUndoRedo(): Promise | IDisposable | void {
+ if (this._delegate) {
+ return this._delegate.prepareUndoRedo(this);
+ }
+ }
+
+ public getMissingModels(): URI[] {
+ const result: URI[] = [];
+ for (const editStackElement of this._editStackElementsArr) {
+ if (URI.isUri(editStackElement.model)) {
+ result.push(editStackElement.model);
+ }
+ }
+ return result;
+ }
+
+ public matchesResource(resource: URI): boolean {
+ const key = uriGetComparisonKey(resource);
+ return (this._editStackElementsMap.has(key));
}
public setModel(model: ITextModel | URI): void {
@@ -310,7 +348,7 @@ function getModelEOL(model: ITextModel): EndOfLineSequence {
}
}
-function isKnownStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement {
+export function isEditStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement {
if (!element) {
return false;
}
@@ -329,7 +367,7 @@ export class EditStack {
public pushStackElement(): void {
const lastElement = this._undoRedoService.getLastElement(this._model.uri);
- if (isKnownStackElement(lastElement)) {
+ if (isEditStackElement(lastElement)) {
lastElement.close();
}
}
@@ -340,7 +378,7 @@ export class EditStack {
private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null): EditStackElement {
const lastElement = this._undoRedoService.getLastElement(this._model.uri);
- if (isKnownStackElement(lastElement) && lastElement.canAppend(this._model)) {
+ if (isEditStackElement(lastElement) && lastElement.canAppend(this._model)) {
return lastElement;
}
const newElement = new SingleModelEditStackElement(this._model, beforeCursorState);
diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts
index 77e0df59de7..89a77eed92e 100644
--- a/src/vs/editor/common/modes.ts
+++ b/src/vs/editor/common/modes.ts
@@ -1411,7 +1411,7 @@ export interface RenameProvider {
*/
export interface AuthenticationSession {
id: string;
- getAccessToken(): Thenable;
+ accessToken: string;
account: {
displayName: string;
id: string;
@@ -1603,7 +1603,7 @@ export interface IWebviewPortMapping {
export interface IWebviewOptions {
readonly enableScripts?: boolean;
readonly enableCommandUris?: boolean;
- readonly localResourceRoots?: ReadonlyArray