Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Aeschlimann <martinae@microsoft.com>2020-05-28 23:20:53 +0300
committerMartin Aeschlimann <martinae@microsoft.com>2020-05-28 23:20:53 +0300
commit9b4e59b7f7335c27776393fa6b38ebe3770f6a13 (patch)
tree050dba9677f5a3b66ecb7e373428a3e5255de796
parentc6f665734aeeb0920d07d9fa8437ec5eabeed352 (diff)
parenta79822ac0a0ea924e69d7f2c596f626f14a8f2ca (diff)
Merge branch 'master' into aeschli/cssheadless
-rw-r--r--.yarnrc2
-rw-r--r--build/azure-pipelines/product-build.yml11
-rw-r--r--build/azure-pipelines/product-compile.yml23
-rw-r--r--build/azure-pipelines/win32/product-build-win32-arm64.yml190
-rw-r--r--build/azure-pipelines/win32/publish.ps113
-rw-r--r--build/builtin/main.js4
-rw-r--r--build/gulpfile.vscode.win32.js14
-rw-r--r--build/lib/electron.js2
-rw-r--r--build/lib/electron.ts2
-rw-r--r--build/npm/postinstall.js7
-rw-r--r--build/win32/code.iss9
-rw-r--r--cgmanifest.json4
-rw-r--r--extensions/docker/package.json6
-rw-r--r--extensions/git/package.json14
-rw-r--r--extensions/git/package.nls.json1
-rw-r--r--extensions/git/src/commands.ts72
-rw-r--r--extensions/git/src/git.ts35
-rw-r--r--extensions/github-authentication/build/generateconfig.js35
-rw-r--r--extensions/github-authentication/src/common/clientRegistrar.ts102
-rw-r--r--extensions/github-authentication/src/github.ts48
-rw-r--r--extensions/github-authentication/src/githubServer.ts162
-rw-r--r--extensions/image-preview/media/main.js9
-rw-r--r--extensions/microsoft-authentication/src/AADHelper.ts3
-rw-r--r--extensions/microsoft-authentication/src/keychain.ts16
-rw-r--r--extensions/npm/images/code.svg3
-rw-r--r--extensions/npm/package.json3
-rw-r--r--extensions/search-result/syntaxes/generateTMLanguage.js2
-rw-r--r--extensions/typescript-basics/snippets/typescript.code-snippets13
-rw-r--r--extensions/vscode-notebook-tests/src/notebook.test.ts37
-rw-r--r--package.json7
-rw-r--r--product.json2
-rw-r--r--src/vs/base/browser/contextmenu.ts1
-rw-r--r--src/vs/base/browser/ui/codicons/codicon/codicon.ttfbin59372 -> 60144 bytes
-rw-r--r--src/vs/base/browser/ui/contextview/contextview.css9
-rw-r--r--src/vs/base/browser/ui/contextview/contextview.ts19
-rw-r--r--src/vs/base/browser/ui/dropdown/dropdown.ts5
-rw-r--r--src/vs/base/browser/ui/list/listView.ts26
-rw-r--r--src/vs/base/browser/ui/list/listWidget.ts9
-rw-r--r--src/vs/base/browser/ui/menu/menu.css3
-rw-r--r--src/vs/base/browser/ui/scrollbar/scrollableElement.ts8
-rw-r--r--src/vs/base/browser/ui/selectBox/selectBox.css6
-rw-r--r--src/vs/base/browser/ui/selectBox/selectBox.ts1
-rw-r--r--src/vs/base/browser/ui/selectBox/selectBoxCustom.ts6
-rw-r--r--src/vs/base/browser/ui/tree/abstractTree.ts4
-rw-r--r--src/vs/base/common/codicons.ts10
-rw-r--r--src/vs/base/common/skipList.ts203
-rw-r--r--src/vs/base/test/common/skipList.test.ts218
-rw-r--r--src/vs/code/electron-main/app.ts12
-rw-r--r--src/vs/code/electron-main/auth.ts3
-rw-r--r--src/vs/code/electron-main/sharedProcess.ts1
-rw-r--r--src/vs/code/electron-main/window.ts8
-rw-r--r--src/vs/code/node/cliProcessMain.ts14
-rw-r--r--src/vs/platform/contextview/browser/contextMenuHandler.css4
-rw-r--r--src/vs/platform/contextview/browser/contextMenuHandler.ts6
-rw-r--r--src/vs/platform/contextview/browser/contextView.ts2
-rw-r--r--src/vs/platform/contextview/browser/contextViewService.ts25
-rw-r--r--src/vs/platform/environment/node/argv.ts2
-rw-r--r--src/vs/platform/extensionManagement/common/extensionManagement.ts8
-rw-r--r--src/vs/platform/extensionManagement/common/extensionManagementUtil.ts6
-rw-r--r--src/vs/platform/extensionManagement/node/extensionManagementService.ts48
-rw-r--r--src/vs/platform/extensionManagement/node/extensionsScanner.ts29
-rw-r--r--src/vs/platform/issue/electron-main/issueMainService.ts6
-rw-r--r--src/vs/platform/list/browser/listService.ts23
-rw-r--r--src/vs/platform/undoRedo/common/undoRedo.ts9
-rw-r--r--src/vs/platform/undoRedo/common/undoRedoService.ts614
-rw-r--r--src/vs/platform/update/electron-main/updateService.win32.ts4
-rw-r--r--src/vs/platform/userDataSync/common/abstractSynchronizer.ts2
-rw-r--r--src/vs/platform/userDataSync/common/userDataAutoSyncService.ts53
-rw-r--r--src/vs/platform/userDataSync/common/userDataSyncStoreService.ts3
-rw-r--r--src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts6
-rw-r--r--src/vs/platform/webview/common/mimeTypes.ts6
-rw-r--r--src/vs/platform/webview/common/resourceLoader.ts6
-rw-r--r--src/vs/platform/webview/common/webviewManagerService.ts24
-rw-r--r--src/vs/platform/webview/electron-main/webviewMainService.ts48
-rw-r--r--src/vs/platform/webview/electron-main/webviewProtocolProvider.ts54
-rw-r--r--src/vs/vscode.d.ts14
-rw-r--r--src/vs/workbench/api/browser/mainThreadNotebook.ts90
-rw-r--r--src/vs/workbench/api/common/extHost.protocol.ts12
-rw-r--r--src/vs/workbench/api/common/extHostLanguageFeatures.ts77
-rw-r--r--src/vs/workbench/api/common/extHostNotebook.ts314
-rw-r--r--src/vs/workbench/browser/parts/compositeBar.ts2
-rw-r--r--src/vs/workbench/browser/parts/editor/media/titlecontrol.css2
-rw-r--r--src/vs/workbench/browser/parts/statusbar/statusbarPart.ts8
-rw-r--r--src/vs/workbench/browser/parts/views/viewPaneContainer.ts18
-rw-r--r--src/vs/workbench/contrib/debug/browser/debug.contribution.ts19
-rw-r--r--src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts6
-rw-r--r--src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts2
-rw-r--r--src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts26
-rw-r--r--src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts26
-rw-r--r--src/vs/workbench/contrib/markers/browser/media/markers.css2
-rw-r--r--src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts27
-rw-r--r--src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts12
-rw-r--r--src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts12
-rw-r--r--src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts290
-rw-r--r--src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts22
-rw-r--r--src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts6
-rw-r--r--src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts9
-rw-r--r--src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts36
-rw-r--r--src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts34
-rw-r--r--src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts8
-rw-r--r--src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts91
-rw-r--r--src/vs/workbench/contrib/notebook/common/notebookCommon.ts67
-rw-r--r--src/vs/workbench/contrib/notebook/common/notebookService.ts11
-rw-r--r--src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts10
-rw-r--r--src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts10
-rw-r--r--src/vs/workbench/contrib/output/browser/outputView.ts2
-rw-r--r--src/vs/workbench/contrib/remote/common/showCandidate.ts5
-rw-r--r--src/vs/workbench/contrib/remote/common/tunnelFactory.ts5
-rw-r--r--src/vs/workbench/contrib/search/browser/search.contribution.ts4
-rw-r--r--src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts11
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminalActions.ts2
-rw-r--r--src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts6
-rw-r--r--src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts8
-rw-r--r--src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts75
-rw-r--r--src/vs/workbench/electron-browser/window.ts12
-rw-r--r--src/vs/workbench/services/search/common/search.ts1
-rw-r--r--src/vs/workbench/services/title/electron-sandbox/titleService.ts2
-rw-r--r--src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts61
-rw-r--r--src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts38
-rw-r--r--src/vs/workbench/workbench.web.api.ts22
-rw-r--r--test/unit/electron/index.js3
-rw-r--r--yarn.lock8
122 files changed, 2464 insertions, 1469 deletions
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/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
@@ -74,29 +74,6 @@ steps:
- 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
displayName: Run postinstall scripts
condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true'))
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/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 c459adb6075..bb71f14c12d 100644
--- a/build/lib/electron.js
+++ b/build/lib/electron.js
@@ -64,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',
diff --git a/build/lib/electron.ts b/build/lib/electron.ts
index 82e9cc5f358..e0beca78079 100644
--- a/build/lib/electron.ts
+++ b/build/lib/electron.ts
@@ -69,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',
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/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/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/docker/package.json b/extensions/docker/package.json
index aa4ef1c294b..c1b91dc283d 100644
--- a/extensions/docker/package.json
+++ b/extensions/docker/package.json
@@ -12,9 +12,9 @@
"contributes": {
"languages": [{
"id": "dockerfile",
- "extensions": [ ".dockerfile" ],
- "filenames": [ "Dockerfile" ],
- "aliases": [ "Dockerfile" ],
+ "extensions": [ ".dockerfile", ".containerfile" ],
+ "filenames": [ "Dockerfile", "Containerfile" ],
+ "aliases": [ "Dockerfile", "Containerfile" ],
"configuration": "./language-configuration.json"
}],
"grammars": [{
diff --git a/extensions/git/package.json b/extensions/git/package.json
index 1a5dea75d9f..d33bafbad5f 100644
--- a/extensions/git/package.json
+++ b/extensions/git/package.json
@@ -106,6 +106,12 @@
"icon": "$(add)"
},
{
+ "command": "git.stageAllMerge",
+ "title": "%command.stageAllMerge%",
+ "category": "Git",
+ "icon": "$(add)"
+ },
+ {
"command": "git.stageSelectedRanges",
"title": "%command.stageSelectedRanges%",
"category": "Git"
@@ -491,6 +497,10 @@
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
+ "command": "git.stageAllMerge",
+ "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
+ },
+ {
"command": "git.stageSelectedRanges",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
@@ -897,12 +907,12 @@
],
"scm/resourceGroup/context": [
{
- "command": "git.stageAll",
+ "command": "git.stageAllMerge",
"when": "scmProvider == git && scmResourceGroup == merge",
"group": "1_modification"
},
{
- "command": "git.stageAll",
+ "command": "git.stageAllMerge",
"when": "scmProvider == git && scmResourceGroup == merge",
"group": "inline"
},
diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json
index 43b13827aa8..be817e22df1 100644
--- a/extensions/git/package.nls.json
+++ b/extensions/git/package.nls.json
@@ -14,6 +14,7 @@
"command.stageAll": "Stage All Changes",
"command.stageAllTracked": "Stage All Tracked Changes",
"command.stageAllUntracked": "Stage All Untracked Changes",
+ "command.stageAllMerge": "Stage All Merge Changes",
"command.stageSelectedRanges": "Stage Selected Ranges",
"command.revertSelectedRanges": "Revert Selected Ranges",
"command.stageChange": "Stage Change",
diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts
index 6beae6fe6f8..e60dc39e6a6 100644
--- a/extensions/git/src/commands.ts
+++ b/extensions/git/src/commands.ts
@@ -1000,37 +1000,14 @@ export class CommandCenter {
@command('git.stageAll', { repository: true })
async stageAll(repository: Repository): Promise<void> {
- const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[];
- const { merge, unresolved, deletionConflicts } = await categorizeResourceByResolution(resources);
-
- try {
- for (const deletionConflict of deletionConflicts) {
- await this._stageDeletionConflict(repository, deletionConflict.resourceUri);
- }
- } catch (err) {
- if (/Cancelled/.test(err.message)) {
- return;
- }
-
- throw err;
- }
-
- if (unresolved.length > 0) {
- const message = unresolved.length > 1
- ? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", merge.length)
- : localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(merge[0].resourceUri.fsPath));
-
- const yes = localize('yes', "Yes");
- const pick = await window.showWarningMessage(message, { modal: true }, yes);
+ const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates];
+ const uris = resources.map(r => r.resourceUri);
- if (pick !== yes) {
- return;
- }
+ if (uris.length > 0) {
+ const config = workspace.getConfiguration('git', Uri.file(repository.root));
+ const untrackedChanges = config.get<'mixed' | 'separate' | 'hidden'>('untrackedChanges');
+ await repository.add(uris, untrackedChanges === 'mixed' ? undefined : { update: true });
}
-
- const config = workspace.getConfiguration('git', Uri.file(repository.root));
- const untrackedChanges = config.get<'mixed' | 'separate' | 'hidden'>('untrackedChanges');
- await repository.add([], untrackedChanges === 'mixed' ? undefined : { update: true });
}
private async _stageDeletionConflict(repository: Repository, uri: Uri): Promise<void> {
@@ -1086,6 +1063,43 @@ export class CommandCenter {
await repository.add(uris);
}
+ @command('git.stageAllMerge', { repository: true })
+ async stageAllMerge(repository: Repository): Promise<void> {
+ const resources = repository.mergeGroup.resourceStates.filter(s => s instanceof Resource) as Resource[];
+ const { merge, unresolved, deletionConflicts } = await categorizeResourceByResolution(resources);
+
+ try {
+ for (const deletionConflict of deletionConflicts) {
+ await this._stageDeletionConflict(repository, deletionConflict.resourceUri);
+ }
+ } catch (err) {
+ if (/Cancelled/.test(err.message)) {
+ return;
+ }
+
+ throw err;
+ }
+
+ if (unresolved.length > 0) {
+ const message = unresolved.length > 1
+ ? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", merge.length)
+ : localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(merge[0].resourceUri.fsPath));
+
+ const yes = localize('yes', "Yes");
+ const pick = await window.showWarningMessage(message, { modal: true }, yes);
+
+ if (pick !== yes) {
+ return;
+ }
+ }
+
+ const uris = resources.map(r => r.resourceUri);
+
+ if (uris.length > 0) {
+ await repository.add(uris);
+ }
+ }
+
@command('git.stageChange')
async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];
diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts
index e952280f472..66d91da4d7a 100644
--- a/extensions/git/src/git.ts
+++ b/extensions/git/src/git.ts
@@ -1223,15 +1223,13 @@ export class Repository {
args.push('-A');
}
- args.push('--');
-
if (paths && paths.length) {
- args.push.apply(args, paths.map(sanitizePath));
+ for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
+ await this.run([...args, '--', ...chunk]);
+ }
} else {
- args.push('.');
+ await this.run([...args, '--', '.']);
}
-
- await this.run(args);
}
async rm(paths: string[]): Promise<void> {
@@ -1440,10 +1438,11 @@ export class Repository {
const limiter = new Limiter(5);
const promises: Promise<any>[] = [];
+ const args = ['clean', '-f', '-q'];
for (const paths of groups) {
for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
- promises.push(limiter.queue(() => this.run(['clean', '-f', '-q', '--', ...chunk])));
+ promises.push(limiter.queue(() => this.run([...args, '--', ...chunk])));
}
}
@@ -1475,19 +1474,19 @@ export class Repository {
// In case there are no branches, we must use rm --cached
if (!result.stdout) {
- args = ['rm', '--cached', '-r', '--'];
+ args = ['rm', '--cached', '-r'];
} else {
- args = ['reset', '-q', treeish, '--'];
- }
-
- if (paths && paths.length) {
- args.push.apply(args, paths.map(sanitizePath));
- } else {
- args.push('.');
+ args = ['reset', '-q', treeish];
}
try {
- await this.run(args);
+ if (paths && paths.length > 0) {
+ for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
+ await this.run([...args, '--', ...chunk]);
+ }
+ } else {
+ await this.run([...args, '--', '.']);
+ }
} catch (err) {
// In case there are merge conflicts to be resolved, git reset will output
// some "needs merge" data. We try to get around that.
@@ -1975,10 +1974,10 @@ export class Repository {
}
async updateSubmodules(paths: string[]): Promise<void> {
- const args = ['submodule', 'update', '--'];
+ const args = ['submodule', 'update'];
for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) {
- await this.run([...args, ...chunk]);
+ await this.run([...args, '--', ...chunk]);
}
}
diff --git a/extensions/github-authentication/build/generateconfig.js b/extensions/github-authentication/build/generateconfig.js
deleted file mode 100644
index 2f311efc223..00000000000
--- a/extensions/github-authentication/build/generateconfig.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-const fs = require('fs');
-const path = require('path');
-
-const schemes = ['OSS', 'INSIDERS', 'STABLE', 'EXPLORATION', 'VSO', 'VSO_PPE', 'VSO_DEV'];
-
-function main() {
- let content = {};
-
- for (const scheme of schemes) {
- const id = process.env[`${scheme}_GITHUB_ID`];
- const secret = process.env[`${scheme}_GITHUB_SECRET`];
-
- if (id && secret) {
- content[scheme] = { id, secret };
- }
- }
-
- const githubAppId = process.env.GITHUB_APP_ID;
- const githubAppSecret = process.env.GITHUB_APP_SECRET;
-
- if (githubAppId && githubAppSecret) {
- content.GITHUB_APP = { id: githubAppId, secret: githubAppSecret }
- }
-
- if (Object.keys(content).length > 0) {
- fs.writeFileSync(path.join(__dirname, '../src/common/config.json'), JSON.stringify(content));
- }
-}
-
-main();
diff --git a/extensions/github-authentication/src/common/clientRegistrar.ts b/extensions/github-authentication/src/common/clientRegistrar.ts
deleted file mode 100644
index 4666ec6a6c0..00000000000
--- a/extensions/github-authentication/src/common/clientRegistrar.ts
+++ /dev/null
@@ -1,102 +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 { Uri, env } from 'vscode';
-import * as fs from 'fs';
-import * as path from 'path';
-
-export interface ClientDetails {
- id?: string;
- secret?: string;
-}
-
-export interface ClientConfig {
- OSS: ClientDetails;
- INSIDERS: ClientDetails;
- STABLE: ClientDetails;
- EXPLORATION: ClientDetails;
-
- VSO: ClientDetails;
- VSO_PPE: ClientDetails;
- VSO_DEV: ClientDetails;
-
- GITHUB_APP: ClientDetails;
-}
-
-export class Registrar {
- private _config: ClientConfig;
-
- constructor() {
- try {
- const fileContents = fs.readFileSync(path.join(env.appRoot, 'extensions/github-authentication/src/common/config.json')).toString();
- this._config = JSON.parse(fileContents);
- } catch (e) {
- this._config = {
- OSS: {},
- INSIDERS: {},
- STABLE: {},
- EXPLORATION: {},
- VSO: {},
- VSO_PPE: {},
- VSO_DEV: {},
- GITHUB_APP: {}
- };
- }
- }
-
- getGitHubAppDetails(): ClientDetails {
- if (!this._config.GITHUB_APP.id || !this._config.GITHUB_APP.secret) {
- throw new Error(`No GitHub App client configuration available`);
- }
-
- return this._config.GITHUB_APP;
- }
-
- getClientDetails(callbackUri: Uri): ClientDetails {
- let details: ClientDetails | undefined;
- switch (callbackUri.scheme) {
- case 'code-oss':
- details = this._config.OSS;
- break;
-
- case 'vscode-insiders':
- details = this._config.INSIDERS;
- break;
-
- case 'vscode':
- details = this._config.STABLE;
- break;
-
- case 'vscode-exploration':
- details = this._config.EXPLORATION;
- break;
-
- case 'https':
- switch (callbackUri.authority) {
- case 'online.visualstudio.com':
- details = this._config.VSO;
- break;
- case 'online-ppe.core.vsengsaas.visualstudio.com':
- details = this._config.VSO_PPE;
- break;
- case 'online.dev.core.vsengsaas.visualstudio.com':
- details = this._config.VSO_DEV;
- break;
- }
-
- default:
- throw new Error(`Unrecognized callback ${callbackUri}`);
- }
-
- if (!details.id || !details.secret) {
- throw new Error(`No client configuration available for ${callbackUri}`);
- }
-
- return details;
- }
-}
-
-const ClientRegistrar = new Registrar();
-export default ClientRegistrar;
diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts
index 7ad1b238499..731a20bb25d 100644
--- a/extensions/github-authentication/src/github.ts
+++ b/extensions/github-authentication/src/github.ts
@@ -21,18 +21,6 @@ interface SessionData {
accessToken: string;
}
-// TODO remove
-interface OldSessionData {
- id: string;
- accountName: string;
- scopes: string[];
- accessToken: string;
-}
-
-function isOldSessionData(x: any): x is OldSessionData {
- return !!x.accountName;
-}
-
export class GitHubAuthenticationProvider {
private _sessions: vscode.AuthenticationSession2[] = [];
private _githubServer = new GitHubServer();
@@ -44,8 +32,6 @@ export class GitHubAuthenticationProvider {
// Ignore, network request failed
}
- // TODO revert Cannot validate tokens from auth server, no available clientId
- // await this.validateSessions();
this.pollForChange();
}
@@ -98,9 +84,9 @@ export class GitHubAuthenticationProvider {
const storedSessions = await keychain.getToken();
if (storedSessions) {
try {
- const sessionData: (SessionData | OldSessionData)[] = JSON.parse(storedSessions);
- const sessionPromises = sessionData.map(async (session: SessionData | OldSessionData): Promise<vscode.AuthenticationSession2> => {
- const needsUserInfo = isOldSessionData(session) || !session.account;
+ const sessionData: SessionData[] = JSON.parse(storedSessions);
+ const sessionPromises = sessionData.map(async (session: SessionData): Promise<vscode.AuthenticationSession2> => {
+ const needsUserInfo = !session.account;
let userInfo: { id: string, accountName: string };
if (needsUserInfo) {
userInfo = await this._githubServer.getUserInfo(session.accessToken);
@@ -109,12 +95,8 @@ export class GitHubAuthenticationProvider {
return {
id: session.id,
account: {
- displayName: isOldSessionData(session)
- ? session.accountName
- : session.account?.displayName ?? userInfo!.accountName,
- id: isOldSessionData(session)
- ? userInfo!.id
- : session.account?.id ?? userInfo!.id
+ displayName: session.account?.displayName ?? userInfo!.accountName,
+ id: session.account?.id ?? userInfo!.id
},
scopes: session.scopes,
accessToken: session.accessToken
@@ -144,22 +126,12 @@ export class GitHubAuthenticationProvider {
}
public async login(scopes: string): Promise<vscode.AuthenticationSession2> {
- const token = scopes === 'vso' ? await this.loginAndInstallApp(scopes) : await this._githubServer.login(scopes);
+ const token = await this._githubServer.login(scopes);
const session = await this.tokenToSession(token, scopes.split(' '));
await this.setToken(session);
return session;
}
- public async loginAndInstallApp(scopes: string): Promise<string> {
- const token = await this._githubServer.login(scopes);
- const hasUserInstallation = await this._githubServer.hasUserInstallation(token);
- if (hasUserInstallation) {
- return token;
- } else {
- return this._githubServer.installApp();
- }
- }
-
public async manuallyProvideToken(): Promise<void> {
this._githubServer.manuallyProvideToken();
}
@@ -184,14 +156,6 @@ export class GitHubAuthenticationProvider {
const sessionIndex = this._sessions.findIndex(session => session.id === id);
if (sessionIndex > -1) {
this._sessions.splice(sessionIndex, 1);
- // TODO revert
- // Cannot revoke tokens from auth server, no clientId available
- // const token = await session.getAccessToken();
- // try {
- // await this._githubServer.revokeToken(token);
- // } catch (_) {
- // // ignore, should still remove from keychain
- // }
}
await this.storeSessions();
diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts
index 5e9b2732d46..8499c43fab6 100644
--- a/extensions/github-authentication/src/githubServer.ts
+++ b/extensions/github-authentication/src/githubServer.ts
@@ -9,7 +9,6 @@ import * as vscode from 'vscode';
import * as uuid from 'uuid';
import { PromiseAdapter, promiseFromEvent } from './common/utils';
import Logger from './common/logger';
-import ClientRegistrar from './common/clientRegistrar';
const localize = nls.loadMessageBundle();
@@ -24,8 +23,8 @@ class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.
export const uriHandler = new UriEventHandler;
-const exchangeCodeForToken: (state: string, host: string, getPath: (code: string) => string) => PromiseAdapter<vscode.Uri, string> =
- (state, host, getPath) => async (uri, resolve, reject) => {
+const exchangeCodeForToken: (state: string) => PromiseAdapter<vscode.Uri, string> =
+ (state) => async (uri, resolve, reject) => {
Logger.info('Exchanging code for token...');
const query = parseQuery(uri);
const code = query.code;
@@ -36,8 +35,8 @@ const exchangeCodeForToken: (state: string, host: string, getPath: (code: string
}
const post = https.request({
- host: host,
- path: getPath(code),
+ host: AUTH_RELAY_SERVER,
+ path: `/token?code=${code}&state=${state}`,
method: 'POST',
headers: {
Accept: 'application/json'
@@ -81,26 +80,13 @@ export class GitHubServer {
const state = uuid();
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
- let uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code`);
- if (scopes === 'vso') {
- const clientDetails = ClientRegistrar.getGitHubAppDetails();
- uri = vscode.Uri.parse(`https://github.com/login/oauth/authorize?redirect_uri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&client_id=${clientDetails.id}`);
- }
+ const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code`);
vscode.env.openExternal(uri);
- return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state,
- scopes === 'vso' ? 'github.com' : AUTH_RELAY_SERVER,
- (code) => {
- if (scopes === 'vso') {
- const clientDetails = ClientRegistrar.getGitHubAppDetails();
- return `/login/oauth/access_token?client_id=${clientDetails.id}&client_secret=${clientDetails.secret}&state=${state}&code=${code}`;
- } else {
- return `/token?code=${code}&state=${state}`;
- }
- })).finally(() => {
- this.updateStatusBarItem(false);
- });
+ return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state)).finally(() => {
+ this.updateStatusBarItem(false);
+ });
}
private updateStatusBarItem(isStart?: boolean) {
@@ -130,51 +116,6 @@ export class GitHubServer {
}
}
- public async hasUserInstallation(token: string): Promise<boolean> {
- return new Promise((resolve, reject) => {
- Logger.info('Getting user installations...');
- const post = https.request({
- host: 'api.github.com',
- path: `/user/installations`,
- method: 'GET',
- headers: {
- Accept: 'application/vnd.github.machine-man-preview+json',
- Authorization: `token ${token}`,
- 'User-Agent': 'Visual-Studio-Code'
- }
- }, result => {
- const buffer: Buffer[] = [];
- result.on('data', (chunk: Buffer) => {
- buffer.push(chunk);
- });
- result.on('end', () => {
- if (result.statusCode === 200) {
- const json = JSON.parse(Buffer.concat(buffer).toString());
- Logger.info('Got installation info!');
- const hasInstallation = json.installations.some((installation: { app_slug: string }) => installation.app_slug === 'microsoft-visual-studio-code');
- resolve(hasInstallation);
- } else {
- reject(new Error(result.statusMessage));
- }
- });
- });
-
- post.end();
- post.on('error', err => {
- reject(err);
- });
- });
- }
-
- public async installApp(): Promise<string> {
- const clientDetails = ClientRegistrar.getGitHubAppDetails();
- const state = uuid();
- const uri = vscode.Uri.parse(`https://github.com/apps/microsoft-visual-studio-code/installations/new?state=${state}`);
-
- vscode.env.openExternal(uri);
- return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state, 'github.com', (code) => `/login/oauth/access_token?client_id=${clientDetails.id}&client_secret=${clientDetails.secret}&state=${state}&code=${code}`));
- }
-
public async getUserInfo(token: string): Promise<{ id: string, accountName: string }> {
return new Promise((resolve, reject) => {
Logger.info('Getting account info...');
@@ -210,91 +151,4 @@ export class GitHubServer {
});
});
}
-
- public async validateToken(token: string): Promise<void> {
- return new Promise(async (resolve, reject) => {
- const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
- const clientDetails = ClientRegistrar.getClientDetails(callbackUri);
- const detailsString = `${clientDetails.id}:${clientDetails.secret}`;
-
- const payload = JSON.stringify({ access_token: token });
-
- Logger.info('Validating token...');
- const post = https.request({
- host: 'api.github.com',
- path: `/applications/${clientDetails.id}/token`,
- method: 'POST',
- headers: {
- Authorization: `Basic ${Buffer.from(detailsString).toString('base64')}`,
- 'User-Agent': 'Visual-Studio-Code',
- 'Content-Type': 'application/json',
- 'Content-Length': Buffer.byteLength(payload)
- }
- }, result => {
- const buffer: Buffer[] = [];
- result.on('data', (chunk: Buffer) => {
- buffer.push(chunk);
- });
- result.on('end', () => {
- if (result.statusCode === 200) {
- Logger.info('Validated token!');
- resolve();
- } else {
- Logger.info(`Validating token failed: ${result.statusMessage}`);
- reject(new Error(result.statusMessage));
- }
- });
- });
-
- post.write(payload);
- post.end();
- post.on('error', err => {
- Logger.error(err.message);
- reject(new Error(NETWORK_ERROR));
- });
- });
- }
-
- public async revokeToken(token: string): Promise<void> {
- return new Promise(async (resolve, reject) => {
- const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
- const clientDetails = ClientRegistrar.getClientDetails(callbackUri);
- const detailsString = `${clientDetails.id}:${clientDetails.secret}`;
-
- const payload = JSON.stringify({ access_token: token });
-
- Logger.info('Revoking token...');
- const post = https.request({
- host: 'api.github.com',
- path: `/applications/${clientDetails.id}/token`,
- method: 'DELETE',
- headers: {
- Authorization: `Basic ${Buffer.from(detailsString).toString('base64')}`,
- 'User-Agent': 'Visual-Studio-Code',
- 'Content-Type': 'application/json',
- 'Content-Length': Buffer.byteLength(payload)
- }
- }, result => {
- const buffer: Buffer[] = [];
- result.on('data', (chunk: Buffer) => {
- buffer.push(chunk);
- });
- result.on('end', () => {
- if (result.statusCode === 204) {
- Logger.info('Revoked token!');
- resolve();
- } else {
- Logger.info(`Revoking token failed: ${result.statusMessage}`);
- reject(new Error(result.statusMessage));
- }
- });
- });
-
- post.write(payload);
- post.end();
- post.on('error', err => {
- reject(err);
- });
- });
- }
}
diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js
index 394511aa8ef..d344b99fbaf 100644
--- a/extensions/image-preview/media/main.js
+++ b/extensions/image-preview/media/main.js
@@ -279,6 +279,9 @@
image.classList.add('scale-to-fit');
image.addEventListener('load', () => {
+ if (hasLoadedImage) {
+ return;
+ }
hasLoadedImage = true;
vscode.postMessage({
@@ -297,7 +300,11 @@
}
});
- image.addEventListener('error', () => {
+ image.addEventListener('error', e => {
+ if (hasLoadedImage) {
+ return;
+ }
+
hasLoadedImage = true;
document.body.classList.add('error');
document.body.classList.remove('loading');
diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts
index fd826ec1e2e..ebc453cc2ad 100644
--- a/extensions/microsoft-authentication/src/AADHelper.ts
+++ b/extensions/microsoft-authentication/src/AADHelper.ts
@@ -82,9 +82,6 @@ export class AzureActiveDirectoryService {
}
public async initialize(): Promise<void> {
- // TODO remove, temporary migration
- await keychain.migrateToken();
-
const storedData = await keychain.getToken();
if (storedData) {
try {
diff --git a/extensions/microsoft-authentication/src/keychain.ts b/extensions/microsoft-authentication/src/keychain.ts
index afda7035521..bc0fcc55381 100644
--- a/extensions/microsoft-authentication/src/keychain.ts
+++ b/extensions/microsoft-authentication/src/keychain.ts
@@ -43,22 +43,6 @@ export class Keychain {
this.keytar = keytar;
}
- // TODO remove, temporary migration
- async migrateToken(): Promise<void> {
- const oldServiceId = `${vscode.env.uriScheme}-vscode.login`;
- try {
- const data = await this.keytar.getPassword(oldServiceId, ACCOUNT_ID);
- if (data) {
- Logger.info('Migrating token...');
- this.setToken(data);
- await this.keytar.deletePassword(oldServiceId, ACCOUNT_ID);
- Logger.info('Migration successful');
- }
- } catch (e) {
- Logger.error(`Migrating token failed: ${e}`);
- }
- }
-
async setToken(token: string): Promise<void> {
try {
diff --git a/extensions/npm/images/code.svg b/extensions/npm/images/code.svg
new file mode 100644
index 00000000000..f776259aad0
--- /dev/null
+++ b/extensions/npm/images/code.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.32798 5.00905L1.2384 8.09746L4.32798 11.1859L3.5016 12.0123L0 8.51065V7.68427L3.5016 4.18267L4.32798 5.00905ZM12.4984 4.18267L11.672 5.00905L14.7616 8.09746L11.672 11.1859L12.4984 12.0123L16 8.51065V7.68427L12.4984 4.18267ZM4.56142 13.672L5.6049 14.1949L11.4409 2.52291L10.3974 2L4.56142 13.672V13.672Z" fill="black"/>
+</svg>
diff --git a/extensions/npm/package.json b/extensions/npm/package.json
index 265dffcacf7..ed1e8bd9015 100644
--- a/extensions/npm/package.json
+++ b/extensions/npm/package.json
@@ -55,7 +55,8 @@
{
"id": "npm",
"name": "%view.name%",
- "when": "npm:showScriptExplorer || config.npm.enableScriptExplorer"
+ "when": "npm:showScriptExplorer || config.npm.enableScriptExplorer",
+ "icon": "images/code.svg"
}
]
},
diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js
index 23e0615a718..eac084ddbc1 100644
--- a/extensions/search-result/syntaxes/generateTMLanguage.js
+++ b/extensions/search-result/syntaxes/generateTMLanguage.js
@@ -12,7 +12,7 @@ const mappings = [
['css', 'source.css'],
['dart', 'source.dart'],
['diff', 'source.diff'],
- ['dockerfile', 'source.dockerfile', '(?:dockerfile|Dockerfile)'],
+ ['dockerfile', 'source.dockerfile', '(?:dockerfile|Dockerfile|containerfile|Containerfile)'],
['fs', 'source.fsharp'],
['go', 'source.go'],
['groovy', 'source.groovy'],
diff --git a/extensions/typescript-basics/snippets/typescript.code-snippets b/extensions/typescript-basics/snippets/typescript.code-snippets
index 0a7195751d8..0b4f448a803 100644
--- a/extensions/typescript-basics/snippets/typescript.code-snippets
+++ b/extensions/typescript-basics/snippets/typescript.code-snippets
@@ -123,7 +123,7 @@
"Throw Exception": {
"prefix": "throw",
"body": [
- "throw \"$1\";",
+ "throw new Error(\"$1\");",
"$0"
],
"description": "Throw Exception"
@@ -167,7 +167,16 @@
"}"
],
"description": "For-Of Loop"
- },
+ },
+ "For-Await-Of Loop": {
+ "prefix": "forawaitof",
+ "body": [
+ "for await (const ${1:iterator} of ${2:object}) {",
+ "\t$0",
+ "}"
+ ],
+ "description": "For-Await-Of Loop"
+ },
"Function Statement": {
"prefix": "function",
"body": [
diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts
index ec6eab2d898..bd3a32e8204 100644
--- a/extensions/vscode-notebook-tests/src/notebook.test.ts
+++ b/extensions/vscode-notebook-tests/src/notebook.test.ts
@@ -238,8 +238,29 @@ suite('API tests', () => {
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.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+
+ const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(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', () => {
@@ -321,15 +342,15 @@ suite('notebook workflow', () => {
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}`);
- await vscode.commands.executeCommand('notebook.cell.moveDown');
- activeCell = vscode.notebook.activeNotebookEditor!.selection;
+ // 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, '');
+ // 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, '');
// ---- ---- //
diff --git a/package.json b/package.json
index 7d109b5c4cd..f242d818ab0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.46.0",
- "distro": "64a6a85a3b1af4e177210fa8c72b822322b129ab",
+ "distro": "d4eced2b3485a4ef446032a8141459a9856c18ad",
"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",
@@ -100,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",
diff --git a/product.json b/product.json
index a6707b09ed4..7ef26e21fcd 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",
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/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf
index abcf8283afa..ba25be2a8e9 100644
--- a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf
+++ b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf
Binary files 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/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/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts
index b7c858d1ed2..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';
@@ -48,7 +48,12 @@ export interface IListViewAccessibilityProvider<T> {
isChecked?(element: T): boolean | undefined;
}
-export interface IListViewOptions<T> {
+export interface IListViewOptionsUpdate {
+ readonly additionalScrollHeight?: number;
+ readonly smoothScrolling?: boolean;
+}
+
+export interface IListViewOptions<T> extends IListViewOptionsUpdate {
readonly dnd?: IListViewDragAndDrop<T>;
readonly useShadows?: boolean;
readonly verticalScrollMode?: ScrollbarVisibility;
@@ -58,7 +63,6 @@ export interface IListViewOptions<T> {
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly accessibilityProvider?: IListViewAccessibilityProvider<T>;
- readonly additionalScrollHeight?: number;
readonly transformOptimization?: boolean;
}
@@ -204,7 +208,8 @@ export class ListView<T> implements ISpliceable<T>, 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<void>(50);
@@ -285,12 +290,13 @@ export class ListView<T> implements ISpliceable<T>, 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<T> implements ISpliceable<T>, IDisposable {
if (options.additionalScrollHeight !== undefined) {
this.additionalScrollHeight = options.additionalScrollHeight;
}
+
+ if (options.smoothScrolling !== undefined) {
+ this.scrollable.setSmoothScrollDuration(options.smoothScrolling ? 125 : 0);
+ }
}
triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {
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<T> implements IListViewDragAndDrop<T> {
}
}
-export interface IListOptionsUpdate {
+export interface IListOptionsUpdate extends IListViewOptionsUpdate {
readonly enableKeyboardNavigation?: boolean;
readonly automaticKeyboardNavigation?: boolean;
- readonly additionalScrollHeight?: number;
}
export class List<T> implements ISpliceable<T>, IDisposable {
@@ -1288,9 +1287,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.typeLabelController.updateOptions(this._options);
}
- if (optionsUpdate.additionalScrollHeight !== undefined) {
- this.view.updateOptions(optionsUpdate);
- }
+ this.view.updateOptions(optionsUpdate);
}
get options(): IListOptions<T> {
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/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 50558044d09..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<ISelectData>;
@@ -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;
diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts
index a71e4d6ae48..f19a1cc4cad 100644
--- a/src/vs/base/browser/ui/tree/abstractTree.ts
+++ b/src/vs/base/browser/ui/tree/abstractTree.ts
@@ -962,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
readonly simpleKeyboardNavigation?: boolean;
readonly filterOnType?: boolean;
readonly openOnSingleClick?: boolean;
+ readonly smoothScrolling?: boolean;
}
export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTreeOptionsUpdate, IListOptions<T> {
@@ -1360,7 +1361,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> 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/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<K, V> {
+ readonly forward: Node<K, V>[];
+ constructor(readonly level: number, readonly key: K, public value: V) {
+ this.forward = [];
+ }
+}
+
+const NIL: undefined = undefined;
+
+interface Comparator<K> {
+ (a: K, b: K): number;
+}
+
+export class SkipList<K, V> implements Map<K, V> {
+
+ readonly [Symbol.toStringTag] = 'SkipList';
+
+ private _maxLevel: number;
+ private _level: number = 1;
+ private _header: Node<K, V>;
+ 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 = <any>new Node(this._maxLevel, NIL, NIL);
+ }
+
+ get size(): number {
+ return this._size;
+ }
+
+ clear(): void {
+ this._header = <any>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<K, V>) => 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<K> {
+ let node = this._header.forward[0];
+ while (node) {
+ yield node.key;
+ node = node.forward[0];
+ }
+ }
+
+ *values(): IterableIterator<V> {
+ 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<K, V>(list: SkipList<K, V>, searchKey: K, comparator: Comparator<K>) {
+ 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<K, V>(list: SkipList<K, V>, searchKey: K, value: V, comparator: Comparator<K>) {
+ let update: Node<K, V>[] = [];
+ 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<K, V>(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<any, any>, p: number = 0.5): number {
+ let lvl = 1;
+ while (Math.random() < p && lvl < list._maxLevel) {
+ lvl += 1;
+ }
+ return lvl;
+ }
+
+ private static _delete<K, V>(list: SkipList<K, V>, searchKey: K, comparator: Comparator<K>) {
+ let update: Node<K, V>[] = [];
+ 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/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<V>(list: SkipList<any, V>, 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<K>(list: SkipList<K, any>, 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<number, number>((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<number, boolean>((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<number, boolean>((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<number>();
+ 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<number, boolean>(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/electron-main/app.ts b/src/vs/code/electron-main/app.ts
index 4d8f90aa2ba..de09ab8f939 100644
--- a/src/vs/code/electron-main/app.ts
+++ b/src/vs/code/electron-main/app.ts
@@ -78,8 +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 { IFileService } from 'vs/platform/files/common/files';
-import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
+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;
@@ -88,7 +88,6 @@ export class CodeApplication extends Disposable {
constructor(
private readonly mainIpcServer: Server,
private readonly userEnv: IProcessEnvironment,
- @IFileService fileService: IFileService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: INativeEnvironmentService,
@@ -99,8 +98,6 @@ export class CodeApplication extends Disposable {
super();
this.registerListeners();
-
- this._register(new WebviewProtocolProvider(fileService));
}
private registerListeners(): void {
@@ -471,6 +468,7 @@ export class CodeApplication extends Disposable {
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(IMenubarMainService, new SyncDescriptor(MenubarMainService));
@@ -578,6 +576,10 @@ export class CodeApplication extends Disposable {
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/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts
index e9f95448a08..7ace2583d75 100644
--- a/src/vs/code/electron-main/sharedProcess.ts
+++ b/src/vs/code/electron-main/sharedProcess.ts
@@ -46,6 +46,7 @@ export class SharedProcess implements ISharedProcess {
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
}
});
diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts
index 193fa596db1..78095162798 100644
--- a/src/vs/code/electron-main/window.ts
+++ b/src/vs/code/electron-main/window.ts
@@ -166,7 +166,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
preload: URI.parse(this.doGetPreloadUrl()).fsPath,
nodeIntegration: true,
nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER,
- webviewTag: true
+ webviewTag: true,
+ enableWebSQL: false
}
};
@@ -600,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<string>('http.proxy') || '').trim()
+ let newHttpProxy = (this.configurationService.getValue<string>('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;
diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts
index ea0f62156a2..fbb9673b535 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['default']);
} 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<void> {
+ private async installExtensions(extensions: string[], force: boolean, isDefault: boolean): Promise<void> {
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, isDefault);
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<IExtensionManifest | null> {
+ private async installExtension(extension: string, force: boolean, isDefault: boolean): Promise<IExtensionManifest | null> {
if (/\.vsix$/i.test(extension)) {
extension = path.isAbsolute(extension) ? extension : path.join(process.cwd(), extension);
@@ -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, isDefault);
return manifest;
}));
}
@@ -227,11 +227,11 @@ export class Main {
return true;
}
- private async installFromGallery(id: string, extension: IGalleryExtension): Promise<void> {
+ private async installFromGallery(id: string, extension: IGalleryExtension, isDefault: boolean): Promise<void> {
console.log(localize('installing', "Installing extension '{0}' v{1}...", id, extension.version));
try {
- await this.extensionManagementService.installFromGallery(extension);
+ await this.extensionManagementService.installFromGallery(extension, isDefault);
console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, extension.version));
} catch (error) {
if (isPromiseCanceledError(error)) {
diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.css b/src/vs/platform/contextview/browser/contextMenuHandler.css
index 97ac10bc5be..ef8a5236187 100644
--- a/src/vs/platform/contextview/browser/contextMenuHandler.css
+++ b/src/vs/platform/contextview/browser/contextMenuHandler.css
@@ -9,9 +9,9 @@
.context-view-block {
position: fixed;
+ cursor: initial;
left:0;
top:0;
- z-index: -1;
width: 100%;
height: 100%;
-} \ No newline at end of file
+}
diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts
index 921a5f810a5..3daf186140d 100644
--- a/src/vs/platform/contextview/browser/contextMenuHandler.ts
+++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts
@@ -14,7 +14,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
-import { EventType, $, removeNode } from 'vs/base/browser/dom';
+import { EventType, $, removeNode, isHTMLElement } from 'vs/base/browser/dom';
import { attachMenuStyler } from 'vs/platform/theme/common/styler';
import { domEvent } from 'vs/base/browser/event';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
@@ -50,6 +50,7 @@ export class ContextMenuHandler {
let menu: Menu | undefined;
+ const anchor = delegate.getAnchor();
this.contextViewService.showContextView({
getAnchor: () => delegate.getAnchor(),
canRelayout: false,
@@ -65,6 +66,7 @@ export class ContextMenuHandler {
// Render invisible div to block mouse interaction in the rest of the UI
if (this.options.blockMouse) {
this.block = container.appendChild($('.context-view-block'));
+ domEvent(this.block, EventType.MOUSE_DOWN)((e: MouseEvent) => e.stopPropagation());
}
const menuDisposables = new DisposableStore();
@@ -131,7 +133,7 @@ export class ContextMenuHandler {
this.focusToReturn.focus();
}
}
- });
+ }, !!delegate.anchorAsContainer && isHTMLElement(anchor) ? anchor : undefined);
}
private onActionRun(e: IRunEvent): void {
diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts
index 2104e66c0b4..a4b225a173d 100644
--- a/src/vs/platform/contextview/browser/contextView.ts
+++ b/src/vs/platform/contextview/browser/contextView.ts
@@ -15,7 +15,7 @@ export interface IContextViewService extends IContextViewProvider {
_serviceBrand: undefined;
- showContextView(delegate: IContextViewDelegate): void;
+ showContextView(delegate: IContextViewDelegate, container?: HTMLElement): void;
hideContextView(data?: any): void;
layout(): void;
anchorAlignment?: AnchorAlignment;
diff --git a/src/vs/platform/contextview/browser/contextViewService.ts b/src/vs/platform/contextview/browser/contextViewService.ts
index 1cbd4908c2d..9e06e73db61 100644
--- a/src/vs/platform/contextview/browser/contextViewService.ts
+++ b/src/vs/platform/contextview/browser/contextViewService.ts
@@ -12,13 +12,15 @@ export class ContextViewService extends Disposable implements IContextViewServic
_serviceBrand: undefined;
private contextView: ContextView;
+ private container: HTMLElement;
constructor(
@ILayoutService readonly layoutService: ILayoutService
) {
super();
- this.contextView = this._register(new ContextView(layoutService.container));
+ this.container = layoutService.container;
+ this.contextView = this._register(new ContextView(this.container, false));
this.layout();
this._register(layoutService.onLayout(() => this.layout()));
@@ -26,11 +28,24 @@ export class ContextViewService extends Disposable implements IContextViewServic
// ContextView
- setContainer(container: HTMLElement): void {
- this.contextView.setContainer(container);
+ setContainer(container: HTMLElement, useFixedPosition?: boolean): void {
+ this.contextView.setContainer(container, !!useFixedPosition);
}
- showContextView(delegate: IContextViewDelegate): void {
+ showContextView(delegate: IContextViewDelegate, container?: HTMLElement): void {
+
+ if (container) {
+ if (container !== this.container) {
+ this.container = container;
+ this.setContainer(container, true);
+ }
+ } else {
+ if (this.container !== this.layoutService.container) {
+ this.container = this.layoutService.container;
+ this.setContainer(this.container, false);
+ }
+ }
+
this.contextView.show(delegate);
}
@@ -41,4 +56,4 @@ export class ContextViewService extends Disposable implements IContextViewServic
hideContextView(data?: any): void {
this.contextView.hide(data);
}
-} \ No newline at end of file
+}
diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts
index 61c2b16ad7a..5bd1488b472 100644
--- a/src/vs/platform/environment/node/argv.ts
+++ b/src/vs/platform/environment/node/argv.ts
@@ -72,6 +72,7 @@ export interface ParsedArgs {
remote?: string;
'disable-user-env-probe'?: boolean;
'force'?: boolean;
+ 'default'?: boolean;
'force-user-env'?: boolean;
'sync'?: 'on' | 'off';
@@ -187,6 +188,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'file-chmod': { type: 'boolean' },
'driver-verbose': { type: 'boolean' },
'force': { type: 'boolean' },
+ 'default': { type: 'boolean' },
'trace': { type: 'boolean' },
'trace-category-filter': { type: 'string' },
'trace-options': { type: 'string' },
diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts
index a3867cd9c02..36735a9b105 100644
--- a/src/vs/platform/extensionManagement/common/extensionManagement.ts
+++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts
@@ -92,7 +92,9 @@ export interface IGalleryMetadata {
export interface ILocalExtension extends IExtension {
readonly manifest: IExtensionManifest;
- metadata: IGalleryMetadata;
+ isDefault: boolean | undefined;
+ publisherId: string | null;
+ publisherDisplayName: string | null;
readmeUrl: URI | null;
changelogUrl: URI | null;
}
@@ -204,8 +206,8 @@ export interface IExtensionManagementService {
zip(extension: ILocalExtension): Promise<URI>;
unzip(zipLocation: URI): Promise<IExtensionIdentifier>;
getManifest(vsix: URI): Promise<IExtensionManifest>;
- install(vsix: URI): Promise<ILocalExtension>;
- installFromGallery(extension: IGalleryExtension): Promise<ILocalExtension>;
+ install(vsix: URI, isDefault?: boolean): Promise<ILocalExtension>;
+ installFromGallery(extension: IGalleryExtension, isDefault?: boolean): Promise<ILocalExtension>;
uninstall(extension: ILocalExtension, force?: boolean): Promise<void>;
reinstallFromGallery(extension: ILocalExtension): Promise<void>;
getInstalled(type?: ExtensionType): Promise<ILocalExtension[]>;
diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts
index bad92e00651..fef60bf20b8 100644
--- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts
+++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts
@@ -69,9 +69,9 @@ export function getLocalExtensionTelemetryData(extension: ILocalExtension): any
id: extension.identifier.id,
name: extension.manifest.name,
galleryId: null,
- publisherId: extension.metadata ? extension.metadata.publisherId : null,
+ publisherId: extension.publisherId,
publisherName: extension.manifest.publisher,
- publisherDisplayName: extension.metadata ? extension.metadata.publisherDisplayName : null,
+ publisherDisplayName: extension.publisherDisplayName,
dependencies: extension.manifest.extensionDependencies && extension.manifest.extensionDependencies.length > 0
};
}
@@ -116,4 +116,4 @@ export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set<str
}
return result;
-} \ No newline at end of file
+}
diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
index 6384ceb4880..5fffb8f48e5 100644
--- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts
+++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
@@ -45,7 +45,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader';
-import { ExtensionsScanner } from 'vs/platform/extensionManagement/node/extensionsScanner';
+import { ExtensionsScanner, IMetadata } from 'vs/platform/extensionManagement/node/extensionsScanner';
import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle';
const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled';
@@ -57,7 +57,7 @@ const ERROR_UNKNOWN = 'unknown';
interface InstallableExtension {
zipPath: string;
identifierWithVersion: ExtensionIdentifierWithVersion;
- metadata: IGalleryMetadata | null;
+ metadata?: IMetadata;
}
export class ExtensionManagementService extends Disposable implements IExtensionManagementService {
@@ -152,7 +152,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
- install(vsix: URI): Promise<ILocalExtension> {
+ install(vsix: URI, isDefault?: boolean): Promise<ILocalExtension> {
this.logService.trace('ExtensionManagementService#install', vsix.toString());
return createCancelablePromise(token => {
return this.downloadVsix(vsix).then(downloadLocation => {
@@ -192,10 +192,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
.then(() => {
this.logService.info('Installing the extension:', identifier.id);
this._onInstallExtension.fire({ identifier, zipPath });
- return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name))
+ return this.getGalleryMetadata(getGalleryExtensionId(manifest.publisher, manifest.name))
.then(
- metadata => this.installFromZipPath(identifierWithVersion, zipPath, metadata, operation, token),
- () => this.installFromZipPath(identifierWithVersion, zipPath, null, operation, token))
+ metadata => this.installFromZipPath(identifierWithVersion, zipPath, { ...metadata, isDefault }, operation, token),
+ () => this.installFromZipPath(identifierWithVersion, zipPath, isDefault ? { isDefault } : undefined, operation, token))
.then(
local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; },
e => {
@@ -219,9 +219,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
return this.downloadService.download(vsix, URI.file(downloadedLocation)).then(() => URI.file(downloadedLocation));
}
- private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IGalleryMetadata | null, operation: InstallOperation, token: CancellationToken): Promise<ILocalExtension> {
+ private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IMetadata | undefined, operation: InstallOperation, token: CancellationToken): Promise<ILocalExtension> {
return this.toNonCancellablePromise(this.installExtension({ zipPath, identifierWithVersion, metadata }, token)
- .then(local => this.installDependenciesAndPackExtensions(local, null)
+ .then(local => this.installDependenciesAndPackExtensions(local, undefined)
.then(
() => local,
error => {
@@ -239,7 +239,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
));
}
- async installFromGallery(extension: IGalleryExtension): Promise<ILocalExtension> {
+ async installFromGallery(extension: IGalleryExtension, isDefault?: boolean): Promise<ILocalExtension> {
if (!this.galleryService.isEnabled()) {
return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")));
}
@@ -281,14 +281,17 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.installingExtensions.set(key, cancellablePromise);
try {
const installed = await this.getInstalled(ExtensionType.User);
- const existingExtension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0];
+ const existingExtension = installed.find(i => areSameExtensions(i.identifier, extension.identifier));
if (existingExtension) {
operation = InstallOperation.Update;
}
this.downloadInstallableExtension(extension, operation)
- .then(installableExtension => this.installExtension(installableExtension, cancellationToken)
- .then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local)))
+ .then(installableExtension => {
+ installableExtension.metadata.isDefault = isDefault !== undefined ? isDefault : existingExtension?.isDefault;
+ return this.installExtension(installableExtension, cancellationToken)
+ .then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local));
+ })
.then(local => this.installDependenciesAndPackExtensions(local, existingExtension)
.then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error))))
.then(
@@ -358,7 +361,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
.then(report => getMaliciousExtensionsSet(report).has(extension.identifier.id));
}
- private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise<InstallableExtension> {
+ private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise<Required<InstallableExtension>> {
const metadata = <IGalleryMetadata>{
id: extension.identifier.uuid,
publisherId: extension.publisherId,
@@ -373,7 +376,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.logService.info('Downloaded extension:', extension.identifier.id, zipPath);
return getManifest(zipPath)
.then(
- manifest => (<InstallableExtension>{ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }),
+ manifest => (<Required<InstallableExtension>>{ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }),
error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING))
);
},
@@ -419,13 +422,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
let local = await this.extensionsScanner.extractUserExtension(identifierWithVersion, zipPath, token);
this.logService.info('Installation completed.', identifier.id);
if (metadata) {
- this.setMetadata(local, metadata);
- local = await this.extensionsScanner.saveMetadataForLocalExtension(local);
+ local = await this.extensionsScanner.saveMetadataForLocalExtension(local, metadata);
}
return local;
}
- private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | null): Promise<void> {
+ private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | undefined): Promise<void> {
if (this.galleryService.isEnabled()) {
const dependenciesAndPackExtensions: string[] = installed.manifest.extensionDependencies || [];
if (installed.manifest.extensionPack) {
@@ -481,15 +483,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id);
- local.metadata = metadata;
- local = await this.extensionsScanner.saveMetadataForLocalExtension(local);
+ local = await this.extensionsScanner.saveMetadataForLocalExtension(local, { ...metadata, isDefault: local.isDefault });
this.manifestCache.invalidate();
return local;
}
- private getMetadata(extensionName: string): Promise<IGalleryMetadata | null> {
+ private getGalleryMetadata(extensionName: string): Promise<IGalleryMetadata | undefined> {
return this.findGalleryExtensionByName(extensionName)
- .then(galleryExtension => galleryExtension ? <IGalleryMetadata>{ id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : null);
+ .then(galleryExtension => galleryExtension ? <IGalleryMetadata>{ id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : undefined);
}
private findGalleryExtension(local: ILocalExtension): Promise<IGalleryExtension> {
@@ -629,11 +630,6 @@ export class ExtensionManagementService extends Disposable implements IExtension
return this.extensionsScanner.scanExtensions(type);
}
- private setMetadata(local: ILocalExtension, metadata: IGalleryMetadata): void {
- local.metadata = metadata;
- local.identifier.uuid = metadata.id;
- }
-
removeDeprecatedExtensions(): Promise<void> {
return this.extensionsScanner.cleanUp();
}
diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts
index cfffb08b2ef..63c85181cc1 100644
--- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts
+++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts
@@ -32,6 +32,8 @@ const INSTALL_ERROR_EXTRACTING = 'extracting';
const INSTALL_ERROR_DELETING = 'deleting';
const INSTALL_ERROR_RENAMING = 'renaming';
+export type IMetadata = Partial<IGalleryMetadata & { isDefault: boolean; }>;
+
export class ExtensionsScanner extends Disposable {
private readonly systemExtensionsPath: string;
@@ -127,14 +129,13 @@ export class ExtensionsScanner extends Disposable {
throw new Error(localize('cannot read', "Cannot read the extension from {0}", this.extensionsPath));
}
- async saveMetadataForLocalExtension(local: ILocalExtension): Promise<ILocalExtension> {
- if (local.metadata) {
- const manifestPath = path.join(local.location.fsPath, 'package.json');
- const raw = await pfs.readFile(manifestPath, 'utf8');
- const { manifest } = await this.parseManifest(raw);
- assign(manifest, { __metadata: local.metadata });
- await pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t'));
- }
+ async saveMetadataForLocalExtension(local: ILocalExtension, metadata: IMetadata): Promise<ILocalExtension> {
+ this.setMetadata(local, metadata);
+ const manifestPath = path.join(local.location.fsPath, 'package.json');
+ const raw = await pfs.readFile(manifestPath, 'utf8');
+ const { manifest } = await this.parseManifest(raw);
+ assign(manifest, { __metadata: metadata });
+ await pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t'));
return local;
}
@@ -228,7 +229,7 @@ export class ExtensionsScanner extends Disposable {
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : null;
const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
- const local = <ILocalExtension>{ type, identifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl };
+ const local = <ILocalExtension>{ type, identifier, manifest, location: URI.file(extensionPath), readmeUrl, changelogUrl, publisherDisplayName: null, publisherId: null, isDefault: undefined };
if (metadata) {
this.setMetadata(local, metadata);
}
@@ -256,9 +257,11 @@ export class ExtensionsScanner extends Disposable {
}
}
- private setMetadata(local: ILocalExtension, metadata: IGalleryMetadata): void {
- local.metadata = metadata;
+ private setMetadata(local: ILocalExtension, metadata: IMetadata): void {
+ local.publisherDisplayName = metadata.publisherDisplayName || null;
+ local.publisherId = metadata.publisherId || null;
local.identifier.uuid = metadata.id;
+ local.isDefault = metadata.isDefault;
}
private async removeUninstalledExtensions(): Promise<void> {
@@ -314,7 +317,7 @@ export class ExtensionsScanner extends Disposable {
return this._devSystemExtensionsPath;
}
- private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> {
+ private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IMetadata | null; }> {
const promises = [
pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8')
.then(raw => this.parseManifest(raw)),
@@ -330,7 +333,7 @@ export class ExtensionsScanner extends Disposable {
};
}
- private parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> {
+ private parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IMetadata | null; }> {
return new Promise((c, e) => {
try {
const manifest = JSON.parse(raw);
diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts
index 81a5be1fe4b..fcd734b4305 100644
--- a/src/vs/platform/issue/electron-main/issueMainService.ts
+++ b/src/vs/platform/issue/electron-main/issueMainService.ts
@@ -195,7 +195,8 @@ export class IssueMainService implements ICommonIssueService {
backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR,
webPreferences: {
preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath,
- nodeIntegration: true
+ nodeIntegration: true,
+ enableWebSQL: false
}
});
@@ -245,7 +246,8 @@ export class IssueMainService implements ICommonIssueService {
title: localize('processExplorer', "Process Explorer"),
webPreferences: {
preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath,
- nodeIntegration: true
+ nodeIntegration: true,
+ enableWebSQL: false
}
});
diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts
index f81f8534749..235c3a7e78c 100644
--- a/src/vs/platform/list/browser/listService.ts
+++ b/src/vs/platform/list/browser/listService.ts
@@ -124,6 +124,7 @@ export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation';
export const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation';
const treeIndentKey = 'workbench.tree.indent';
const treeRenderIndentGuidesKey = 'workbench.tree.renderIndentGuides';
+const listSmoothScrolling = 'workbench.list.smoothScrolling';
function getHorizontalScrollingSetting(configurationService: IConfigurationService): boolean {
return getMigratedSettingValue<boolean>(configurationService, horizontalScrollingKey, 'workbench.tree.horizontalScrolling');
@@ -823,6 +824,7 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
...workbenchListOptions,
indent: configurationService.getValue<number>(treeIndentKey),
renderIndentGuides: configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey),
+ smoothScrolling: configurationService.getValue<boolean>(listSmoothScrolling),
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter',
@@ -898,25 +900,33 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
}),
configurationService.onDidChangeConfiguration(e => {
+ let options: any = {};
if (e.affectsConfiguration(openModeSettingKey)) {
- tree.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
+ options = { ...options, openOnSingleClick: useSingleClickToOpen(configurationService) };
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
if (e.affectsConfiguration(treeIndentKey)) {
const indent = configurationService.getValue<number>(treeIndentKey);
- tree.updateOptions({ indent });
+ options = { ...options, indent };
}
if (e.affectsConfiguration(treeRenderIndentGuidesKey)) {
const renderIndentGuides = configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey);
- tree.updateOptions({ renderIndentGuides });
+ options = { ...options, renderIndentGuides };
+ }
+ if (e.affectsConfiguration(listSmoothScrolling)) {
+ const smoothScrolling = configurationService.getValue<boolean>(listSmoothScrolling);
+ options = { ...options, smoothScrolling };
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
updateKeyboardNavigation();
}
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
- tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
+ options = { ...options, automaticKeyboardNavigation: getAutomaticKeyboardNavigation() };
+ }
+ if (Object.keys(options).length > 0) {
+ tree.updateOptions(options);
}
}),
this.contextKeyService.onDidChangeContext(e => {
@@ -1001,6 +1011,11 @@ configurationRegistry.registerConfiguration({
default: 'onHover',
description: localize('render tree indent guides', "Controls whether the tree should render indent guides.")
},
+ [listSmoothScrolling]: {
+ type: 'boolean',
+ default: false,
+ description: localize('list smoothScrolling setting', "Controls whether lists and trees have smooth scrolling."),
+ },
[keyboardNavigationSettingKey]: {
'type': 'string',
'enum': ['simple', 'highlight', 'filter'],
diff --git a/src/vs/platform/undoRedo/common/undoRedo.ts b/src/vs/platform/undoRedo/common/undoRedo.ts
index 3dc62c71af2..34ee024a079 100644
--- a/src/vs/platform/undoRedo/common/undoRedo.ts
+++ b/src/vs/platform/undoRedo/common/undoRedo.ts
@@ -45,9 +45,18 @@ export interface IPastFutureElements {
future: IUndoRedoElement[];
}
+export interface UriComparisonKeyComputer {
+ /**
+ * Return `null` if you don't own this URI.
+ */
+ getComparisonKey(uri: URI): string | null;
+}
+
export interface IUndoRedoService {
_serviceBrand: undefined;
+ registerUriComparisonKeyComputer(uriComparisonKeyComputer: UriComparisonKeyComputer): IDisposable;
+
/**
* Add a new element to the `undo` stack.
* This will destroy the `redo` stack.
diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts
index ddb7b3e9336..cecdce90f9b 100644
--- a/src/vs/platform/undoRedo/common/undoRedoService.ts
+++ b/src/vs/platform/undoRedo/common/undoRedoService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
-import { IUndoRedoService, IResourceUndoRedoElement, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo';
+import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, UriComparisonKeyComputer } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -12,29 +12,29 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { Schemas } from 'vs/base/common/network';
import { INotificationService } from 'vs/platform/notification/common/notification';
-import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
+import { IDisposable, Disposable, isDisposable } from 'vs/base/common/lifecycle';
-function uriGetComparisonKey(resource: URI): string {
- return resource.toString();
+function getResourceLabel(resource: URI): string {
+ return resource.scheme === Schemas.file ? resource.fsPath : resource.path;
}
class ResourceStackElement {
public readonly type = UndoRedoElementType.Resource;
- public readonly actual: IResourceUndoRedoElement;
+ public readonly actual: IUndoRedoElement;
public readonly label: string;
- public readonly resource: URI;
+ public readonly resourceLabel: string;
public readonly strResource: string;
- public readonly resources: URI[];
+ public readonly resourceLabels: string[];
public readonly strResources: string[];
public isValid: boolean;
- constructor(actual: IResourceUndoRedoElement) {
+ constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string) {
this.actual = actual;
this.label = actual.label;
- this.resource = actual.resource;
- this.strResource = uriGetComparisonKey(this.resource);
- this.resources = [this.resource];
+ this.resourceLabel = resourceLabel;
+ this.strResource = strResource;
+ this.resourceLabels = [this.resourceLabel];
this.strResources = [this.strResource];
this.isValid = true;
}
@@ -51,7 +51,7 @@ const enum RemovedResourceReason {
class ResourceReasonPair {
constructor(
- public readonly resource: URI,
+ public readonly resourceLabel: string,
public readonly reason: RemovedResourceReason
) { }
}
@@ -59,10 +59,6 @@ class ResourceReasonPair {
class RemovedResources {
private readonly elements = new Map<string, ResourceReasonPair>();
- private _getPath(resource: URI): string {
- return resource.scheme === Schemas.file ? resource.fsPath : resource.path;
- }
-
public createMessage(): string {
const externalRemoval: string[] = [];
const noParallelUniverses: string[] = [];
@@ -72,7 +68,7 @@ class RemovedResources {
? externalRemoval
: noParallelUniverses
);
- dest.push(this._getPath(element.resource));
+ dest.push(element.resourceLabel);
}
let messages: string[] = [];
@@ -107,30 +103,30 @@ class WorkspaceStackElement {
public readonly actual: IWorkspaceUndoRedoElement;
public readonly label: string;
- public readonly resources: URI[];
+ public readonly resourceLabels: string[];
public readonly strResources: string[];
public removedResources: RemovedResources | null;
public invalidatedResources: RemovedResources | null;
- constructor(actual: IWorkspaceUndoRedoElement) {
+ constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[]) {
this.actual = actual;
this.label = actual.label;
- this.resources = actual.resources.slice(0);
- this.strResources = this.resources.map(resource => uriGetComparisonKey(resource));
+ this.resourceLabels = resourceLabels;
+ this.strResources = strResources;
this.removedResources = null;
this.invalidatedResources = null;
}
- public removeResource(resource: URI, strResource: string, reason: RemovedResourceReason): void {
+ public removeResource(resourceLabel: string, strResource: string, reason: RemovedResourceReason): void {
if (!this.removedResources) {
this.removedResources = new RemovedResources();
}
if (!this.removedResources.has(strResource)) {
- this.removedResources.set(strResource, new ResourceReasonPair(resource, reason));
+ this.removedResources.set(strResource, new ResourceReasonPair(resourceLabel, reason));
}
}
- public setValid(resource: URI, strResource: string, isValid: boolean): void {
+ public setValid(resourceLabel: string, strResource: string, isValid: boolean): void {
if (isValid) {
if (this.invalidatedResources) {
this.invalidatedResources.delete(strResource);
@@ -143,7 +139,7 @@ class WorkspaceStackElement {
this.invalidatedResources = new RemovedResources();
}
if (!this.invalidatedResources.has(strResource)) {
- this.invalidatedResources.set(strResource, new ResourceReasonPair(resource, RemovedResourceReason.ExternalRemoval));
+ this.invalidatedResources.set(strResource, new ResourceReasonPair(resourceLabel, RemovedResourceReason.ExternalRemoval));
}
}
}
@@ -152,16 +148,179 @@ class WorkspaceStackElement {
type StackElement = ResourceStackElement | WorkspaceStackElement;
class ResourceEditStack {
- public resource: URI;
- public past: StackElement[];
- public future: StackElement[];
+ public readonly resourceLabel: string;
+ private readonly strResource: string;
+ private _past: StackElement[];
+ private _future: StackElement[];
public locked: boolean;
+ public versionId: number;
- constructor(resource: URI) {
- this.resource = resource;
- this.past = [];
- this.future = [];
+ constructor(resourceLabel: string, strResource: string) {
+ this.resourceLabel = resourceLabel;
+ this.strResource = strResource;
+ this._past = [];
+ this._future = [];
this.locked = false;
+ this.versionId = 1;
+ }
+
+ public dispose(): void {
+ for (const element of this._past) {
+ if (element.type === UndoRedoElementType.Workspace) {
+ element.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.ExternalRemoval);
+ }
+ }
+ for (const element of this._future) {
+ if (element.type === UndoRedoElementType.Workspace) {
+ element.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.ExternalRemoval);
+ }
+ }
+ this.versionId++;
+ }
+
+ public flushAllElements(): void {
+ this._past = [];
+ this._future = [];
+ this.versionId++;
+ }
+
+ public setElementsIsValid(isValid: boolean): void {
+ for (const element of this._past) {
+ if (element.type === UndoRedoElementType.Workspace) {
+ element.setValid(this.resourceLabel, this.strResource, isValid);
+ } else {
+ element.setValid(isValid);
+ }
+ }
+ for (const element of this._future) {
+ if (element.type === UndoRedoElementType.Workspace) {
+ element.setValid(this.resourceLabel, this.strResource, isValid);
+ } else {
+ element.setValid(isValid);
+ }
+ }
+ }
+
+ public pushElement(element: StackElement): void {
+ // remove the future
+ for (const futureElement of this._future) {
+ if (futureElement.type === UndoRedoElementType.Workspace) {
+ futureElement.removeResource(this.resourceLabel, this.strResource, RemovedResourceReason.NoParallelUniverses);
+ }
+ }
+ this._future = [];
+ if (this._past.length > 0) {
+ const lastElement = this._past[this._past.length - 1];
+ if (lastElement.type === UndoRedoElementType.Resource && !lastElement.isValid) {
+ // clear undo stack
+ this._past = [];
+ }
+ }
+ this._past.push(element);
+ this.versionId++;
+ }
+
+ public getElements(): IPastFutureElements {
+ const past: IUndoRedoElement[] = [];
+ const future: IUndoRedoElement[] = [];
+
+ for (const element of this._past) {
+ past.push(element.actual);
+ }
+ for (const element of this._future) {
+ future.push(element.actual);
+ }
+
+ return { past, future };
+ }
+
+ public getClosestPastElement(): StackElement | null {
+ if (this._past.length === 0) {
+ return null;
+ }
+ return this._past[this._past.length - 1];
+ }
+
+ public getClosestFutureElement(): StackElement | null {
+ if (this._future.length === 0) {
+ return null;
+ }
+ return this._future[this._future.length - 1];
+ }
+
+ public hasPastElements(): boolean {
+ return (this._past.length > 0);
+ }
+
+ public hasFutureElements(): boolean {
+ return (this._future.length > 0);
+ }
+
+ public splitPastWorkspaceElement(toRemove: WorkspaceStackElement, individualMap: Map<string, ResourceStackElement>): void {
+ for (let j = this._past.length - 1; j >= 0; j--) {
+ if (this._past[j] === toRemove) {
+ if (individualMap.has(this.strResource)) {
+ // gets replaced
+ this._past[j] = individualMap.get(this.strResource)!;
+ } else {
+ // gets deleted
+ this._past.splice(j, 1);
+ }
+ break;
+ }
+ }
+ this.versionId++;
+ }
+
+ public splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, individualMap: Map<string, ResourceStackElement>): void {
+ for (let j = this._future.length - 1; j >= 0; j--) {
+ if (this._future[j] === toRemove) {
+ if (individualMap.has(this.strResource)) {
+ // gets replaced
+ this._future[j] = individualMap.get(this.strResource)!;
+ } else {
+ // gets deleted
+ this._future.splice(j, 1);
+ }
+ break;
+ }
+ }
+ this.versionId++;
+ }
+
+ public moveBackward(element: StackElement): void {
+ this._past.pop();
+ this._future.push(element);
+ this.versionId++;
+ }
+
+ public moveForward(element: StackElement): void {
+ this._future.pop();
+ this._past.push(element);
+ this.versionId++;
+ }
+}
+
+class EditStackSnapshot {
+
+ public readonly editStacks: ResourceEditStack[];
+ private readonly _versionIds: number[];
+
+ constructor(editStacks: ResourceEditStack[]) {
+ this.editStacks = editStacks;
+ this._versionIds = [];
+ for (let i = 0, len = this.editStacks.length; i < len; i++) {
+ this._versionIds[i] = this.editStacks[i].versionId;
+ }
+ }
+
+ public isValid(): boolean {
+ for (let i = 0, len = this.editStacks.length; i < len; i++) {
+ if (this._versionIds[i] !== this.editStacks[i].versionId) {
+ return false;
+ }
+ }
+ return true;
}
}
@@ -169,57 +328,95 @@ export class UndoRedoService implements IUndoRedoService {
_serviceBrand: undefined;
private readonly _editStacks: Map<string, ResourceEditStack>;
+ private readonly _uriComparisonKeyComputers: UriComparisonKeyComputer[];
constructor(
@IDialogService private readonly _dialogService: IDialogService,
@INotificationService private readonly _notificationService: INotificationService,
) {
this._editStacks = new Map<string, ResourceEditStack>();
+ this._uriComparisonKeyComputers = [];
}
- public pushElement(_element: IUndoRedoElement): void {
- const element: StackElement = (_element.type === UndoRedoElementType.Resource ? new ResourceStackElement(_element) : new WorkspaceStackElement(_element));
- for (let i = 0, len = element.resources.length; i < len; i++) {
- const resource = element.resources[i];
+ public registerUriComparisonKeyComputer(uriComparisonKeyComputer: UriComparisonKeyComputer): IDisposable {
+ this._uriComparisonKeyComputers.push(uriComparisonKeyComputer);
+ return {
+ dispose: () => {
+ for (let i = 0, len = this._uriComparisonKeyComputers.length; i < len; i++) {
+ if (this._uriComparisonKeyComputers[i] === uriComparisonKeyComputer) {
+ this._uriComparisonKeyComputers.splice(i, 1);
+ return;
+ }
+ }
+ }
+ };
+ }
+
+ private _uriGetComparisonKey(resource: URI): string {
+ for (const uriComparisonKeyComputer of this._uriComparisonKeyComputers) {
+ const result = uriComparisonKeyComputer.getComparisonKey(resource);
+ if (result !== null) {
+ return result;
+ }
+ }
+ return resource.toString();
+ }
+
+ public pushElement(element: IUndoRedoElement): void {
+ if (element.type === UndoRedoElementType.Resource) {
+ const resourceLabel = getResourceLabel(element.resource);
+ const strResource = this._uriGetComparisonKey(element.resource);
+ this._pushElement(new ResourceStackElement(element, resourceLabel, strResource));
+ } else {
+ const seen = new Set<string>();
+ const resourceLabels: string[] = [];
+ const strResources: string[] = [];
+ for (const resource of element.resources) {
+ const resourceLabel = getResourceLabel(resource);
+ const strResource = this._uriGetComparisonKey(resource);
+
+ if (seen.has(strResource)) {
+ continue;
+ }
+ seen.add(strResource);
+ resourceLabels.push(resourceLabel);
+ strResources.push(strResource);
+ }
+
+ if (resourceLabels.length === 1) {
+ this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0]));
+ } else {
+ this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources));
+ }
+ }
+ }
+
+ private _pushElement(element: StackElement): void {
+ for (let i = 0, len = element.strResources.length; i < len; i++) {
+ const resourceLabel = element.resourceLabels[i];
const strResource = element.strResources[i];
let editStack: ResourceEditStack;
if (this._editStacks.has(strResource)) {
editStack = this._editStacks.get(strResource)!;
} else {
- editStack = new ResourceEditStack(resource);
+ editStack = new ResourceEditStack(resourceLabel, strResource);
this._editStacks.set(strResource, editStack);
}
- // remove the future
- for (const futureElement of editStack.future) {
- if (futureElement.type === UndoRedoElementType.Workspace) {
- futureElement.removeResource(resource, strResource, RemovedResourceReason.NoParallelUniverses);
- }
- }
- editStack.future = [];
- if (editStack.past.length > 0) {
- const lastElement = editStack.past[editStack.past.length - 1];
- if (lastElement.type === UndoRedoElementType.Resource && !lastElement.isValid) {
- // clear undo stack
- editStack.past = [];
- }
- }
- editStack.past.push(element);
+ editStack.pushElement(element);
}
}
public getLastElement(resource: URI): IUndoRedoElement | null {
- const strResource = uriGetComparisonKey(resource);
+ const strResource = this._uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
- if (editStack.future.length > 0) {
+ if (editStack.hasFutureElements()) {
return null;
}
- if (editStack.past.length === 0) {
- return null;
- }
- return editStack.past[editStack.past.length - 1].actual;
+ const closestPastElement = editStack.getClosestPastElement();
+ return closestPastElement ? closestPastElement.actual : null;
}
return null;
}
@@ -228,7 +425,9 @@ export class UndoRedoService implements IUndoRedoService {
const individualArr = toRemove.actual.split();
const individualMap = new Map<string, ResourceStackElement>();
for (const _element of individualArr) {
- const element = new ResourceStackElement(_element);
+ const resourceLabel = getResourceLabel(_element.resource);
+ const strResource = this._uriGetComparisonKey(_element.resource);
+ const element = new ResourceStackElement(_element, resourceLabel, strResource);
individualMap.set(element.strResource, element);
}
@@ -237,18 +436,7 @@ export class UndoRedoService implements IUndoRedoService {
continue;
}
const editStack = this._editStacks.get(strResource)!;
- for (let j = editStack.past.length - 1; j >= 0; j--) {
- if (editStack.past[j] === toRemove) {
- if (individualMap.has(strResource)) {
- // gets replaced
- editStack.past[j] = individualMap.get(strResource)!;
- } else {
- // gets deleted
- editStack.past.splice(j, 1);
- }
- break;
- }
- }
+ editStack.splitPastWorkspaceElement(toRemove, individualMap);
}
}
@@ -256,7 +444,9 @@ export class UndoRedoService implements IUndoRedoService {
const individualArr = toRemove.actual.split();
const individualMap = new Map<string, ResourceStackElement>();
for (const _element of individualArr) {
- const element = new ResourceStackElement(_element);
+ const resourceLabel = getResourceLabel(_element.resource);
+ const strResource = this._uriGetComparisonKey(_element.resource);
+ const element = new ResourceStackElement(_element, resourceLabel, strResource);
individualMap.set(element.strResource, element);
}
@@ -265,94 +455,52 @@ export class UndoRedoService implements IUndoRedoService {
continue;
}
const editStack = this._editStacks.get(strResource)!;
- for (let j = editStack.future.length - 1; j >= 0; j--) {
- if (editStack.future[j] === toRemove) {
- if (individualMap.has(strResource)) {
- // gets replaced
- editStack.future[j] = individualMap.get(strResource)!;
- } else {
- // gets deleted
- editStack.future.splice(j, 1);
- }
- break;
- }
- }
+ editStack.splitFutureWorkspaceElement(toRemove, individualMap);
}
}
- public removeElements(resource: URI): void {
- const strResource = uriGetComparisonKey(resource);
+ public removeElements(resource: URI | string): void {
+ const strResource = typeof resource === 'string' ? resource : this._uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
- for (const element of editStack.past) {
- if (element.type === UndoRedoElementType.Workspace) {
- element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval);
- }
- }
- for (const element of editStack.future) {
- if (element.type === UndoRedoElementType.Workspace) {
- element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval);
- }
- }
+ editStack.dispose();
this._editStacks.delete(strResource);
}
}
public setElementsIsValid(resource: URI, isValid: boolean): void {
- const strResource = uriGetComparisonKey(resource);
+ const strResource = this._uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
- for (const element of editStack.past) {
- if (element.type === UndoRedoElementType.Workspace) {
- element.setValid(resource, strResource, isValid);
- } else {
- element.setValid(isValid);
- }
- }
- for (const element of editStack.future) {
- if (element.type === UndoRedoElementType.Workspace) {
- element.setValid(resource, strResource, isValid);
- } else {
- element.setValid(isValid);
- }
- }
+ editStack.setElementsIsValid(isValid);
}
}
// resource
public hasElements(resource: URI): boolean {
- const strResource = uriGetComparisonKey(resource);
+ const strResource = this._uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
- return (editStack.past.length > 0 || editStack.future.length > 0);
+ return (editStack.hasPastElements() || editStack.hasFutureElements());
}
return false;
}
public getElements(resource: URI): IPastFutureElements {
- const past: IUndoRedoElement[] = [];
- const future: IUndoRedoElement[] = [];
-
- const strResource = uriGetComparisonKey(resource);
+ const strResource = this._uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
- for (const element of editStack.past) {
- past.push(element.actual);
- }
- for (const element of editStack.future) {
- future.push(element.actual);
- }
+ return editStack.getElements();
}
-
- return { past, future };
+ return { past: [], future: [] };
}
public canUndo(resource: URI): boolean {
- const strResource = uriGetComparisonKey(resource);
+ const strResource = this._uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
- return (editStack.past.length > 0);
+ return editStack.hasPastElements();
}
return false;
}
@@ -360,35 +508,35 @@ export class UndoRedoService implements IUndoRedoService {
private _onError(err: Error, element: StackElement): void {
onUnexpectedError(err);
// An error occured while undoing or redoing => drop the undo/redo stack for all affected resources
- for (const resource of element.resources) {
- this.removeElements(resource);
+ for (const strResource of element.strResources) {
+ this.removeElements(strResource);
}
this._notificationService.error(err);
}
- private _acquireLocks(affectedEditStacks: ResourceEditStack[]): () => void {
+ private _acquireLocks(editStackSnapshot: EditStackSnapshot): () => void {
// first, check if all locks can be acquired
- for (const editStack of affectedEditStacks) {
+ for (const editStack of editStackSnapshot.editStacks) {
if (editStack.locked) {
throw new Error('Cannot acquire edit stack lock');
}
}
// can acquire all locks
- for (const editStack of affectedEditStacks) {
+ for (const editStack of editStackSnapshot.editStacks) {
editStack.locked = true;
}
return () => {
// release all locks
- for (const editStack of affectedEditStacks) {
+ for (const editStack of editStackSnapshot.editStacks) {
editStack.locked = false;
}
};
}
- private _safeInvokeWithLocks(element: StackElement, invoke: () => Promise<void> | void, affectedEditStacks: ResourceEditStack[], cleanup: IDisposable = Disposable.None): Promise<void> | void {
- const releaseLocks = this._acquireLocks(affectedEditStacks);
+ private _safeInvokeWithLocks(element: StackElement, invoke: () => Promise<void> | void, editStackSnapshot: EditStackSnapshot, cleanup: IDisposable = Disposable.None): Promise<void> | void {
+ const releaseLocks = this._acquireLocks(editStackSnapshot);
let result: Promise<void> | void;
try {
@@ -419,7 +567,7 @@ export class UndoRedoService implements IUndoRedoService {
}
}
- private async _invokePrepare(element: WorkspaceStackElement): Promise<IDisposable> {
+ private async _invokeWorkspacePrepare(element: WorkspaceStackElement): Promise<IDisposable> {
if (typeof element.actual.prepareUndoRedo === 'undefined') {
return Disposable.None;
}
@@ -430,75 +578,106 @@ export class UndoRedoService implements IUndoRedoService {
return result;
}
- private _getAffectedEditStacks(element: WorkspaceStackElement): ResourceEditStack[] {
+ private _invokeResourcePrepare(element: ResourceStackElement, callback: (disposable: IDisposable) => void): void | Promise<void> {
+ if (element.actual.type !== UndoRedoElementType.Workspace || typeof element.actual.prepareUndoRedo === 'undefined') {
+ // no preparation needed
+ callback(Disposable.None);
+ return;
+ }
+
+ const r = element.actual.prepareUndoRedo();
+ if (!r) {
+ // nothing to clean up
+ callback(Disposable.None);
+ return;
+ }
+
+ if (isDisposable(r)) {
+ callback(r);
+ return;
+ }
+
+ return r.then((disposable) => {
+ callback(disposable);
+ });
+ }
+
+ private _getAffectedEditStacks(element: WorkspaceStackElement): EditStackSnapshot {
const affectedEditStacks: ResourceEditStack[] = [];
for (const strResource of element.strResources) {
affectedEditStacks.push(this._editStacks.get(strResource)!);
}
- return affectedEditStacks;
+ return new EditStackSnapshot(affectedEditStacks);
}
- private _checkWorkspaceUndo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[], checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
+ private _checkWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
this._splitPastWorkspaceElement(element, element.removedResources);
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.undo(resource));
+ return new WorkspaceVerificationError(this.undo(strResource));
}
if (checkInvalidatedResources && element.invalidatedResources) {
this._splitPastWorkspaceElement(element, element.invalidatedResources);
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage());
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.undo(resource));
+ return new WorkspaceVerificationError(this.undo(strResource));
}
// this must be the last past element in all the impacted resources!
- const cannotUndoDueToResources: URI[] = [];
- for (const editStack of affectedEditStacks) {
- if (editStack.past.length === 0 || editStack.past[editStack.past.length - 1] !== element) {
- cannotUndoDueToResources.push(editStack.resource);
+ const cannotUndoDueToResources: string[] = [];
+ for (const editStack of editStackSnapshot.editStacks) {
+ if (editStack.getClosestPastElement() !== element) {
+ cannotUndoDueToResources.push(editStack.resourceLabel);
}
}
if (cannotUndoDueToResources.length > 0) {
this._splitPastWorkspaceElement(element, null);
- const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
- const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
+ const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, cannotUndoDueToResources.join(', '));
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.undo(resource));
+ return new WorkspaceVerificationError(this.undo(strResource));
}
- const cannotLockDueToResources: URI[] = [];
- for (const editStack of affectedEditStacks) {
+ const cannotLockDueToResources: string[] = [];
+ for (const editStack of editStackSnapshot.editStacks) {
if (editStack.locked) {
- cannotLockDueToResources.push(editStack.resource);
+ cannotLockDueToResources.push(editStack.resourceLabel);
}
}
if (cannotLockDueToResources.length > 0) {
this._splitPastWorkspaceElement(element, null);
- const paths = cannotLockDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
- const message = nls.localize('cannotWorkspaceUndoDueToInProgressUndoRedo', "Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, paths.join(', '));
+ const message = nls.localize('cannotWorkspaceUndoDueToInProgressUndoRedo', "Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', '));
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.undo(resource));
+ return new WorkspaceVerificationError(this.undo(strResource));
+ }
+
+ // check if new stack elements were added in the meantime...
+ if (!editStackSnapshot.isValid()) {
+ this._splitPastWorkspaceElement(element, null);
+ const message = nls.localize('cannotWorkspaceUndoDueToInMeantimeUndoRedo', "Could not undo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label);
+ this._notificationService.info(message);
+ return new WorkspaceVerificationError(this.undo(strResource));
}
return null;
}
- private _workspaceUndo(resource: URI, element: WorkspaceStackElement): Promise<void> | void {
+ private _workspaceUndo(strResource: string, element: WorkspaceStackElement): Promise<void> | void {
const affectedEditStacks = this._getAffectedEditStacks(element);
- const verificationError = this._checkWorkspaceUndo(resource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false);
+ const verificationError = this._checkWorkspaceUndo(strResource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false);
if (verificationError) {
return verificationError.returnValue;
}
- return this._confirmAndExecuteWorkspaceUndo(resource, element, affectedEditStacks);
+ return this._confirmAndExecuteWorkspaceUndo(strResource, element, affectedEditStacks);
}
- private async _confirmAndExecuteWorkspaceUndo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[]): Promise<void> {
+ private async _confirmAndExecuteWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot): Promise<void> {
+
const result = await this._dialogService.show(
Severity.Info,
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label),
[
- nls.localize('ok', "Undo in {0} Files", affectedEditStacks.length),
+ nls.localize('ok', "Undo in {0} Files", editStackSnapshot.editStacks.length),
nls.localize('nok', "Undo this File"),
nls.localize('cancel', "Cancel"),
],
@@ -515,13 +694,13 @@ export class UndoRedoService implements IUndoRedoService {
if (result.choice === 1) {
// choice: undo this file
this._splitPastWorkspaceElement(element, null);
- return this.undo(resource);
+ return this.undo(strResource);
}
// choice: undo in all files
// At this point, it is possible that the element has been made invalid in the meantime (due to the confirmation await)
- const verificationError1 = this._checkWorkspaceUndo(resource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false);
+ const verificationError1 = this._checkWorkspaceUndo(strResource, element, editStackSnapshot, /*invalidated resources will be checked after the prepare call*/false);
if (verificationError1) {
return verificationError1.returnValue;
}
@@ -529,30 +708,28 @@ export class UndoRedoService implements IUndoRedoService {
// prepare
let cleanup: IDisposable;
try {
- cleanup = await this._invokePrepare(element);
+ cleanup = await this._invokeWorkspacePrepare(element);
} catch (err) {
return this._onError(err, element);
}
// At this point, it is possible that the element has been made invalid in the meantime (due to the prepare await)
- const verificationError2 = this._checkWorkspaceUndo(resource, element, affectedEditStacks, /*now also check that there are no more invalidated resources*/true);
+ const verificationError2 = this._checkWorkspaceUndo(strResource, element, editStackSnapshot, /*now also check that there are no more invalidated resources*/true);
if (verificationError2) {
cleanup.dispose();
return verificationError2.returnValue;
}
- for (const editStack of affectedEditStacks) {
- editStack.past.pop();
- editStack.future.push(element);
+ for (const editStack of editStackSnapshot.editStacks) {
+ editStack.moveBackward(element);
}
- return this._safeInvokeWithLocks(element, () => element.actual.undo(), affectedEditStacks, cleanup);
+ return this._safeInvokeWithLocks(element, () => element.actual.undo(), editStackSnapshot, cleanup);
}
private _resourceUndo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
if (!element.isValid) {
// invalid element => immediately flush edit stack!
- editStack.past = [];
- editStack.future = [];
+ editStack.flushAllElements();
return;
}
if (editStack.locked) {
@@ -560,122 +737,127 @@ export class UndoRedoService implements IUndoRedoService {
this._notificationService.info(message);
return;
}
- editStack.past.pop();
- editStack.future.push(element);
- return this._safeInvokeWithLocks(element, () => element.actual.undo(), [editStack]);
+ return this._invokeResourcePrepare(element, (cleanup) => {
+ editStack.moveBackward(element);
+ return this._safeInvokeWithLocks(element, () => element.actual.undo(), new EditStackSnapshot([editStack]), cleanup);
+ });
}
- public undo(resource: URI): Promise<void> | void {
- const strResource = uriGetComparisonKey(resource);
+ public undo(resource: URI | string): Promise<void> | void {
+ const strResource = typeof resource === 'string' ? resource : this._uriGetComparisonKey(resource);
if (!this._editStacks.has(strResource)) {
return;
}
const editStack = this._editStacks.get(strResource)!;
- if (editStack.past.length === 0) {
+ const element = editStack.getClosestPastElement();
+ if (!element) {
return;
}
- const element = editStack.past[editStack.past.length - 1];
if (element.type === UndoRedoElementType.Workspace) {
- return this._workspaceUndo(resource, element);
+ return this._workspaceUndo(strResource, element);
} else {
return this._resourceUndo(editStack, element);
}
}
public canRedo(resource: URI): boolean {
- const strResource = uriGetComparisonKey(resource);
+ const strResource = this._uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
- return (editStack.future.length > 0);
+ return editStack.hasFutureElements();
}
return false;
}
- private _checkWorkspaceRedo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[], checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
+ private _checkWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
this._splitFutureWorkspaceElement(element, element.removedResources);
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.redo(resource));
+ return new WorkspaceVerificationError(this.redo(strResource));
}
if (checkInvalidatedResources && element.invalidatedResources) {
this._splitFutureWorkspaceElement(element, element.invalidatedResources);
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage());
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.redo(resource));
+ return new WorkspaceVerificationError(this.redo(strResource));
}
// this must be the last future element in all the impacted resources!
- const cannotRedoDueToResources: URI[] = [];
- for (const editStack of affectedEditStacks) {
- if (editStack.future.length === 0 || editStack.future[editStack.future.length - 1] !== element) {
- cannotRedoDueToResources.push(editStack.resource);
+ const cannotRedoDueToResources: string[] = [];
+ for (const editStack of editStackSnapshot.editStacks) {
+ if (editStack.getClosestFutureElement() !== element) {
+ cannotRedoDueToResources.push(editStack.resourceLabel);
}
}
if (cannotRedoDueToResources.length > 0) {
this._splitFutureWorkspaceElement(element, null);
- const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
- const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
+ const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, cannotRedoDueToResources.join(', '));
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.redo(resource));
+ return new WorkspaceVerificationError(this.redo(strResource));
}
- const cannotLockDueToResources: URI[] = [];
- for (const editStack of affectedEditStacks) {
+ const cannotLockDueToResources: string[] = [];
+ for (const editStack of editStackSnapshot.editStacks) {
if (editStack.locked) {
- cannotLockDueToResources.push(editStack.resource);
+ cannotLockDueToResources.push(editStack.resourceLabel);
}
}
if (cannotLockDueToResources.length > 0) {
this._splitFutureWorkspaceElement(element, null);
- const paths = cannotLockDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
- const message = nls.localize('cannotWorkspaceRedoDueToInProgressUndoRedo', "Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, paths.join(', '));
+ const message = nls.localize('cannotWorkspaceRedoDueToInProgressUndoRedo', "Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', '));
+ this._notificationService.info(message);
+ return new WorkspaceVerificationError(this.redo(strResource));
+ }
+
+ // check if new stack elements were added in the meantime...
+ if (!editStackSnapshot.isValid()) {
+ this._splitPastWorkspaceElement(element, null);
+ const message = nls.localize('cannotWorkspaceRedoDueToInMeantimeUndoRedo', "Could not redo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label);
this._notificationService.info(message);
- return new WorkspaceVerificationError(this.redo(resource));
+ return new WorkspaceVerificationError(this.redo(strResource));
}
return null;
}
- private _workspaceRedo(resource: URI, element: WorkspaceStackElement): Promise<void> | void {
+ private _workspaceRedo(strResource: string, element: WorkspaceStackElement): Promise<void> | void {
const affectedEditStacks = this._getAffectedEditStacks(element);
- const verificationError = this._checkWorkspaceRedo(resource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false);
+ const verificationError = this._checkWorkspaceRedo(strResource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false);
if (verificationError) {
return verificationError.returnValue;
}
- return this._executeWorkspaceRedo(resource, element, affectedEditStacks);
+ return this._executeWorkspaceRedo(strResource, element, affectedEditStacks);
}
- private async _executeWorkspaceRedo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[]): Promise<void> {
+ private async _executeWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot): Promise<void> {
// prepare
let cleanup: IDisposable;
try {
- cleanup = await this._invokePrepare(element);
+ cleanup = await this._invokeWorkspacePrepare(element);
} catch (err) {
return this._onError(err, element);
}
// At this point, it is possible that the element has been made invalid in the meantime (due to the prepare await)
- const verificationError = this._checkWorkspaceRedo(resource, element, affectedEditStacks, /*now also check that there are no more invalidated resources*/true);
+ const verificationError = this._checkWorkspaceRedo(strResource, element, editStackSnapshot, /*now also check that there are no more invalidated resources*/true);
if (verificationError) {
cleanup.dispose();
return verificationError.returnValue;
}
- for (const editStack of affectedEditStacks) {
- editStack.future.pop();
- editStack.past.push(element);
+ for (const editStack of editStackSnapshot.editStacks) {
+ editStack.moveForward(element);
}
- return this._safeInvokeWithLocks(element, () => element.actual.redo(), affectedEditStacks, cleanup);
+ return this._safeInvokeWithLocks(element, () => element.actual.redo(), editStackSnapshot, cleanup);
}
private _resourceRedo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
if (!element.isValid) {
// invalid element => immediately flush edit stack!
- editStack.past = [];
- editStack.future = [];
+ editStack.flushAllElements();
return;
}
if (editStack.locked) {
@@ -683,25 +865,27 @@ export class UndoRedoService implements IUndoRedoService {
this._notificationService.info(message);
return;
}
- editStack.future.pop();
- editStack.past.push(element);
- return this._safeInvokeWithLocks(element, () => element.actual.redo(), [editStack]);
+
+ return this._invokeResourcePrepare(element, (cleanup) => {
+ editStack.moveForward(element);
+ return this._safeInvokeWithLocks(element, () => element.actual.redo(), new EditStackSnapshot([editStack]), cleanup);
+ });
}
- public redo(resource: URI): Promise<void> | void {
- const strResource = uriGetComparisonKey(resource);
+ public redo(resource: URI | string): Promise<void> | void {
+ const strResource = typeof resource === 'string' ? resource : this._uriGetComparisonKey(resource);
if (!this._editStacks.has(strResource)) {
return;
}
const editStack = this._editStacks.get(strResource)!;
- if (editStack.future.length === 0) {
+ const element = editStack.getClosestFutureElement();
+ if (!element) {
return;
}
- const element = editStack.future[editStack.future.length - 1];
if (element.type === UndoRedoElementType.Workspace) {
- return this._workspaceRedo(resource, element);
+ return this._workspaceRedo(strResource, element);
} else {
return this._resourceRedo(editStack, element);
}
diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts
index d8ff2bb11e5..8d78ddddd01 100644
--- a/src/vs/platform/update/electron-main/updateService.win32.ts
+++ b/src/vs/platform/update/electron-main/updateService.win32.ts
@@ -93,8 +93,8 @@ export class Win32UpdateService extends AbstractUpdateService {
protected buildUpdateFeedUrl(quality: string): string | undefined {
let platform = 'win32';
- if (process.arch === 'x64') {
- platform += '-x64';
+ if (process.arch !== 'ia32') {
+ platform += `-${process.arch}`;
}
if (getUpdateType() === UpdateType.Archive) {
diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
index 433461b6c7e..d8d5d0dd876 100644
--- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
+++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
@@ -238,7 +238,7 @@ export abstract class AbstractSynchroniser extends Disposable {
if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) {
// current version is not compatible with cloud version
this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/incompatible', { source: this.resource });
- throw new UserDataSyncError(localize('incompatible', "Cannot sync {0} as its version {1} is not compatible with cloud {2}", this.resource, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.resource);
+ throw new UserDataSyncError(localize({ key: 'incompatible', comment: ['This is an error while syncing a resource that its local version is not compatible with its remote version.'] }, "Cannot sync {0} as its local version {1} is not compatible with its remote version {2}", this.resource, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.resource);
}
try {
const status = await this.performSync(remoteUserData, lastSyncUserData);
diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
index 20d27834d3f..bdb95740b0a 100644
--- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
@@ -6,9 +6,11 @@
import { Delayer, disposableTimeout } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, toDisposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle';
-import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService, ALL_SYNC_RESOURCES } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncLogService, IUserDataSyncService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService, ALL_SYNC_RESOURCES, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { IProductService } from 'vs/platform/product/common/productService';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
type AutoSyncClassification = {
sources: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -34,26 +36,33 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
+ @IProductService private readonly productService: IProductService,
+ @IConfigurationService private readonly configurationService: IConfigurationService,
) {
super();
- this.updateEnablement();
this.syncTriggerDelayer = this._register(new Delayer<void>(0));
- this._register(Event.any(authTokenService.onDidChangeToken, userDataSyncService.onDidChangeStatus, this.userDataSyncEnablementService.onDidChangeEnablement)(() => this.updateEnablement()));
- this._register(Event.filter(this.userDataSyncEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerAutoSync([RESOURCE_ENABLEMENT_SOURCE])));
+
+ if (getUserDataSyncStore(this.productService, this.configurationService)) {
+ this.updateAutoSync();
+ this._register(Event.any(authTokenService.onDidChangeToken, this.userDataSyncEnablementService.onDidChangeEnablement)(() => this.updateAutoSync()));
+ this._register(Event.filter(this.userDataSyncEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerAutoSync([RESOURCE_ENABLEMENT_SOURCE])));
+ }
}
- private updateEnablement(): void {
+ private updateAutoSync(): void {
const { enabled, reason } = this.isAutoSyncEnabled();
if (enabled) {
if (this.autoSync.value === undefined) {
- const autoSync = new AutoSync(this.startAutoSync(), 1000 * 60 * 5 /* 5 miutes */, this.userDataSyncService, this.logService);
- autoSync.register(autoSync.onDidStartSync(() => this.lastSyncTriggerTime = new Date().getTime()));
- autoSync.register(autoSync.onDidFinishSync(e => this.onDidFinishSync(e)));
- this.autoSync.value = autoSync;
+ this.autoSync.value = new AutoSync(1000 * 60 * 5 /* 5 miutes */, this.userDataSyncService, this.logService);
+ this.autoSync.value.register(this.autoSync.value.onDidStartSync(() => this.lastSyncTriggerTime = new Date().getTime()));
+ this.autoSync.value.register(this.autoSync.value.onDidFinishSync(e => this.onDidFinishSync(e)));
+ if (this.startAutoSync()) {
+ this.autoSync.value.start();
+ }
}
} else {
if (this.autoSync.value !== undefined) {
- this.logService.trace('Auto Sync: Disabled because', reason);
+ this.logService.info('Auto Sync: Disabled because', reason);
this.autoSync.clear();
}
}
@@ -66,9 +75,6 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
if (!this.userDataSyncEnablementService.isEnabled()) {
return { enabled: false, reason: 'sync is disabled' };
}
- if (this.userDataSyncService.status === SyncStatus.Uninitialized) {
- return { enabled: false, reason: 'sync is not initialized' };
- }
if (!this.authTokenService.token) {
return { enabled: false, reason: 'token is not avaialable' };
}
@@ -145,21 +151,21 @@ class AutoSync extends Disposable {
readonly onDidFinishSync = this._onDidFinishSync.event;
constructor(
- start: boolean,
private readonly interval: number /* in milliseconds */,
private readonly userDataSyncService: IUserDataSyncService,
private readonly logService: IUserDataSyncLogService,
) {
super();
- if (start) {
- this._register(this.onDidFinishSync(() => this.waitUntilNextIntervalAndSync()));
- this._register(toDisposable(() => {
- this.logService.info('Auto Sync: Stopped');
- this.userDataSyncService.stop();
- }));
- this.logService.info('Auto Sync: Started');
- this.sync(AutoSync.INTERVAL_SYNCING);
- }
+ }
+
+ start(): void {
+ this._register(this.onDidFinishSync(() => this.waitUntilNextIntervalAndSync()));
+ this._register(toDisposable(() => {
+ this.userDataSyncService.stop();
+ this.logService.info('Auto Sync: Stopped');
+ }));
+ this.logService.info('Auto Sync: Started');
+ this.sync(AutoSync.INTERVAL_SYNCING);
}
private waitUntilNextIntervalAndSync(): void {
@@ -173,6 +179,7 @@ class AutoSync extends Disposable {
try {
await this.userDataSyncService.sync();
} catch (e) {
+ this.logService.error(e);
error = e;
}
this._onDidFinishSync.fire(error);
diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
index 344d98aa553..3778148041d 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
@@ -19,7 +19,6 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { assign } from 'vs/base/common/objects';
import { generateUuid } from 'vs/base/common/uuid';
import { isWeb } from 'vs/base/common/platform';
-import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
const USER_SESSION_ID_KEY = 'sync.user-session-id';
const MACHINE_SESSION_ID_KEY = 'sync.machine-session-id';
@@ -43,7 +42,6 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@IStorageService private readonly storageService: IStorageService,
- @ITelemetryService telemetryService: ITelemetryService,
) {
super();
this.userDataSyncStore = getUserDataSyncStore(productService, configurationService);
@@ -314,7 +312,6 @@ export class RequestsSession {
}
if (this.count >= this.limit) {
-
throw new UserDataSyncStoreError(`Too many requests. Allowed only ${this.limit} requests in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests);
}
diff --git a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts
index 04c5fa7f6e2..433968985ff 100644
--- a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts
+++ b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts
@@ -9,6 +9,8 @@ import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron
import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { IProductService } from 'vs/platform/product/common/productService';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@@ -19,8 +21,10 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IAuthenticationTokenService authTokenService: IAuthenticationTokenService,
@ITelemetryService telemetryService: ITelemetryService,
+ @IProductService productService: IProductService,
+ @IConfigurationService configurationService: IConfigurationService,
) {
- super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService);
+ super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService, productService, configurationService);
this._register(Event.debounce<string, string[]>(Event.any<string>(
Event.map(electronService.onWindowFocus, () => 'windowFocus'),
diff --git a/src/vs/platform/webview/common/mimeTypes.ts b/src/vs/platform/webview/common/mimeTypes.ts
index 0f1f583d451..f9a54488d19 100644
--- a/src/vs/platform/webview/common/mimeTypes.ts
+++ b/src/vs/platform/webview/common/mimeTypes.ts
@@ -20,7 +20,7 @@ const webviewMimeTypes = new Map([
['.xml', 'application/xml'],
]);
-export function getWebviewContentMimeType(normalizedPath: URI): string {
- const ext = extname(normalizedPath.fsPath).toLowerCase();
- return webviewMimeTypes.get(ext) || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN;
+export function getWebviewContentMimeType(resource: URI): string {
+ const ext = extname(resource.fsPath).toLowerCase();
+ return webviewMimeTypes.get(ext) || getMediaMime(resource.fsPath) || MIME_UNKNOWN;
}
diff --git a/src/vs/platform/webview/common/resourceLoader.ts b/src/vs/platform/webview/common/resourceLoader.ts
index 0873dcdd038..dd147ebc264 100644
--- a/src/vs/platform/webview/common/resourceLoader.ts
+++ b/src/vs/platform/webview/common/resourceLoader.ts
@@ -53,7 +53,8 @@ export async function loadLocalResource(
try {
const data = await fileService.readFile(resourceToLoad);
- return new WebviewResourceResponse.BufferSuccess(data.value, getWebviewContentMimeType(resourceToLoad));
+ const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime
+ return new WebviewResourceResponse.BufferSuccess(data.value, mime);
} catch (err) {
console.log(err);
return WebviewResourceResponse.Failed;
@@ -73,7 +74,8 @@ export async function loadLocalResourceStream(
try {
const contents = await fileService.readFileStream(resourceToLoad);
- return new WebviewResourceResponse.StreamSuccess(contents.value, getWebviewContentMimeType(requestUri));
+ const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime
+ return new WebviewResourceResponse.StreamSuccess(contents.value, mime);
} catch (err) {
console.log(err);
return WebviewResourceResponse.Failed;
diff --git a/src/vs/platform/webview/common/webviewManagerService.ts b/src/vs/platform/webview/common/webviewManagerService.ts
new file mode 100644
index 00000000000..5a67ccbc3d4
--- /dev/null
+++ b/src/vs/platform/webview/common/webviewManagerService.ts
@@ -0,0 +1,24 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { UriComponents } from 'vs/base/common/uri';
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
+
+export const IWebviewManagerService = createDecorator<IWebviewManagerService>('webviewManagerService');
+
+export interface IWebviewManagerService {
+ _serviceBrand: unknown;
+
+ registerWebview(id: string, metadata: RegisterWebviewMetadata): Promise<void>;
+ unregisterWebview(id: string): Promise<void>;
+ updateLocalResourceRoots(id: string, roots: UriComponents[]): Promise<void>;
+
+ setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise<void>;
+}
+
+export interface RegisterWebviewMetadata {
+ readonly extensionLocation: UriComponents | undefined;
+ readonly localResourceRoots: readonly UriComponents[];
+}
diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts
new file mode 100644
index 00000000000..93990e16fbd
--- /dev/null
+++ b/src/vs/platform/webview/electron-main/webviewMainService.ts
@@ -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.
+ *--------------------------------------------------------------------------------------------*/
+
+import { webContents } from 'electron';
+import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService';
+import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
+import { IFileService } from 'vs/platform/files/common/files';
+import { UriComponents, URI } from 'vs/base/common/uri';
+
+export class WebviewMainService implements IWebviewManagerService {
+
+ _serviceBrand: undefined;
+
+ private protocolProvider: WebviewProtocolProvider;
+
+ constructor(
+ @IFileService fileService: IFileService,
+ ) {
+ this.protocolProvider = new WebviewProtocolProvider(fileService);
+ }
+
+ public async registerWebview(id: string, metadata: RegisterWebviewMetadata): Promise<void> {
+ this.protocolProvider.registerWebview(id,
+ metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined,
+ metadata.localResourceRoots.map((x: UriComponents) => URI.from(x))
+ );
+ }
+
+ public async unregisterWebview(id: string): Promise<void> {
+ this.protocolProvider.unreigsterWebview(id);
+ }
+
+ public async updateLocalResourceRoots(id: string, roots: UriComponents[]): Promise<void> {
+ this.protocolProvider.updateLocalResourceRoots(id, roots.map((x: UriComponents) => URI.from(x)));
+ }
+
+ public async setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise<void> {
+ const contents = webContents.fromId(webContentsId);
+ if (!contents) {
+ throw new Error(`Invalid webContentsId: ${webContentsId}`);
+ }
+ if (!contents.isDestroyed()) {
+ contents.setIgnoreMenuShortcuts(enabled);
+ }
+ }
+}
diff --git a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts
index 0c62c6bee2a..171439f0975 100644
--- a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts
+++ b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts
@@ -3,25 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { ipcMain as ipc, IpcMainEvent, protocol } from 'electron';
+import { protocol } from 'electron';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
-import { URI, UriComponents } from 'vs/base/common/uri';
+import { URI } from 'vs/base/common/uri';
import { streamToNodeReadable } from 'vs/base/node/stream';
import { IFileService } from 'vs/platform/files/common/files';
import { loadLocalResourceStream, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
-export interface RegisterWebviewMetadata {
- readonly extensionLocation: URI | undefined;
- readonly localResourceRoots: readonly URI[];
-}
-
-
export class WebviewProtocolProvider extends Disposable {
private readonly webviewMetadata = new Map<string, {
readonly extensionLocation: URI | undefined;
- readonly localResourceRoots: URI[];
+ readonly localResourceRoots: readonly URI[];
}>();
constructor(
@@ -29,30 +23,6 @@ export class WebviewProtocolProvider extends Disposable {
) {
super();
- ipc.on('vscode:registerWebview', (event: IpcMainEvent, id: string, data: RegisterWebviewMetadata) => {
- this.webviewMetadata.set(id, {
- extensionLocation: data.extensionLocation ? URI.from(data.extensionLocation) : undefined,
- localResourceRoots: data.localResourceRoots.map((x: UriComponents) => URI.from(x)),
- });
-
- event.sender.send(`vscode:didRegisterWebview-${id}`);
- });
-
- ipc.on('vscode:updateWebviewLocalResourceRoots', (event: IpcMainEvent, id: string, localResourceRoots: readonly URI[]) => {
- const entry = this.webviewMetadata.get(id);
- if (entry) {
- this.webviewMetadata.set(id, {
- extensionLocation: entry.extensionLocation,
- localResourceRoots: localResourceRoots.map((x: UriComponents) => URI.from(x)),
- });
- }
- event.sender.send(`vscode:didUpdateWebviewLocalResourceRoots-${id}`);
- });
-
- ipc.on('vscode:unregisterWebview', (_event: IpcMainEvent, id: string) => {
- this.webviewMetadata.delete(id);
- });
-
protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, async (request, callback): Promise<void> => {
try {
const uri = URI.parse(request.url);
@@ -86,4 +56,22 @@ export class WebviewProtocolProvider extends Disposable {
this._register(toDisposable(() => protocol.unregisterProtocol(Schemas.vscodeWebviewResource)));
}
+
+ public registerWebview(id: string, extensionLocation: URI | undefined, localResourceRoots: readonly URI[]): void {
+ this.webviewMetadata.set(id, { extensionLocation, localResourceRoots });
+ }
+
+ public unreigsterWebview(id: string): void {
+ this.webviewMetadata.delete(id);
+ }
+
+ public updateLocalResourceRoots(id: string, localResourceRoots: readonly URI[]) {
+ const entry = this.webviewMetadata.get(id);
+ if (entry) {
+ this.webviewMetadata.set(id, {
+ extensionLocation: entry.extensionLocation,
+ localResourceRoots: localResourceRoots,
+ });
+ }
+ }
}
diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts
index 1db8f073041..f44e0d90b12 100644
--- a/src/vs/vscode.d.ts
+++ b/src/vs/vscode.d.ts
@@ -2216,7 +2216,7 @@ declare module 'vscode' {
}
/**
- * Metadata about the type of code actions that a [CodeActionProvider](#CodeActionProvider) providers.
+ * Metadata about the type of code actions that a [CodeActionProvider](#CodeActionProvider) provides.
*/
export interface CodeActionProviderMetadata {
/**
@@ -3950,9 +3950,13 @@ declare module 'vscode' {
*
* The editor will only resolve a completion item once.
*
- * *Note* that accepting a completion item will not wait for it to be resolved. Because of that [`insertText`](#CompletionItem.insertText),
- * [`additionalTextEdits`](#CompletionItem.additionalTextEdits), and [`command`](#CompletionItem.command) should not
- * be changed when resolving an item.
+ * *Note* that this function is called when completion items are already showing in the UI or when an item has been
+ * selected for insertion. Because of that, no property that changes the presentation (label, sorting, filtering etc)
+ * or the (primary) insert behaviour ([insertText](#CompletionItem.insertText)) can be changed.
+ *
+ * This function may fill in [additionalTextEdits](#CompletionItem.additionalTextEdits). However, that means an item might be
+ * inserted *before* resolving is done and in that case the editor will do a best effort to still apply those additional
+ * text edits.
*
* @param item A completion item currently active in the UI.
* @param token A cancellation token.
@@ -9589,7 +9593,7 @@ declare module 'vscode' {
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider A code action provider.
- * @param metadata Metadata about the kind of code actions the provider providers.
+ * @param metadata Metadata about the kind of code actions the provider provides.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerCodeActionsProvider(selector: DocumentSelector, provider: CodeActionProvider, metadata?: CodeActionProviderMetadata): Disposable;
diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts
index 54e67c584d3..f88f0d1ec0d 100644
--- a/src/vs/workbench/api/browser/mainThreadNotebook.ts
+++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts
@@ -8,7 +8,7 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx
import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService';
-import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, INotebookTextModelBackup, IEditor } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, INotebookTextModelBackup, IEditor, INotebookRendererInfo, IOutputRenderRequest, IOutputRenderResponse } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -29,11 +29,12 @@ export class MainThreadNotebookDocument extends Disposable {
private readonly _proxy: ExtHostNotebookShape,
public handle: number,
public viewType: string,
- public uri: URI
+ public uri: URI,
+ readonly notebookService: INotebookService
) {
super();
this._textModel = new NotebookTextModel(handle, viewType, uri);
- this._register(this._textModel.onDidModelChange(e => {
+ this._register(this._textModel.onDidModelChangeProxy(e => {
this._proxy.$acceptModelChanged(this.uri, e);
this._proxy.$acceptEditorPropertiesChanged(uri, { selections: { selections: this._textModel.selections }, metadata: null });
}));
@@ -43,14 +44,15 @@ export class MainThreadNotebookDocument extends Disposable {
}));
}
- applyEdit(modelVersionId: number, edits: ICellEditOperation[]): boolean {
- return this._textModel.applyEdit(modelVersionId, edits);
+ async applyEdit(modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean> {
+ await this.notebookService.transformEditsOutputs(this.textModel, edits);
+ return this._textModel.$applyEdit(modelVersionId, edits);
}
- updateRenderers(renderers: number[]) {
- this._textModel.updateRenderers(renderers);
+ async spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]) {
+ await this.notebookService.transformSpliceOutputs(this.textModel, splices);
+ this._textModel.$spliceNotebookCellOutputs(cellHandle, splices);
}
-
dispose() {
this._textModel.dispose();
super.dispose();
@@ -100,11 +102,6 @@ class DocumentAndEditorState {
// const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
- // return new DocumentAndEditorStateDelta(
- // documentDelta.removed, documentDelta.added,
- // editorDelta.removed, editorDelta.added,
- // oldActiveEditor, newActiveEditor
- // );
return {
addedEditors: addedAPIEditors,
removedEditors: removedAPIEditors,
@@ -125,6 +122,7 @@ class DocumentAndEditorState {
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
private readonly _notebookProviders = new Map<string, MainThreadNotebookController>();
private readonly _notebookKernels = new Map<string, MainThreadNotebookKernel>();
+ private readonly _notebookRenderers = new Map<string, MainThreadNotebookRenderer>();
private readonly _proxy: ExtHostNotebookShape;
private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
private _currentState?: DocumentAndEditorState;
@@ -203,7 +201,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
}
async addNotebookDocument(data: INotebookModelAddedData) {
- this._proxy.$acceptDocumentAndEditorsDelta({
+ await this._proxy.$acceptDocumentAndEditorsDelta({
addedDocuments: [data]
});
}
@@ -267,16 +265,18 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
// }
}
- async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise<void> {
- this._notebookService.registerNotebookRenderer(handle, extension, type, selectors, preloads.map(uri => URI.revive(uri)));
+ async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: UriComponents[]): Promise<void> {
+ const renderer = new MainThreadNotebookRenderer(this._proxy, type, extension.id, URI.revive(extension.location), selectors, preloads.map(uri => URI.revive(uri)));
+ this._notebookRenderers.set(type, renderer);
+ this._notebookService.registerNotebookRenderer(type, renderer);
}
- async $unregisterNotebookRenderer(handle: number): Promise<void> {
- this._notebookService.unregisterNotebookRenderer(handle);
+ async $unregisterNotebookRenderer(id: string): Promise<void> {
+ this._notebookService.unregisterNotebookRenderer(id);
}
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, kernel: INotebookKernelInfoDto | undefined): Promise<void> {
- let controller = new MainThreadNotebookController(this._proxy, this, viewType, kernel);
+ let controller = new MainThreadNotebookController(this._proxy, this, viewType, kernel, this._notebookService);
this._notebookProviders.set(viewType, controller);
this._notebookService.registerNotebookController(viewType, extension, controller);
return;
@@ -327,7 +327,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
let controller = this._notebookProviders.get(viewType);
- controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
+ await controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
}
async executeNotebook(viewType: string, uri: URI, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
@@ -358,7 +358,9 @@ export class MainThreadNotebookController implements IMainNotebookController {
private readonly _proxy: ExtHostNotebookShape,
private _mainThreadNotebook: MainThreadNotebooks,
private _viewType: string,
- readonly kernel: INotebookKernelInfoDto | undefined
+ readonly kernel: INotebookKernelInfoDto | undefined,
+ readonly notebookService: INotebookService,
+
) {
}
@@ -374,16 +376,15 @@ export class MainThreadNotebookController implements IMainNotebookController {
mainthreadNotebook.textModel.languages = data.languages;
mainthreadNotebook.textModel.metadata = data.metadata;
- mainthreadNotebook.textModel.applyEdit(mainthreadNotebook.textModel.versionId, [
+ mainthreadNotebook.textModel.$applyEdit(mainthreadNotebook.textModel.versionId, [
{ editType: CellEditType.Delete, count: mainthreadNotebook.textModel.cells.length, index: 0 },
{ editType: CellEditType.Insert, index: 0, cells: data.cells }
]);
- mainthreadNotebook.textModel.updateRenderers(data.renderers);
}
return mainthreadNotebook.textModel;
}
- let document = new MainThreadNotebookDocument(this._proxy, MainThreadNotebookController.documentHandle++, viewType, uri);
+ let document = new MainThreadNotebookDocument(this._proxy, MainThreadNotebookController.documentHandle++, viewType, uri, this.notebookService);
this._mapping.set(document.uri.toString(), document);
if (backup) {
@@ -391,7 +392,7 @@ export class MainThreadNotebookController implements IMainNotebookController {
document.textModel.metadata = backup.metadata;
document.textModel.languages = backup.languages;
- document.textModel.applyEdit(document.textModel.versionId, [
+ document.textModel.$applyEdit(document.textModel.versionId, [
{
editType: CellEditType.Insert,
index: 0,
@@ -399,9 +400,7 @@ export class MainThreadNotebookController implements IMainNotebookController {
}
]);
- // TODO@rebornix load renderers after reloading
-
- this._mainThreadNotebook.addNotebookDocument({
+ await this._mainThreadNotebook.addNotebookDocument({
viewType: document.viewType,
handle: document.handle,
uri: document.uri,
@@ -433,7 +432,6 @@ export class MainThreadNotebookController implements IMainNotebookController {
document.textModel.languages = data.languages;
document.textModel.metadata = data.metadata;
- document.textModel.updateRenderers(data.renderers);
if (data.cells.length) {
document.textModel.initialize(data!.cells);
@@ -472,17 +470,15 @@ export class MainThreadNotebookController implements IMainNotebookController {
let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
if (mainthreadNotebook) {
- mainthreadNotebook.updateRenderers(renderers);
- return mainthreadNotebook.applyEdit(modelVersionId, edits);
+ return await mainthreadNotebook.applyEdit(modelVersionId, edits);
}
return false;
}
- spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): void {
+ async spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
- mainthreadNotebook?.textModel.updateRenderers(renderers);
- mainthreadNotebook?.textModel.$spliceNotebookCellOutputs(cellHandle, splices);
+ await mainthreadNotebook?.spliceNotebookCellOutputs(cellHandle, splices);
}
async executeNotebook(viewType: string, uri: URI, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
@@ -522,11 +518,6 @@ export class MainThreadNotebookController implements IMainNotebookController {
document?.textModel.updateNotebookCellMetadata(handle, metadata);
}
- updateNotebookRenderers(resource: UriComponents, renderers: number[]): void {
- let document = this._mapping.get(URI.from(resource).toString());
- document?.textModel.updateRenderers(renderers);
- }
-
async executeNotebookCell(uri: URI, handle: number, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
return this._proxy.$executeNotebook(this._viewType, uri, handle, useAttachedKernel, token);
}
@@ -557,3 +548,24 @@ export class MainThreadNotebookKernel implements INotebookKernelInfo {
return this._proxy.$executeNotebook2(this.id, viewType, uri, handle, token);
}
}
+
+export class MainThreadNotebookRenderer implements INotebookRendererInfo {
+ constructor(
+ private readonly _proxy: ExtHostNotebookShape,
+ readonly id: string,
+ readonly extensionId: ExtensionIdentifier,
+ readonly extensionLocation: URI,
+ readonly selectors: INotebookMimeTypeSelector,
+ readonly preloads: URI[]
+ ) {
+
+ }
+
+ render(uri: URI, request: IOutputRenderRequest<UriComponents>): Promise<IOutputRenderResponse<UriComponents> | undefined> {
+ return this._proxy.$renderOutputs(uri, this.id, request);
+ }
+
+ render2<T>(uri: URI, request: IOutputRenderRequest<T>): Promise<IOutputRenderResponse<T> | undefined> {
+ return this._proxy.$renderOutputs2(uri, this.id, request);
+ }
+}
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index da4859056f3..2b950183241 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
-import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { INotebookMimeTypeSelector, IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, IOutputRenderRequest, IOutputRenderResponse, IRawOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
@@ -680,7 +680,7 @@ export interface ICellDto {
source: string[];
language: string;
cellKind: CellKind;
- outputs: IOutput[];
+ outputs: IProcessedOutput[];
metadata?: NotebookCellMetadata;
}
@@ -693,14 +693,14 @@ export type NotebookCellsSplice = [
export type NotebookCellOutputsSplice = [
number /* start */,
number /* delete count */,
- IOutput[]
+ IRawOutput[]
];
export interface MainThreadNotebookShape extends IDisposable {
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, kernelInfoDto: INotebookKernelInfoDto | undefined): Promise<void>;
$unregisterNotebookProvider(viewType: string): Promise<void>;
- $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise<void>;
- $unregisterNotebookRenderer(handle: number): Promise<void>;
+ $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: UriComponents[]): Promise<void>;
+ $unregisterNotebookRenderer(id: string): Promise<void>;
$registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise<void>;
$unregisterNotebookKernel(id: string): Promise<void>;
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean>;
@@ -1584,6 +1584,8 @@ export interface ExtHostNotebookShape {
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
+ $renderOutputs(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<UriComponents>): Promise<IOutputRenderResponse<UriComponents> | undefined>;
+ $renderOutputs2<T>(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<T>): Promise<IOutputRenderResponse<T> | undefined>;
$onDidReceiveMessage(editorId: string, message: any): void;
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void;
$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void;
diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts
index 13697b1e1ed..a114726c453 100644
--- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts
+++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts
@@ -857,16 +857,11 @@ class SuggestAdapter {
private _cache = new Cache<vscode.CompletionItem>('CompletionItem');
private _disposables = new Map<number, DisposableStore>();
- private _didWarnMust: boolean = false;
- private _didWarnShould: boolean = false;
-
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _provider: vscode.CompletionItemProvider,
- private readonly _logService: ILogService,
private readonly _apiDeprecation: IExtHostApiDeprecationService,
- private readonly _telemetry: extHostProtocol.MainThreadTelemetryShape,
private readonly _extension: IExtensionDescription,
) { }
@@ -930,41 +925,12 @@ class SuggestAdapter {
return undefined;
}
- const _mustNotChange = SuggestAdapter._mustNotChangeHash(item);
- const _mayNotChange = SuggestAdapter._mayNotChangeHash(item);
-
const resolvedItem = await asPromise(() => this._provider.resolveCompletionItem!(item, token));
if (!resolvedItem) {
return undefined;
}
- type BlameExtension = {
- extensionId: string;
- kind: string;
- index: string;
- };
-
- type BlameExtensionMeta = {
- extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
- kind: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
- index: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' };
- };
-
- let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem);
- if (typeof _mustNotChangeIndex === 'string') {
- this._logService.warn(`[${this._extension.identifier.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`);
- this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
- this._didWarnMust = true;
- }
-
- let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem);
- if (typeof _mayNotChangeIndex === 'string') {
- this._logService.info(`[${this._extension.identifier.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`);
- this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex });
- this._didWarnShould = true;
- }
-
return this._convertCompletionItem(resolvedItem, id);
}
@@ -1035,45 +1001,6 @@ class SuggestAdapter {
return result;
}
-
- private static _mustNotChangeHash(item: vscode.CompletionItem) {
- const res = JSON.stringify([item.label, item.sortText, item.filterText, item.insertText, item.range]);
- return res;
- }
-
- private static _mustNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void {
- const thisArr = [item.label, item.sortText, item.filterText, item.insertText, item.range];
- const thisHash = JSON.stringify(thisArr);
- if (hash === thisHash) {
- return;
- }
- const arr = JSON.parse(hash);
- for (let i = 0; i < 6; i++) {
- if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) {
- return i.toString();
- }
- }
- return 'unknown';
- }
-
- private static _mayNotChangeHash(item: vscode.CompletionItem) {
- return JSON.stringify([item.additionalTextEdits, item.command]);
- }
-
- private static _mayNotChangeDiff(hash: string, item: vscode.CompletionItem): string | void {
- const thisArr = [item.additionalTextEdits, item.command];
- const thisHash = JSON.stringify(thisArr);
- if (hash === thisHash) {
- return;
- }
- const arr = JSON.parse(hash);
- for (let i = 0; i < 6; i++) {
- if (JSON.stringify(arr[i] !== JSON.stringify(thisArr[i]))) {
- return i.toString();
- }
- }
- return 'unknown';
- }
}
class SignatureHelpAdapter {
@@ -1392,7 +1319,6 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
private readonly _uriTransformer: IURITransformer | null;
private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape;
- private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape;
private _documents: ExtHostDocuments;
private _commands: ExtHostCommands;
private _diagnostics: ExtHostDiagnostics;
@@ -1411,7 +1337,6 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
) {
this._uriTransformer = uriTransformer;
this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures);
- this._telemetryShape = mainContext.getProxy(extHostProtocol.MainContext.MainThreadTelemetry);
this._documents = documents;
this._commands = commands;
this._diagnostics = diagnostics;
@@ -1780,7 +1705,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- suggestion
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
- const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService, this._apiDeprecation, this._telemetryShape, extension), extension);
+ const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._apiDeprecation, extension), extension);
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier);
return this._createDisposable(handle);
}
diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts
index f351207b2ac..1109053d164 100644
--- a/src/vs/workbench/api/common/extHostNotebook.ts
+++ b/src/vs/workbench/api/common/extHostNotebook.ts
@@ -10,10 +10,10 @@ import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecyc
import { ISplice } from 'vs/base/common/sequence';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
-import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol';
+import { CellKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
-import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, INotebookDisplayOrder, INotebookEditData, NotebookCellsChangedEvent, NotebookCellsSplice2, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseOutputInfo, IOutputRenderResponseCellInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
@@ -261,7 +261,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
accpetModelChanged(event: NotebookCellsChangedEvent): void {
this._versionId = event.versionId;
if (event.kind === NotebookCellsChangeType.ModelChange) {
- this.$spliceNotebookCells(event.change);
+ this.$spliceNotebookCells(event.changes);
} else if (event.kind === NotebookCellsChangeType.Move) {
this.$moveCell(event.index, event.newIdx);
} else if (event.kind === NotebookCellsChangeType.CellClearOutput) {
@@ -273,50 +273,52 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
}
}
- private $spliceNotebookCells(splice: NotebookCellsSplice2): void {
+ private $spliceNotebookCells(splices: NotebookCellsSplice2[]): void {
if (this._disposed) {
return;
}
let contentChangeEvents: vscode.NotebookCellsChangeData[] = [];
- let cellDtos = splice[2];
- let newCells = cellDtos.map(cell => {
- const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy);
- const documentData = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
+ splices.reverse().forEach(splice => {
+ let cellDtos = splice[2];
+ let newCells = cellDtos.map(cell => {
+ const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy);
+ const documentData = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
- if (documentData) {
- extCell.attachTextDocument(documentData);
- }
+ if (documentData) {
+ extCell.attachTextDocument(documentData);
+ }
- if (!this._cellDisposableMapping.has(extCell.handle)) {
- this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
- }
+ if (!this._cellDisposableMapping.has(extCell.handle)) {
+ this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
+ }
- let store = this._cellDisposableMapping.get(extCell.handle)!;
+ let store = this._cellDisposableMapping.get(extCell.handle)!;
- store.add(extCell.onDidChangeOutputs((diffs) => {
- this.eventuallyUpdateCellOutputs(extCell, diffs);
- }));
+ store.add(extCell.onDidChangeOutputs((diffs) => {
+ this.eventuallyUpdateCellOutputs(extCell, diffs);
+ }));
- return extCell;
- });
+ return extCell;
+ });
- for (let j = splice[0]; j < splice[0] + splice[1]; j++) {
- this._cellDisposableMapping.get(this.cells[j].handle)?.dispose();
- this._cellDisposableMapping.delete(this.cells[j].handle);
+ for (let j = splice[0]; j < splice[0] + splice[1]; j++) {
+ this._cellDisposableMapping.get(this.cells[j].handle)?.dispose();
+ this._cellDisposableMapping.delete(this.cells[j].handle);
- }
+ }
- this.cells.splice(splice[0], splice[1], ...newCells);
+ this.cells.splice(splice[0], splice[1], ...newCells);
- const event: vscode.NotebookCellsChangeData = {
- start: splice[0],
- deletedCount: splice[1],
- items: newCells
- };
+ const event: vscode.NotebookCellsChangeData = {
+ start: splice[0],
+ deletedCount: splice[1],
+ items: newCells
+ };
- contentChangeEvents.push(event);
+ contentChangeEvents.push(event);
+ });
this._emitter.emitModelChange({
document: this,
@@ -372,21 +374,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
let renderers = new Set<number>();
let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
let outputs = diff.toInsert;
-
- let transformedOutputs = outputs.map(output => {
- if (output.outputKind === CellOutputKind.Rich) {
- const ret = this.transformMimeTypes(output);
-
- if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
- renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
- }
- return ret;
- } else {
- return output as IStreamOutput | IErrorOutput;
- }
- });
-
- return [diff.start, diff.deleteCount, transformedOutputs];
+ return [diff.start, diff.deleteCount, outputs];
});
await this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers));
@@ -396,61 +384,14 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
});
}
- transformMimeTypes(output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
- let mimeTypes = Object.keys(output.data);
- let coreDisplayOrder = this.renderingHandler.outputDisplayOrder;
- const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], this._displayOrder, coreDisplayOrder?.defaultOrder || []);
-
- let orderMimeTypes: IOrderedMimeType[] = [];
-
- sorted.forEach(mimeType => {
- let handlers = this.renderingHandler.findBestMatchedRenderer(mimeType);
-
- if (handlers.length) {
- let renderedOutput = handlers[0].render(this, output, mimeType);
-
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: true,
- rendererId: handlers[0].handle,
- output: renderedOutput
- });
-
- for (let i = 1; i < handlers.length; i++) {
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: false,
- rendererId: handlers[i].handle
- });
- }
-
- if (mimeTypeSupportedByCore(mimeType)) {
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: false,
- rendererId: -1
- });
- }
- } else {
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: false
- });
- }
- });
-
- return {
- outputKind: output.outputKind,
- data: output.data,
- orderedMimeTypes: orderMimeTypes,
- pickedMimeTypeIndex: 0
- };
- }
-
getCell(cellHandle: number) {
return this.cells.find(cell => cell.handle === cellHandle);
}
+ getCell2(cellUri: UriComponents) {
+ return this.cells.find(cell => cell.uri.fragment === cellUri.fragment);
+ }
+
attachCellTextDocument(textDocument: ExtHostDocumentData) {
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
if (cell) {
@@ -501,25 +442,10 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE
source: sourceArr,
language,
cellKind: type,
- outputs: (outputs as any[]), // TODO@rebornix
+ outputs: outputs,
metadata
};
- const transformedOutputs = outputs.map(output => {
- if (output.outputKind === CellOutputKind.Rich) {
- const ret = this.editor.document.transformMimeTypes(output);
-
- if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
- this._renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
- }
- return ret;
- } else {
- return output as IStreamOutput | IErrorOutput;
- }
- });
-
- cell.outputs = transformedOutputs;
-
this._collectedEdits.push({
editType: CellEditType.Insert,
index,
@@ -716,7 +642,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
private readonly _documents = new Map<string, ExtHostNotebookDocument>();
private readonly _unInitializedDocuments = new Map<string, ExtHostNotebookDocument>();
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any>; }>();
- private readonly _notebookOutputRenderers = new Map<number, ExtHostNotebookOutputRenderer>();
+ private readonly _notebookOutputRenderers = new Map<string, ExtHostNotebookOutputRenderer>();
private readonly _onDidChangeNotebookCells = new Emitter<vscode.NotebookCellsChangeEvent>();
readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event;
private readonly _onDidChangeCellOutputs = new Emitter<vscode.NotebookCellOutputsChangeEvent>();
@@ -781,15 +707,85 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
filter: vscode.NotebookOutputSelector,
renderer: vscode.NotebookOutputRenderer
): vscode.Disposable {
+ if (this._notebookKernels.has(type)) {
+ throw new Error(`Notebook renderer for '${type}' already registered`);
+ }
+
let extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer);
- this._notebookOutputRenderers.set(extHostRenderer.handle, extHostRenderer);
- this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation }, type, filter, extHostRenderer.handle, renderer.preloads || []);
+ this._notebookOutputRenderers.set(extHostRenderer.type, extHostRenderer);
+ this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation }, type, filter, renderer.preloads || []);
return new extHostTypes.Disposable(() => {
- this._notebookOutputRenderers.delete(extHostRenderer.handle);
- this._proxy.$unregisterNotebookRenderer(extHostRenderer.handle);
+ this._notebookOutputRenderers.delete(extHostRenderer.type);
+ this._proxy.$unregisterNotebookRenderer(extHostRenderer.type);
});
}
+ async $renderOutputs(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<UriComponents>): Promise<IOutputRenderResponse<UriComponents> | undefined> {
+ if (!this._notebookOutputRenderers.has(id)) {
+ throw new Error(`Notebook renderer for '${id}' is not registered`);
+ }
+
+ const document = this._documents.get(URI.revive(uriComponents).toString());
+
+ if (!document) {
+ return;
+ }
+
+ const renderer = this._notebookOutputRenderers.get(id)!;
+ const cellsResponse: IOutputRenderResponseCellInfo<UriComponents>[] = request.items.map(cellInfo => {
+ const cell = document.getCell2(cellInfo.key);
+ const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
+ return {
+ index: output.index,
+ mimeType: output.mimeType,
+ handlerId: id,
+ transformedOutput: renderer.render(document, cell!.outputs[output.index] as vscode.CellDisplayOutput, output.mimeType)
+ };
+ });
+
+ return {
+ key: cellInfo.key,
+ outputs: outputResponse
+ };
+ });
+
+ return { items: cellsResponse };
+ }
+
+ /**
+ * The request carry the raw data for outputs so we don't look up in the existing document
+ */
+ async $renderOutputs2<T>(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<T>): Promise<IOutputRenderResponse<T> | undefined> {
+ if (!this._notebookOutputRenderers.has(id)) {
+ throw new Error(`Notebook renderer for '${id}' is not registered`);
+ }
+
+ const document = this._documents.get(URI.revive(uriComponents).toString());
+
+ if (!document) {
+ return;
+ }
+
+ const renderer = this._notebookOutputRenderers.get(id)!;
+ const cellsResponse: IOutputRenderResponseCellInfo<T>[] = request.items.map(cellInfo => {
+ const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
+ return {
+ index: output.index,
+ mimeType: output.mimeType,
+ handlerId: id,
+ transformedOutput: renderer.render(document, output.output! as vscode.CellDisplayOutput, output.mimeType)
+ };
+ });
+
+ return {
+ key: cellInfo.key,
+ outputs: outputResponse
+ };
+ });
+
+ return { items: cellsResponse };
+ }
+
findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[] {
let matches: ExtHostNotebookOutputRenderer[] = [];
for (let renderer of this._notebookOutputRenderers) {
@@ -862,97 +858,21 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
const rawCells = await provider.provider.openNotebook(URI.revive(uri));
- const renderers = new Set<number>();
const dto = {
metadata: {
...notebookDocumentMetadataDefaults,
...rawCells.metadata
},
languages: rawCells.languages,
- cells: rawCells.cells.map(cell => {
- let transformedOutputs = cell.outputs.map(output => {
- if (output.outputKind === CellOutputKind.Rich) {
- // TODO display string[]
- const ret = this._transformMimeTypes(document!, (rawCells.metadata.displayOrder as string[]) || [], output);
-
- if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
- renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
- }
- return ret;
- } else {
- return output as IStreamOutput | IErrorOutput;
- }
- });
-
- return {
- language: cell.language,
- cellKind: cell.cellKind,
- metadata: cell.metadata,
- source: cell.source,
- outputs: transformedOutputs
- };
- }),
- renderers: [] as number[]
+ cells: rawCells.cells,
};
- dto.renderers = [...renderers];
return dto;
}
return;
}
- private _transformMimeTypes(document: ExtHostNotebookDocument, displayOrder: string[], output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
- let mimeTypes = Object.keys(output.data);
- let coreDisplayOrder = this.outputDisplayOrder;
- const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], displayOrder, coreDisplayOrder?.defaultOrder || []);
-
- let orderMimeTypes: IOrderedMimeType[] = [];
-
- sorted.forEach(mimeType => {
- let handlers = this.findBestMatchedRenderer(mimeType);
-
- if (handlers.length) {
- let renderedOutput = handlers[0].render(document, output, mimeType);
-
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: true,
- rendererId: handlers[0].handle,
- output: renderedOutput
- });
-
- for (let i = 1; i < handlers.length; i++) {
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: false,
- rendererId: handlers[i].handle
- });
- }
-
- if (mimeTypeSupportedByCore(mimeType)) {
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: false,
- rendererId: -1
- });
- }
- } else {
- orderMimeTypes.push({
- mimeType: mimeType,
- isResolved: false
- });
- }
- });
-
- return {
- outputKind: output.outputKind,
- data: output.data,
- orderedMimeTypes: orderMimeTypes,
- pickedMimeTypeIndex: 0
- };
- }
-
async $executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
let document = this._documents.get(URI.revive(uri).toString());
@@ -1178,11 +1098,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
document.accpetModelChanged({
kind: NotebookCellsChangeType.ModelChange,
versionId: modelData.versionId,
- change: [
+ changes: [[
0,
0,
modelData.cells
- ]
+ ]]
});
this._documents.get(revivedUriStr)?.dispose();
diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts
index d491ecb4915..fece3ecd26f 100644
--- a/src/vs/workbench/browser/parts/compositeBar.ts
+++ b/src/vs/workbench/browser/parts/compositeBar.ts
@@ -682,7 +682,7 @@ class CompositeBarModel {
private updateItemsOrder(): void {
if (this._items) {
- this.items.forEach((item, index) => item.order = index);
+ this.items.forEach((item, index) => { if (item.order !== undefined) { item.order = index; } });
}
}
diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css
index ac6dc47d50f..90b596924ec 100644
--- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css
+++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css
@@ -29,7 +29,7 @@
/* Title Actions */
.monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions .action-label,
-.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label {
+.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-label:not(span) {
display: flex;
height: 35px;
min-width: 28px;
diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
index dde922f9908..6f17fd71773 100644
--- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
+++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
@@ -9,7 +9,6 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
import { dispose, IDisposable, Disposable, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
import { ICommandService } from 'vs/platform/commands/common/commands';
-import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Part } from 'vs/workbench/browser/part';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -726,7 +725,6 @@ class StatusbarEntryItem extends Disposable {
@ICommandService private readonly commandService: ICommandService,
@INotificationService private readonly notificationService: INotificationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
- @IEditorService private readonly editorService: IEditorService,
@IThemeService private readonly themeService: IThemeService
) {
super();
@@ -829,12 +827,6 @@ class StatusbarEntryItem extends Disposable {
const id = typeof command === 'string' ? command : command.id;
const args = typeof command === 'string' ? [] : command.arguments ?? [];
- // Maintain old behaviour of always focusing the editor here
- const activeTextEditorControl = this.editorService.activeTextEditorControl;
- if (activeTextEditorControl) {
- activeTextEditorControl.focus();
- }
-
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id, from: 'status bar' });
try {
await this.commandService.executeCommand(id, ...args);
diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts
index ad62952a45e..77247227d33 100644
--- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts
+++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts
@@ -49,7 +49,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { URI } from 'vs/base/common/uri';
-import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
+import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
export interface IPaneColors extends IColorMapping {
@@ -1580,8 +1580,8 @@ registerAction2(
id: 'views.moveViewUp',
title: nls.localize('viewMoveUp', "Move View Up"),
keybinding: {
- primary: KeyMod.Shift + KeyMod.Alt + KeyCode.UpArrow,
- weight: KeybindingWeight.WorkbenchContrib,
+ primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.UpArrow),
+ weight: KeybindingWeight.WorkbenchContrib + 1,
when: FocusedViewContext.notEqualsTo('')
}
}, -1);
@@ -1596,8 +1596,8 @@ registerAction2(
id: 'views.moveViewLeft',
title: nls.localize('viewMoveLeft', "Move View Left"),
keybinding: {
- primary: KeyMod.Shift + KeyMod.Alt + KeyCode.LeftArrow,
- weight: KeybindingWeight.WorkbenchContrib,
+ primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.LeftArrow),
+ weight: KeybindingWeight.WorkbenchContrib + 1,
when: FocusedViewContext.notEqualsTo('')
}
}, -1);
@@ -1612,8 +1612,8 @@ registerAction2(
id: 'views.moveViewDown',
title: nls.localize('viewMoveDown', "Move View Down"),
keybinding: {
- primary: KeyMod.Shift + KeyMod.Alt + KeyCode.DownArrow,
- weight: KeybindingWeight.WorkbenchContrib,
+ primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.DownArrow),
+ weight: KeybindingWeight.WorkbenchContrib + 1,
when: FocusedViewContext.notEqualsTo('')
}
}, 1);
@@ -1628,8 +1628,8 @@ registerAction2(
id: 'views.moveViewRight',
title: nls.localize('viewMoveRight', "Move View Right"),
keybinding: {
- primary: KeyMod.Shift + KeyMod.Alt + KeyCode.RightArrow,
- weight: KeybindingWeight.WorkbenchContrib,
+ primary: KeyChord(KeyMod.CtrlCmd + KeyCode.KEY_K, KeyCode.RightArrow),
+ weight: KeybindingWeight.WorkbenchContrib + 1,
when: FocusedViewContext.notEqualsTo('')
}
}, 1);
diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts
index b3df4f720d6..fc20b6d386c 100644
--- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts
+++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts
@@ -56,6 +56,7 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl
import { StartDebugQuickAccessProvider } from 'vs/workbench/contrib/debug/browser/debugQuickAccess';
import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/debugProgress';
import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle';
+import { Codicon } from 'vs/base/common/codicons';
class OpenDebugViewletAction extends ShowViewletAction {
public static readonly ID = VIEWLET_ID;
@@ -76,7 +77,7 @@ const viewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewCo
id: VIEWLET_ID,
name: nls.localize('run', "Run"),
ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer),
- icon: 'codicon-debug-alt-2',
+ icon: Codicon.debugAlt.classNames,
alwaysUseContainerInfo: true,
order: 2
}, ViewContainerLocation.Sidebar);
@@ -93,7 +94,7 @@ const openPanelKb: IKeybindings = {
const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
id: DEBUG_PANEL_ID,
name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'),
- icon: 'codicon-debug-console',
+ icon: Codicon.debugConsole.classNames,
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
storageId: DEBUG_PANEL_ID,
focusCommand: {
@@ -107,7 +108,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewE
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([{
id: REPL_VIEW_ID,
name: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'),
- containerIcon: 'codicon-debug-console',
+ containerIcon: Codicon.debugConsole.classNames,
canToggleVisibility: false,
canMoveView: true,
ctorDescriptor: new SyncDescriptor(Repl),
@@ -115,12 +116,12 @@ Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([{
// Register default debug views
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
-viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
-viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
-viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
-viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
-viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
-viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: 'codicon-debug-alt-2', ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
+viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(VariablesView), order: 10, weight: 40, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
+viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
+viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
+viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
+viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
+viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), containerIcon: Codicon.debugAlt.classNames, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
registerCommands();
diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts
index ea288746ac5..29f0c58c756 100644
--- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts
+++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts
@@ -94,8 +94,8 @@ class Extension implements IExtension {
return this.gallery.publisherDisplayName || this.gallery.publisher;
}
- if (this.local!.metadata && this.local!.metadata.publisherDisplayName) {
- return this.local!.metadata.publisherDisplayName;
+ if (this.local?.publisherDisplayName) {
+ return this.local.publisherDisplayName;
}
return this.local!.manifest.publisher;
@@ -367,7 +367,7 @@ class Extensions extends Disposable {
}
// Sync the local extension with gallery extension if local extension doesnot has metadata
if (extension.local) {
- const local = extension.local.metadata ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId });
+ const local = extension.local.identifier.uuid ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId });
extension.local = local;
extension.gallery = compatible;
this._onChange.fire({ extension });
diff --git a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts
index 84c47be3064..3ce9d79e586 100644
--- a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts
+++ b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts
@@ -30,7 +30,7 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC
disposable = MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: 'workbench.extensions.installLocalExtensions',
- category: localize('remote', "Remote"),
+ category: localize({ key: 'remote', comment: ['Remote as in remote machine'] }, "Remote"),
title: installLocalExtensionsInRemoteAction.label
}
});
diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts
index 7d55bdfcc36..5ac7acf6add 100644
--- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts
+++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts
@@ -11,11 +11,10 @@ import * as ExtensionsActions from 'vs/workbench/contrib/extensions/browser/exte
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension,
- DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService
+ DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
-import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';
@@ -82,11 +81,21 @@ async function setupTest() {
instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService);
instantiationService.stub(ISharedProcessService, TestSharedProcessService);
- instantiationService.stub(IExtensionManagementService, ExtensionManagementService);
- instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event);
- instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event);
- instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event);
- instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
+ instantiationService.stub(IExtensionManagementService, <Partial<IExtensionManagementService>>{
+ onInstallExtension: installEvent.event,
+ onDidInstallExtension: didInstallEvent.event,
+ onUninstallExtension: uninstallEvent.event,
+ onDidUninstallExtension: didUninstallEvent.event,
+ async getInstalled() { return []; },
+ async getExtensionsReport() { return []; },
+ async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) {
+ local.identifier.uuid = metadata.id;
+ local.publisherDisplayName = metadata.publisherDisplayName;
+ local.publisherId = metadata.publisherId;
+ return local;
+ }
+ });
+
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IExtensionManagementServerService, new class extends ExtensionManagementServerService {
@@ -2491,8 +2500,7 @@ function aLocalExtension(name: string = 'someext', manifest: any = {}, propertie
properties = assign({
type: ExtensionType.User,
location: URI.file(`pub.${name}`),
- identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined },
- metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' }
+ identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) }
}, properties);
return <ILocalExtension>Object.create({ manifest, ...properties });
}
diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts
index f7d70b79416..82da373c436 100644
--- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts
+++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts
@@ -12,11 +12,10 @@ import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurat
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension,
- DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService
+ DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
-import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IURLService } from 'vs/platform/url/common/url';
@@ -85,11 +84,20 @@ suite('ExtensionsWorkbenchServiceTest', () => {
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
- instantiationService.stub(IExtensionManagementService, ExtensionManagementService);
- instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event);
- instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event);
- instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event);
- instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
+ instantiationService.stub(IExtensionManagementService, <Partial<IExtensionManagementService>>{
+ onInstallExtension: installEvent.event,
+ onDidInstallExtension: didInstallEvent.event,
+ onUninstallExtension: uninstallEvent.event,
+ onDidUninstallExtension: didUninstallEvent.event,
+ async getInstalled() { return []; },
+ async getExtensionsReport() { return []; },
+ async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) {
+ local.identifier.uuid = metadata.id;
+ local.publisherDisplayName = metadata.publisherDisplayName;
+ local.publisherId = metadata.publisherId;
+ return local;
+ }
+ });
instantiationService.stub(IExtensionManagementServerService, <IExtensionManagementServerService>{
localExtensionManagementServer: {
@@ -109,7 +117,6 @@ suite('ExtensionsWorkbenchServiceTest', () => {
setup(async () => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []);
- instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());
instantiationService.stubPromise(INotificationService, 'prompt', 0);
await (<TestExtensionEnablementService>instantiationService.get(IWorkbenchExtensionEnablementService)).reset();
@@ -985,8 +992,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
properties = assign({
type: ExtensionType.User,
location: URI.file(`pub.${name}`),
- identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined },
- metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' }
+ identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) }
}, properties);
return <ILocalExtension>Object.create({ manifest, ...properties });
}
diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css
index d87789106e6..b5570494b91 100644
--- a/src/vs/workbench/contrib/markers/browser/media/markers.css
+++ b/src/vs/workbench/contrib/markers/browser/media/markers.css
@@ -22,8 +22,6 @@
.monaco-workbench.vs .monaco-action-bar .markers-panel-action-filter .monaco-inputbox {
height: 25px;
- border-width: 1px;
- border-style: solid;
}
.markers-panel-action-filter > .markers-panel-filter-controls {
diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts
index a4365fda7b3..b51074310b4 100644
--- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts
+++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts
@@ -55,6 +55,7 @@ import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransfo
import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget';
import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
+import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
/*--------------------------------------------------------------------------------------------- */
@@ -118,11 +119,33 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
@IEditorService private readonly editorService: EditorServiceImpl,
@INotebookService private readonly notebookService: INotebookService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
- @IConfigurationService private readonly configurationService: IConfigurationService
-
+ @IConfigurationService private readonly configurationService: IConfigurationService,
+ @IUndoRedoService undoRedoService: IUndoRedoService
) {
super();
+ this._register(undoRedoService.registerUriComparisonKeyComputer({
+ getComparisonKey: (uri: URI): string | null => {
+ if (uri.scheme !== CellUri.scheme) {
+ return null;
+ }
+
+ const data = CellUri.parse(uri);
+ if (!data) {
+ return null;
+ }
+
+ return data.notebook.scheme + ':' + data.notebook.fsPath;
+
+ // const documentUri = this._resourceMapping.get(data.notebook)?.resource;
+ // if (documentUri) {
+ // return documentUri.toString();
+ // }
+
+ // return null;
+ }
+ }));
+
this._register(this.editorService.overrideOpenEditor({
getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) => {
const currentEditorForResource = group?.editors.find(editor => isEqual(editor.resource, resource));
diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
index 0ced34f4a94..0a6cb18fab2 100644
--- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
+++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
@@ -23,7 +23,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu
import { CellLanguageStatusBarItem, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
-import { CellKind, IOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernelInfo, IEditor } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { CellKind, IProcessedOutput, IRenderOutput, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernelInfo, IEditor } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
@@ -270,12 +270,12 @@ export interface INotebookEditor extends IEditor {
/**
* Render the output in webview layer
*/
- createInset(cell: ICellViewModel, output: IOutput, shadowContent: string, offset: number): void;
+ createInset(cell: ICellViewModel, output: IProcessedOutput, shadowContent: string, offset: number): void;
/**
* Remove the output from the webview layer
*/
- removeInset(output: IOutput): void;
+ removeInset(output: IProcessedOutput): void;
/**
* Send message to the webview for outputs.
@@ -377,8 +377,8 @@ export interface INotebookCellList {
scrollLeft: number;
length: number;
rowsContainer: HTMLElement;
- readonly onDidRemoveOutput: Event<IOutput>;
- readonly onDidHideOutput: Event<IOutput>;
+ readonly onDidRemoveOutput: Event<IProcessedOutput>;
+ readonly onDidHideOutput: Event<IProcessedOutput>;
readonly onMouseUp: Event<IListMouseEvent<CellViewModel>>;
readonly onMouseDown: Event<IListMouseEvent<CellViewModel>>;
detachViewModel(): void;
@@ -456,7 +456,7 @@ export interface IOutputTransformContribution {
*/
dispose(): void;
- render(output: IOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput;
+ render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput;
}
export interface CellFindMatch {
diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
index bf3aaf47ab0..baac0986c7f 100644
--- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
+++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
@@ -39,7 +39,7 @@ import { CellDragAndDropController, CodeCellRenderer, MarkdownCellRenderer, Note
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
-import { CellKind, IOutput, INotebookKernelInfo, INotebookKernelInfoDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { CellKind, IProcessedOutput, INotebookKernelInfo, INotebookKernelInfoDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils';
@@ -512,8 +512,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this.webview!.element.style.height = `${scrollHeight}px`;
if (this.webview?.insetMapping) {
- let updateItems: { cell: CodeCellViewModel, output: IOutput, cellTop: number }[] = [];
- let removedItems: IOutput[] = [];
+ let updateItems: { cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number }[] = [];
+ let removedItems: IProcessedOutput[] = [];
this.webview?.insetMapping.forEach((value, key) => {
const cell = value.cell;
const viewIndex = this.list?.getViewIndex(cell);
@@ -1224,7 +1224,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this.list?.triggerScrollFromMouseWheelEvent(event);
}
- createInset(cell: CodeCellViewModel, output: IOutput, shadowContent: string, offset: number) {
+ createInset(cell: CodeCellViewModel, output: IProcessedOutput, shadowContent: string, offset: number) {
if (!this.webview) {
return;
}
@@ -1242,7 +1242,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
}
- removeInset(output: IOutput) {
+ removeInset(output: IProcessedOutput) {
if (!this.webview) {
return;
}
@@ -1250,7 +1250,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this.webview!.removeInset(output);
}
- hideInset(output: IOutput) {
+ hideInset(output: IProcessedOutput) {
if (!this.webview) {
return;
}
diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts
index 2119c8d5de6..e68645dc4c6 100644
--- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts
+++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts
@@ -6,12 +6,12 @@
import * as nls from 'vs/nls';
import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { URI } from 'vs/base/common/uri';
+import { URI, UriComponents } from 'vs/base/common/uri';
import { notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Emitter, Event } from 'vs/base/common/event';
-import { INotebookTextModel, INotebookMimeTypeSelector, INotebookRendererInfo, NotebookDocumentMetadata, ICellDto2, INotebookKernelInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { INotebookTextModel, INotebookRendererInfo, NotebookDocumentMetadata, ICellDto2, INotebookKernelInfo, CellOutputKind, ITransformedDisplayOutputDto, IDisplayOutput, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, IOrderedMimeType, mimeTypeSupportedByCore, IOutputRenderRequestOutputInfo, IOutputRenderRequestCellInfo, NotebookCellOutputsSplice, ICellEditOperation, CellEditType, ICellInsertEdit, IOutputRenderResponse, IProcessedOutput, BUILTIN_RENDERER_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
import { Iterable } from 'vs/base/common/iterator';
@@ -24,6 +24,8 @@ import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/
import * as glob from 'vs/base/common/glob';
import { basename } from 'vs/base/common/resources';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
function MODEL_ID(resource: URI): string {
return resource.toString();
@@ -97,7 +99,7 @@ class ModelData implements IDisposable {
export class NotebookService extends Disposable implements INotebookService, ICustomEditorViewTypesHandler {
_serviceBrand: undefined;
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, extensionData: NotebookExtensionDescription }>();
- private readonly _notebookRenderers = new Map<number, { extensionData: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: URI[] }>();
+ private readonly _notebookRenderers = new Map<string, INotebookRendererInfo>();
private readonly _notebookKernels = new Map<string, INotebookKernelInfo>();
notebookProviderInfoStore: NotebookProviderInfoStore = new NotebookProviderInfoStore();
notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore();
@@ -120,10 +122,13 @@ export class NotebookService extends Disposable implements INotebookService, ICu
private cutItems: NotebookCellTextModel[] | undefined;
modelManager: NotebookEditorModelManager;
+ private _displayOrder: { userOrder: string[], defaultOrder: string[] } = Object.create(null);
constructor(
@IExtensionService private readonly extensionService: IExtensionService,
@IEditorService private readonly editorService: IEditorService,
+ @IConfigurationService private readonly configurationService: IConfigurationService,
+ @IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
@@ -166,6 +171,26 @@ export class NotebookService extends Disposable implements INotebookService, ICu
});
this.editorService.registerCustomEditorViewTypesHandler('Notebook', this);
+
+ const updateOrder = () => {
+ let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
+ this._displayOrder = {
+ defaultOrder: this.accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER,
+ userOrder: userOrder
+ };
+ };
+
+ updateOrder();
+
+ this._register(this.configurationService.onDidChangeConfiguration(e => {
+ if (e.affectedKeys.indexOf('notebook.displayOrder') >= 0) {
+ updateOrder();
+ }
+ }));
+
+ this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
+ updateOrder();
+ }));
}
getViewTypes(): ICustomEditorInfo[] {
@@ -195,12 +220,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu
this._onDidChangeViewTypes.fire();
}
- registerNotebookRenderer(handle: number, extensionData: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: URI[]) {
- this._notebookRenderers.set(handle, { extensionData, type, selectors, preloads });
+ registerNotebookRenderer(id: string, renderer: INotebookRendererInfo) {
+ this._notebookRenderers.set(id, renderer);
}
- unregisterNotebookRenderer(handle: number) {
- this._notebookRenderers.delete(handle);
+ unregisterNotebookRenderer(id: string) {
+ this._notebookRenderers.delete(id);
}
registerNotebookKernel(notebook: INotebookKernelInfo): void {
@@ -253,18 +278,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu
return false;
}
- getRendererInfo(handle: number): INotebookRendererInfo | undefined {
- const renderer = this._notebookRenderers.get(handle);
+ getRendererInfo(id: string): INotebookRendererInfo | undefined {
+ const renderer = this._notebookRenderers.get(id);
- if (renderer) {
- return {
- id: renderer.extensionData.id,
- extensionLocation: URI.revive(renderer.extensionData.location),
- preloads: renderer.preloads
- };
- }
-
- return;
+ return renderer;
}
async createNotebookFromBackup(viewType: string, uri: URI, metadata: NotebookDocumentMetadata, languages: string[], cells: ICellDto2[], editorId?: string): Promise<NotebookTextModel | undefined> {
@@ -274,6 +291,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu
}
const notebookModel = await provider.controller.createNotebook(viewType, uri, { metadata, languages, cells }, false, editorId);
+ await this.transformTextModelOutputs(notebookModel!);
if (!notebookModel) {
return undefined;
}
@@ -294,9 +312,8 @@ export class NotebookService extends Disposable implements INotebookService, ICu
return undefined;
}
- let notebookModel: NotebookTextModel | undefined;
-
- notebookModel = await provider.controller.createNotebook(viewType, uri, undefined, forceReload, editorId);
+ const notebookModel = await provider.controller.createNotebook(viewType, uri, undefined, forceReload, editorId);
+ await this.transformTextModelOutputs(notebookModel!);
// new notebook model created
const modelId = MODEL_ID(uri);
@@ -309,6 +326,237 @@ export class NotebookService extends Disposable implements INotebookService, ICu
return modelData.model;
}
+ private async _fillInTransformedOutputs<T>(
+ renderers: Set<string>,
+ requestItems: IOutputRenderRequestCellInfo<T>[],
+ renderFunc: (rendererId: string, items: IOutputRenderRequestCellInfo<T>[]) => Promise<IOutputRenderResponse<T> | undefined>,
+ lookUp: (key: T) => { outputs: IProcessedOutput[] }
+ ) {
+ for (let id of renderers) {
+ const requestsPerRenderer: IOutputRenderRequestCellInfo<T>[] = requestItems.map(req => {
+ return {
+ key: req.key,
+ outputs: req.outputs.filter(output => output.handlerId === id)
+ };
+ });
+
+ const response = await renderFunc(id, requestsPerRenderer);
+
+ // mix the response with existing outputs, which will replace the picked transformed mimetype with resolved result
+ if (response) {
+ response.items.forEach(cellInfo => {
+ const cell = lookUp(cellInfo.key)!;
+ cellInfo.outputs.forEach(outputInfo => {
+ const output = cell.outputs[outputInfo.index];
+ if (output.outputKind === CellOutputKind.Rich && output.orderedMimeTypes && output.orderedMimeTypes.length) {
+ output.orderedMimeTypes[0] = {
+ mimeType: outputInfo.mimeType,
+ isResolved: true,
+ rendererId: outputInfo.handlerId,
+ output: outputInfo.transformedOutput
+ };
+ }
+ });
+ });
+ }
+ }
+ }
+
+ async transformTextModelOutputs(textModel: NotebookTextModel) {
+ const renderers = new Set<string>();
+
+ const cellMapping: Map<string, NotebookCellTextModel> = new Map();
+
+ const requestItems: IOutputRenderRequestCellInfo<UriComponents>[] = [];
+ for (let i = 0; i < textModel.cells.length; i++) {
+ const cell = textModel.cells[i];
+ cellMapping.set(cell.uri.fragment, cell);
+ const outputs = cell.outputs;
+ const outputRequest: IOutputRenderRequestOutputInfo[] = [];
+
+ outputs.forEach((output, index) => {
+ if (output.outputKind === CellOutputKind.Rich) {
+ // TODO no string[] casting
+ const ret = this._transformMimeTypes(output, textModel.metadata.displayOrder as string[] || []);
+ const orderedMimeTypes = ret.orderedMimeTypes!;
+ const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!;
+ output.pickedMimeTypeIndex = pickedMimeTypeIndex;
+ output.orderedMimeTypes = orderedMimeTypes;
+
+ if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) {
+ outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType });
+ renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!);
+ }
+ }
+ });
+
+ requestItems.push({ key: cell.uri, outputs: outputRequest });
+ }
+
+ await this._fillInTransformedOutputs<UriComponents>(renderers, requestItems, async (rendererId, items) => {
+ return await this._notebookRenderers.get(rendererId)?.render(textModel.uri, { items: items });
+ }, (key: UriComponents) => { return cellMapping.get(URI.revive(key).fragment)!; });
+
+ textModel.updateRenderers([...renderers]);
+ }
+
+ async transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) {
+ const renderers = new Set<string>();
+ const requestItems: IOutputRenderRequestCellInfo<[number, number]>[] = [];
+
+ edits.forEach((edit, editIndex) => {
+ if (edit.editType === CellEditType.Insert) {
+ edit.cells.forEach((cell, cellIndex) => {
+ const outputs = cell.outputs;
+ const outputRequest: IOutputRenderRequestOutputInfo[] = [];
+ outputs.map((output, index) => {
+ if (output.outputKind === CellOutputKind.Rich) {
+ const ret = this._transformMimeTypes(output, textModel.metadata.displayOrder as string[] || []);
+ const orderedMimeTypes = ret.orderedMimeTypes!;
+ const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!;
+ output.pickedMimeTypeIndex = pickedMimeTypeIndex;
+ output.orderedMimeTypes = orderedMimeTypes;
+
+ if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) {
+ outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, output: output });
+ renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!);
+ }
+ }
+ });
+
+ requestItems.push({ key: [editIndex, cellIndex], outputs: outputRequest });
+ });
+ }
+ });
+
+ await this._fillInTransformedOutputs<[number, number]>(renderers, requestItems, async (rendererId, items) => {
+ return await this._notebookRenderers.get(rendererId)?.render2<[number, number]>(textModel.uri, { items: items });
+ }, (key: [number, number]) => {
+ return (edits[key[0]] as ICellInsertEdit).cells[key[1]];
+ });
+
+ textModel.updateRenderers([...renderers]);
+ }
+
+ async transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]) {
+ const renderers = new Set<string>();
+ const requestItems: IOutputRenderRequestCellInfo<number>[] = [];
+
+ splices.forEach((splice, spliceIndex) => {
+ const outputs = splice[2];
+ const outputRequest: IOutputRenderRequestOutputInfo[] = [];
+ outputs.map((output, index) => {
+ if (output.outputKind === CellOutputKind.Rich) {
+ const ret = this._transformMimeTypes(output, textModel.metadata.displayOrder as string[] || []);
+ const orderedMimeTypes = ret.orderedMimeTypes!;
+ const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!;
+ output.pickedMimeTypeIndex = pickedMimeTypeIndex;
+ output.orderedMimeTypes = orderedMimeTypes;
+
+ if (orderedMimeTypes[pickedMimeTypeIndex!].rendererId && orderedMimeTypes[pickedMimeTypeIndex].rendererId !== BUILTIN_RENDERER_ID) {
+ outputRequest.push({ index, handlerId: orderedMimeTypes[pickedMimeTypeIndex].rendererId!, mimeType: orderedMimeTypes[pickedMimeTypeIndex].mimeType, output: output });
+ renderers.add(orderedMimeTypes[pickedMimeTypeIndex].rendererId!);
+ }
+ }
+ });
+ requestItems.push({ key: spliceIndex, outputs: outputRequest });
+ });
+
+ await this._fillInTransformedOutputs<number>(renderers, requestItems, async (rendererId, items) => {
+ return await this._notebookRenderers.get(rendererId)?.render2<number>(textModel.uri, { items: items });
+ }, (key: number) => {
+ return { outputs: splices[key][2] };
+ });
+
+ textModel.updateRenderers([...renderers]);
+ }
+
+ async transformSingleOutput(textModel: NotebookTextModel, output: IProcessedOutput, rendererId: string, mimeType: string): Promise<IOrderedMimeType | undefined> {
+ const items = [
+ {
+ key: 0,
+ outputs: [
+ {
+ index: 0,
+ handlerId: rendererId,
+ mimeType: mimeType,
+ output: output
+ }
+ ]
+ }
+ ];
+ const response = await this._notebookRenderers.get(rendererId)?.render2<number>(textModel.uri, { items: items });
+
+ if (response) {
+ textModel.updateRenderers([rendererId]);
+ const outputInfo = response.items[0].outputs[0];
+
+ return {
+ mimeType: outputInfo.mimeType,
+ isResolved: true,
+ rendererId: outputInfo.handlerId,
+ output: outputInfo.transformedOutput
+ };
+ }
+
+ return;
+ }
+
+ private _transformMimeTypes(output: IDisplayOutput, documentDisplayOrder: string[]): ITransformedDisplayOutputDto {
+ let mimeTypes = Object.keys(output.data);
+ let coreDisplayOrder = this._displayOrder;
+ const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []);
+
+ let orderMimeTypes: IOrderedMimeType[] = [];
+
+ sorted.forEach(mimeType => {
+ let handlers = this.findBestMatchedRenderer(mimeType);
+
+ if (handlers.length) {
+ const handler = handlers[0];
+
+ orderMimeTypes.push({
+ mimeType: mimeType,
+ isResolved: false,
+ rendererId: handler.id,
+ });
+
+ for (let i = 1; i < handlers.length; i++) {
+ orderMimeTypes.push({
+ mimeType: mimeType,
+ isResolved: false,
+ rendererId: handlers[i].id
+ });
+ }
+
+ if (mimeTypeSupportedByCore(mimeType)) {
+ orderMimeTypes.push({
+ mimeType: mimeType,
+ isResolved: false,
+ rendererId: BUILTIN_RENDERER_ID
+ });
+ }
+ } else {
+ orderMimeTypes.push({
+ mimeType: mimeType,
+ isResolved: false,
+ rendererId: BUILTIN_RENDERER_ID
+ });
+ }
+ });
+
+ return {
+ outputKind: output.outputKind,
+ data: output.data,
+ orderedMimeTypes: orderMimeTypes,
+ pickedMimeTypeIndex: 0
+ };
+ }
+
+ findBestMatchedRenderer(mimeType: string): readonly NotebookOutputRendererInfo[] {
+ return this.notebookRenderersInfoStore.getContributedRenderer(mimeType);
+ }
+
async executeNotebook(viewType: string, uri: URI, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
let provider = this._notebookProviders.get(viewType);
diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts
index 51390740623..43e575d93a4 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts
@@ -21,7 +21,7 @@ import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellRange, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
-import { diff, IOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { diff, IProcessedOutput, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { clamp } from 'vs/base/common/numbers';
export class NotebookCellList extends WorkbenchList<CellViewModel> implements IDisposable, IStyleController, INotebookCellList {
@@ -35,10 +35,10 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
private _viewModelStore = new DisposableStore();
private styleElement?: HTMLStyleElement;
- private readonly _onDidRemoveOutput = new Emitter<IOutput>();
- readonly onDidRemoveOutput: Event<IOutput> = this._onDidRemoveOutput.event;
- private readonly _onDidHideOutput = new Emitter<IOutput>();
- readonly onDidHideOutput: Event<IOutput> = this._onDidHideOutput.event;
+ private readonly _onDidRemoveOutput = new Emitter<IProcessedOutput>();
+ readonly onDidRemoveOutput: Event<IProcessedOutput> = this._onDidRemoveOutput.event;
+ private readonly _onDidHideOutput = new Emitter<IProcessedOutput>();
+ readonly onDidHideOutput: Event<IProcessedOutput> = this._onDidHideOutput.event;
private _viewModel: NotebookViewModel | null = null;
private _hiddenRangeIds: string[] = [];
@@ -185,8 +185,8 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
if (e.synchronous) {
viewDiffs.reverse().forEach((diff) => {
// remove output in the webview
- const hideOutputs: IOutput[] = [];
- const deletedOutputs: IOutput[] = [];
+ const hideOutputs: IProcessedOutput[] = [];
+ const deletedOutputs: IProcessedOutput[] = [];
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
const cell = this.element(i);
@@ -205,8 +205,8 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
} else {
DOM.scheduleAtNextAnimationFrame(() => {
viewDiffs.reverse().forEach((diff) => {
- const hideOutputs: IOutput[] = [];
- const deletedOutputs: IOutput[] = [];
+ const hideOutputs: IProcessedOutput[] = [];
+ const deletedOutputs: IProcessedOutput[] = [];
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
const cell = this.element(i);
@@ -327,8 +327,8 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
viewDiffs.reverse().forEach((diff) => {
// remove output in the webview
- const hideOutputs: IOutput[] = [];
- const deletedOutputs: IOutput[] = [];
+ const hideOutputs: IProcessedOutput[] = [];
+ const deletedOutputs: IProcessedOutput[] = [];
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
const cell = this.element(i);
diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts
index b6d6134632f..cbc254803dd 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { IOutput, IRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { IProcessedOutput, IRenderOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry';
import { onUnexpectedError } from 'vs/base/common/errors';
import { INotebookEditor, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
@@ -33,7 +33,7 @@ export class OutputRenderer {
}
}
- renderNoop(output: IOutput, container: HTMLElement): IRenderOutput {
+ renderNoop(output: IProcessedOutput, container: HTMLElement): IRenderOutput {
const contentNode = document.createElement('p');
contentNode.innerText = `No renderer could be found for output. It has the following output type: ${output.outputKind}`;
@@ -43,7 +43,7 @@ export class OutputRenderer {
};
}
- render(output: IOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput {
+ render(output: IProcessedOutput, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput {
let transform = this._mimeTypeMapping[output.outputKind];
if (transform) {
diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts
index fc11f14dec0..6ed868928fe 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts
@@ -15,6 +15,8 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { URI } from 'vs/base/common/uri';
import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer';
+import { IThemeService } from 'vs/platform/theme/common/themeService';
+import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform';
class RichRenderer implements IOutputTransformContribution {
private _mdRenderer: MarkdownRenderer;
@@ -24,7 +26,8 @@ class RichRenderer implements IOutputTransformContribution {
public notebookEditor: INotebookEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IModelService private readonly modelService: IModelService,
- @IModeService private readonly modeService: IModeService
+ @IModeService private readonly modeService: IModeService,
+ @IThemeService private readonly themeService: IThemeService
) {
this._mdRenderer = instantiationService.createInstance(MarkdownRenderer, undefined);
this._richMimeTypeRenderers.set('application/json', this.renderJSON.bind(this));
@@ -209,8 +212,8 @@ class RichRenderer implements IOutputTransformContribution {
renderPlainText(output: any, container: HTMLElement) {
let data = output.data['text/plain'];
let str = isArray(data) ? data.join('') : data;
- const contentNode = document.createElement('p');
- contentNode.innerText = str;
+ const contentNode = document.createElement('div');
+ contentNode.appendChild(handleANSIOutput(str, this.themeService));
container.appendChild(contentNode);
return {
diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
index 08190e62d4e..06c54bec940 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
@@ -12,17 +12,18 @@ import { isWeb } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import * as UUID from 'vs/base/common/uuid';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
-import { IOpenerService } from 'vs/platform/opener/common/opener';
+import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
import { CELL_MARGIN, CELL_RUN_GUTTER } from 'vs/workbench/contrib/notebook/browser/constants';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
-import { IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { asWebviewUri } from 'vs/workbench/contrib/webview/common/webviewUri';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { dirname } from 'vs/base/common/resources';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
+import { Schemas } from 'vs/base/common/network';
export interface WebviewIntialized {
__vscode_notebook_message: boolean;
@@ -118,7 +119,7 @@ export interface IUpdatePreloadResourceMessage {
interface ICachedInset {
outputId: string;
cell: CodeCellViewModel;
- preloads: ReadonlySet<number>;
+ preloads: ReadonlySet<string>;
cachedCreation: ICreationRequestMessage;
}
@@ -136,9 +137,9 @@ let version = 0;
export class BackLayerWebView extends Disposable {
element: HTMLElement;
webview!: WebviewElement;
- insetMapping: Map<IOutput, ICachedInset> = new Map();
- hiddenInsetMapping: Set<IOutput> = new Set();
- reversedInsetMapping: Map<string, IOutput> = new Map();
+ insetMapping: Map<IProcessedOutput, ICachedInset> = new Map();
+ hiddenInsetMapping: Set<IProcessedOutput> = new Set();
+ reversedInsetMapping: Map<string, IProcessedOutput> = new Map();
preloadsCache: Map<string, boolean> = new Map();
localResourceRootsCache: URI[] | undefined = undefined;
rendererRootsCache: URI[] = [];
@@ -491,7 +492,7 @@ ${loaderJs}
`;
}
- private resolveOutputId(id: string): { cell: CodeCellViewModel, output: IOutput } | undefined {
+ private resolveOutputId(id: string): { cell: CodeCellViewModel, output: IProcessedOutput } | undefined {
const output = this.reversedInsetMapping.get(id);
if (!output) {
return;
@@ -505,7 +506,14 @@ ${loaderJs}
this.webview.mountTo(this.element);
this._register(this.webview.onDidClickLink(link => {
- this.openerService.open(link, { fromUserGesture: true });
+ if (!link) {
+ return;
+ }
+
+ if (matchesScheme(link, Schemas.http) || matchesScheme(link, Schemas.https) || matchesScheme(link, Schemas.mailto)
+ || matchesScheme(link, Schemas.command)) {
+ this.openerService.open(link, { fromUserGesture: true });
+ }
}));
this._register(this.webview.onDidReload(() => {
@@ -612,7 +620,7 @@ ${loaderJs}
return webview;
}
- shouldUpdateInset(cell: CodeCellViewModel, output: IOutput, cellTop: number) {
+ shouldUpdateInset(cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number) {
if (this._disposed) {
return;
}
@@ -632,7 +640,7 @@ ${loaderJs}
return true;
}
- updateViewScrollTop(top: number, items: { cell: CodeCellViewModel, output: IOutput, cellTop: number }[]) {
+ updateViewScrollTop(top: number, items: { cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number }[]) {
if (this._disposed) {
return;
}
@@ -663,7 +671,7 @@ ${loaderJs}
this.webview.sendMessage(message);
}
- createInset(cell: CodeCellViewModel, output: IOutput, cellTop: number, offset: number, shadowContent: string, preloads: Set<number>) {
+ createInset(cell: CodeCellViewModel, output: IProcessedOutput, cellTop: number, offset: number, shadowContent: string, preloads: Set<string>) {
if (this._disposed) {
return;
}
@@ -702,7 +710,7 @@ ${loaderJs}
this.reversedInsetMapping.set(outputId, output);
}
- removeInset(output: IOutput) {
+ removeInset(output: IProcessedOutput) {
if (this._disposed) {
return;
}
@@ -722,7 +730,7 @@ ${loaderJs}
this.reversedInsetMapping.delete(id);
}
- hideInset(output: IOutput) {
+ hideInset(output: IProcessedOutput) {
if (this._disposed) {
return;
}
@@ -798,7 +806,7 @@ ${loaderJs}
this._updatePreloads(resources, 'kernel');
}
- async updateRendererPreloads(preloads: ReadonlySet<number>) {
+ async updateRendererPreloads(preloads: ReadonlySet<string>) {
if (this._disposed) {
return;
}
diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts
index 2cad8483828..e64ea69581e 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts
@@ -15,7 +15,7 @@ import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workb
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
-import { CellOutputKind, IOutput, IRenderOutput, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { CellOutputKind, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, BUILTIN_RENDERER_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IDimension } from 'vs/editor/common/editorCommon';
@@ -25,8 +25,8 @@ interface IMimeTypeRenderer extends IQuickPickItem {
}
export class CodeCell extends Disposable {
- private outputResizeListeners = new Map<IOutput, DisposableStore>();
- private outputElements = new Map<IOutput, HTMLElement>();
+ private outputResizeListeners = new Map<IProcessedOutput, DisposableStore>();
+ private outputElements = new Map<IProcessedOutput, HTMLElement>();
constructor(
private notebookEditor: INotebookEditor,
private viewCell: CodeCellViewModel,
@@ -155,7 +155,7 @@ export class CodeCell extends Disposable {
viewCell.spliceOutputHeights(splice[0], splice[1], splice[2].map(_ => 0));
});
- let removedKeys: IOutput[] = [];
+ let removedKeys: IProcessedOutput[] = [];
this.outputElements.forEach((value, key) => {
if (viewCell.outputs.indexOf(key) < 0) {
@@ -273,7 +273,7 @@ export class CodeCell extends Disposable {
this.relayoutCell();
}
- renderOutput(currOutput: IOutput, index: number, beforeElement?: HTMLElement) {
+ renderOutput(currOutput: IProcessedOutput, index: number, beforeElement?: HTMLElement) {
if (!this.outputResizeListeners.has(currOutput)) {
this.outputResizeListeners.set(currOutput, new DisposableStore());
}
@@ -284,12 +284,12 @@ export class CodeCell extends Disposable {
if (currOutput.outputKind === CellOutputKind.Rich) {
let transformedDisplayOutput = currOutput as ITransformedDisplayOutputDto;
- if (transformedDisplayOutput.orderedMimeTypes.length > 1) {
+ if (transformedDisplayOutput.orderedMimeTypes!.length > 1) {
outputItemDiv.style.position = 'relative';
const mimeTypePicker = DOM.$('.multi-mimetype-output');
DOM.addClasses(mimeTypePicker, 'codicon', 'codicon-code');
mimeTypePicker.tabIndex = 0;
- mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", transformedDisplayOutput.orderedMimeTypes.map(mimeType => mimeType.mimeType).join(', '));
+ mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", transformedDisplayOutput.orderedMimeTypes!.map(mimeType => mimeType.mimeType).join(', '));
outputItemDiv.appendChild(mimeTypePicker);
this.outputResizeListeners.get(currOutput)!.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => {
if (e.leftButton) {
@@ -309,7 +309,7 @@ export class CodeCell extends Disposable {
})));
}
- let pickedMimeTypeRenderer = currOutput.orderedMimeTypes[currOutput.pickedMimeTypeIndex];
+ let pickedMimeTypeRenderer = currOutput.orderedMimeTypes![currOutput.pickedMimeTypeIndex!];
if (pickedMimeTypeRenderer.isResolved) {
// html
@@ -386,15 +386,15 @@ export class CodeCell extends Disposable {
}
}
- generateRendererInfo(renderId: number | undefined): string {
- if (renderId === undefined || renderId === -1) {
+ generateRendererInfo(renderId: string | undefined): string {
+ if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) {
return nls.localize('builtinRenderInfo', "built-in");
}
let renderInfo = this.notebookService.getRendererInfo(renderId);
if (renderInfo) {
- return renderInfo.id.value;
+ return renderInfo.extensionId.value;
}
return nls.localize('builtinRenderInfo', "built-in");
@@ -402,7 +402,7 @@ export class CodeCell extends Disposable {
async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) {
let currIndex = output.pickedMimeTypeIndex;
- const items = output.orderedMimeTypes.map((mimeType, index): IMimeTypeRenderer => ({
+ const items = output.orderedMimeTypes!.map((mimeType, index): IMimeTypeRenderer => ({
label: mimeType.mimeType,
id: mimeType.mimeType,
index: index,
@@ -442,6 +442,16 @@ export class CodeCell extends Disposable {
output.pickedMimeTypeIndex = pick;
+ if (!output.orderedMimeTypes![pick].isResolved && output.orderedMimeTypes![pick].rendererId !== BUILTIN_RENDERER_ID) {
+ // since it's not build in renderer and not resolved yet
+ // let's see if we can activate the extension and then render
+ // await this.notebookService.transformSpliceOutputs(this.notebookEditor.textModel!, [[0, 0, output]])
+ const outputRet = await this.notebookService.transformSingleOutput(this.notebookEditor.textModel!, output, output.orderedMimeTypes![pick].rendererId!, output.orderedMimeTypes![pick].mimeType);
+ if (outputRet) {
+ output.orderedMimeTypes![pick] = outputRet;
+ }
+ }
+
this.renderOutput(output, index, nextElement);
this.relayoutCell();
}
diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts
index 4ce48fcb70a..94466d9fe03 100644
--- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts
+++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
-import { ICell, IOutput, NotebookCellOutputsSplice, CellKind, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { ICell, IProcessedOutput, NotebookCellOutputsSplice, CellKind, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
import { URI } from 'vs/base/common/uri';
import * as model from 'vs/editor/common/model';
@@ -24,9 +24,9 @@ export class NotebookCellTextModel extends Disposable implements ICell {
private _onDidChangeLanguage = new Emitter<string>();
onDidChangeLanguage: Event<string> = this._onDidChangeLanguage.event;
- private _outputs: IOutput[];
+ private _outputs: IProcessedOutput[];
- get outputs(): IOutput[] {
+ get outputs(): IProcessedOutput[] {
return this._outputs;
}
@@ -75,7 +75,7 @@ export class NotebookCellTextModel extends Disposable implements ICell {
private _source: string | string[],
private _language: string,
public cellKind: CellKind,
- outputs: IOutput[],
+ outputs: IProcessedOutput[],
metadata: NotebookCellMetadata | undefined
) {
super();
diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts
index 096d7850b0b..9232209ce59 100644
--- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts
+++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts
@@ -7,7 +7,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
-import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITextSnapshot } from 'vs/editor/common/model';
function compareRangesUsingEnds(a: [number, number], b: [number, number]): number {
@@ -43,7 +43,8 @@ export class NotebookTextModelSnapshot implements ITextSnapshot {
source: cell.getValue(),
metadata: cell.metadata,
cellKind: cell.cellKind,
- language: cell.language
+ language: cell.language,
+ outputs: cell.outputs
};
const rawStr = JSON.stringify(data);
@@ -70,7 +71,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
private readonly _onDidChangeCells = new Emitter<NotebookCellTextModelSplice[]>();
get onDidChangeCells(): Event<NotebookCellTextModelSplice[]> { return this._onDidChangeCells.event; }
private _onDidModelChangeProxy = new Emitter<NotebookCellsChangedEvent>();
- get onDidModelChange(): Event<NotebookCellsChangedEvent> { return this._onDidModelChangeProxy.event; }
+ get onDidModelChangeProxy(): Event<NotebookCellsChangedEvent> { return this._onDidModelChangeProxy.event; }
private _onDidSelectionChangeProxy = new Emitter<number[] | null>();
get onDidSelectionChange(): Event<number[] | null> { return this._onDidSelectionChangeProxy.event; }
private _onDidChangeContent = new Emitter<void>();
@@ -82,7 +83,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
cells: NotebookCellTextModel[];
languages: string[] = [];
metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults;
- renderers = new Set<number>();
+ renderers = new Set<string>();
private _isUntitled: boolean | undefined = undefined;
private _versionId = 0;
@@ -114,7 +115,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
source: string | string[],
language: string,
cellKind: CellKind,
- outputs: IOutput[],
+ outputs: IProcessedOutput[],
metadata: NotebookCellMetadata | undefined
) {
const cellHandle = this._cellhandlePool++;
@@ -134,7 +135,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this.insertNewCell(0, mainCells);
}
- applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[]): boolean {
+ $applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[]): boolean {
if (modelVersionId !== this._versionId) {
return false;
}
@@ -181,10 +182,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
const cellUri = CellUri.generate(this.uri, cellHandle);
return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata);
});
- this.insertNewCell(insertEdit.index, mainCells);
+ this.insertNewCell(insertEdit.index, mainCells, false);
break;
case CellEditType.Delete:
- this.removeCell(operations[i].index, operations[i].end - operations[i].start);
+ this.removeCell(operations[i].index, operations[i].end - operations[i].start, false);
break;
}
}
@@ -195,6 +196,20 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
return [diff.start, diff.deleteCount, diff.toInsert] as [number, number, NotebookCellTextModel[]];
});
+ this._onDidModelChangeProxy.fire({
+ kind: NotebookCellsChangeType.ModelChange,
+ versionId: this._versionId,
+ changes: diffs.map(diff => [diff[0], diff[1], diff[2].map(cell => ({
+ handle: cell.handle,
+ uri: cell.uri,
+ source: cell.textBuffer.getLinesContent(),
+ language: cell.language,
+ cellKind: cell.cellKind,
+ outputs: cell.outputs,
+ metadata: cell.metadata
+ }))] as [number, number, IMainCellDto[]])
+ });
+
this._onDidChangeCells.fire(diffs);
return true;
}
@@ -229,7 +244,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
}
}
- updateRenderers(renderers: number[]) {
+ updateRenderers(renderers: string[]) {
renderers.forEach(render => {
this.renderers.add(render);
});
@@ -254,8 +269,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._onDidModelChangeProxy.fire({
kind: NotebookCellsChangeType.ModelChange,
- versionId: this._versionId, change:
- [
+ versionId: this._versionId, changes:
+ [[
0,
0,
[{
@@ -267,13 +282,13 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
outputs: cell.outputs,
metadata: cell.metadata
}]
- ]
+ ]]
});
return;
}
- insertNewCell(index: number, cells: NotebookCellTextModel[]): void {
+ insertNewCell(index: number, cells: NotebookCellTextModel[], emitToExtHost: boolean = true): void {
this._isUntitled = false;
for (let i = 0; i < cells.length; i++) {
@@ -288,28 +303,31 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this.cells.splice(index, 0, ...cells);
this._onDidChangeContent.fire();
this._increaseVersionId();
- this._onDidModelChangeProxy.fire({
- kind: NotebookCellsChangeType.ModelChange,
- versionId: this._versionId, change:
- [
- index,
- 0,
- cells.map(cell => ({
- handle: cell.handle,
- uri: cell.uri,
- source: cell.textBuffer.getLinesContent(),
- language: cell.language,
- cellKind: cell.cellKind,
- outputs: cell.outputs,
- metadata: cell.metadata
- }))
- ]
- });
+
+ if (emitToExtHost) {
+ this._onDidModelChangeProxy.fire({
+ kind: NotebookCellsChangeType.ModelChange,
+ versionId: this._versionId, changes:
+ [[
+ index,
+ 0,
+ cells.map(cell => ({
+ handle: cell.handle,
+ uri: cell.uri,
+ source: cell.textBuffer.getLinesContent(),
+ language: cell.language,
+ cellKind: cell.cellKind,
+ outputs: cell.outputs,
+ metadata: cell.metadata
+ }))
+ ]]
+ });
+ }
return;
}
- removeCell(index: number, count: number) {
+ removeCell(index: number, count: number, emitToExtHost: boolean = true) {
this._isUntitled = false;
for (let i = index; i < index + count; i++) {
@@ -321,10 +339,12 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._onDidChangeContent.fire();
this._increaseVersionId();
- this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, change: [index, count, []] });
+ if (emitToExtHost) {
+ this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]] });
+ }
}
- moveCellToIdx(index: number, newIdx: number) {
+ moveCellToIdx(index: number, newIdx: number, emitToExtHost: boolean = true) {
this.assertIndex(index);
this.assertIndex(newIdx);
@@ -332,7 +352,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this.cells.splice(newIdx, 0, ...cells);
this._increaseVersionId();
- this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, newIdx });
+
+ if (emitToExtHost) {
+ this._onDidModelChangeProxy.fire({ kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, newIdx });
+ }
}
assertIndex(index: number) {
diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts
index 14b5c709ffa..9b53e3870ce 100644
--- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts
+++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts
@@ -50,6 +50,8 @@ export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER = [
'image/jpeg',
];
+export const BUILTIN_RENDERER_ID = '_builtin';
+
export const notebookDocumentMetadataDefaults: Required<NotebookDocumentMetadata> = {
editable: true,
runnable: true,
@@ -101,9 +103,12 @@ export interface INotebookMimeTypeSelector {
}
export interface INotebookRendererInfo {
- id: ExtensionIdentifier;
+ id: string;
+ extensionId: ExtensionIdentifier;
extensionLocation: URI,
- preloads: URI[]
+ preloads: URI[],
+ render(uri: URI, request: IOutputRenderRequest<UriComponents>): Promise<IOutputRenderResponse<UriComponents> | undefined>;
+ render2<T>(uri: URI, request: IOutputRenderRequest<T>): Promise<IOutputRenderResponse<T> | undefined>;
}
export interface INotebookKernelInfo {
@@ -165,7 +170,7 @@ export enum MimeTypeRendererResolver {
export interface IOrderedMimeType {
mimeType: string;
isResolved: boolean;
- rendererId?: number;
+ rendererId?: string;
output?: string;
}
@@ -173,8 +178,8 @@ export interface ITransformedDisplayOutputDto {
outputKind: CellOutputKind.Rich;
data: { [key: string]: any; }
- orderedMimeTypes: IOrderedMimeType[];
- pickedMimeTypeIndex: number;
+ orderedMimeTypes?: IOrderedMimeType[];
+ pickedMimeTypeIndex?: number;
}
export interface IGenericOutput {
@@ -184,14 +189,50 @@ export interface IGenericOutput {
transformedOutput?: { [key: string]: IDisplayOutput };
}
-export type IOutput = ITransformedDisplayOutputDto | IStreamOutput | IErrorOutput;
+export type IProcessedOutput = ITransformedDisplayOutputDto | IStreamOutput | IErrorOutput;
+
+export type IRawOutput = IDisplayOutput | IStreamOutput | IErrorOutput;
+
+export interface IOutputRenderRequestOutputInfo {
+ index: number;
+ handlerId: string;
+ mimeType: string;
+ output?: IRawOutput;
+}
+
+export interface IOutputRenderRequestCellInfo<T> {
+ key: T;
+ outputs: IOutputRenderRequestOutputInfo[];
+}
+
+export interface IOutputRenderRequest<T> {
+ items: IOutputRenderRequestCellInfo<T>[];
+}
+
+export interface IOutputRenderResponseOutputInfo {
+ index: number;
+ mimeType: string;
+ handlerId: string;
+ transformedOutput: string;
+}
+
+export interface IOutputRenderResponseCellInfo<T> {
+ key: T;
+ outputs: IOutputRenderResponseOutputInfo[];
+}
+
+
+export interface IOutputRenderResponse<T> {
+ items: IOutputRenderResponseCellInfo<T>[];
+}
+
export interface ICell {
readonly uri: URI;
handle: number;
language: string;
cellKind: CellKind;
- outputs: IOutput[];
+ outputs: IProcessedOutput[];
metadata?: NotebookCellMetadata;
onDidChangeOutputs?: Event<NotebookCellOutputsSplice[]>;
onDidChangeLanguage: Event<string>;
@@ -214,7 +255,7 @@ export interface INotebookTextModel {
readonly versionId: number;
languages: string[];
cells: ICell[];
- renderers: Set<number>;
+ renderers: Set<string>;
onDidChangeCells?: Event<NotebookCellTextModelSplice[]>;
onDidChangeContent: Event<void>;
onWillDispose(listener: () => void): IDisposable;
@@ -234,7 +275,7 @@ export type NotebookCellTextModelSplice = [
export type NotebookCellOutputsSplice = [
number /* start */,
number /* delete count */,
- IOutput[]
+ IProcessedOutput[]
];
export interface IMainCellDto {
@@ -243,7 +284,7 @@ export interface IMainCellDto {
source: string[];
language: string;
cellKind: CellKind;
- outputs: IOutput[];
+ outputs: IProcessedOutput[];
metadata?: NotebookCellMetadata;
}
@@ -263,7 +304,7 @@ export enum NotebookCellsChangeType {
export interface NotebookCellsModelChangedEvent {
readonly kind: NotebookCellsChangeType.ModelChange;
- readonly change: NotebookCellsSplice2;
+ readonly changes: NotebookCellsSplice2[];
readonly versionId: number;
}
@@ -302,7 +343,7 @@ export interface ICellDto2 {
source: string | string[];
language: string;
cellKind: CellKind;
- outputs: IOutput[];
+ outputs: IProcessedOutput[];
metadata?: NotebookCellMetadata;
}
@@ -330,7 +371,6 @@ export interface NotebookDataDto {
readonly cells: ICellDto2[];
readonly languages: string[];
readonly metadata: NotebookDocumentMetadata;
- readonly renderers: number[];
}
@@ -520,3 +560,4 @@ export interface IEditor extends editorCommon.ICompositeCodeEditor {
hasFocus(): boolean;
hasModel(): boolean;
}
+
diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts
index f28505ecd39..163b4d9dac9 100644
--- a/src/vs/workbench/contrib/notebook/common/notebookService.ts
+++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts
@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Event } from 'vs/base/common/event';
-import { INotebookTextModel, INotebookMimeTypeSelector, INotebookRendererInfo, NotebookDocumentMetadata, ICellDto2, INotebookKernelInfo, INotebookKernelInfoDto, INotebookTextModelBackup, IEditor } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { INotebookTextModel, INotebookRendererInfo, NotebookDocumentMetadata, ICellDto2, INotebookKernelInfo, INotebookKernelInfoDto, INotebookTextModelBackup, IEditor, ICellEditOperation, NotebookCellOutputsSplice, IOrderedMimeType, IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CancellationToken } from 'vs/base/common/cancellation';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
@@ -38,12 +38,15 @@ export interface INotebookService {
onDidChangeKernels: Event<void>;
registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void;
unregisterNotebookProvider(viewType: string): void;
- registerNotebookRenderer(handle: number, extensionData: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: URI[]): void;
- unregisterNotebookRenderer(handle: number): void;
+ registerNotebookRenderer(id: string, renderer: INotebookRendererInfo): void;
+ unregisterNotebookRenderer(id: string): void;
+ transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]): Promise<void>;
+ transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]): Promise<void>;
+ transformSingleOutput(textModel: NotebookTextModel, output: IProcessedOutput, rendererId: string, mimeType: string): Promise<IOrderedMimeType | undefined>;
registerNotebookKernel(kernel: INotebookKernelInfo): void;
unregisterNotebookKernel(id: string): void;
getContributedNotebookKernels(viewType: string, resource: URI): readonly INotebookKernelInfo[];
- getRendererInfo(handle: number): INotebookRendererInfo | undefined;
+ getRendererInfo(id: string): INotebookRendererInfo | undefined;
resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string): Promise<NotebookTextModel | undefined>;
createNotebookFromBackup(viewType: string, uri: URI, metadata: NotebookDocumentMetadata, languages: string[], cells: ICellDto2[], editorId?: string): Promise<NotebookTextModel | undefined>;
executeNotebook(viewType: string, uri: URI, useAttachedKernel: boolean, token: CancellationToken): Promise<void>;
diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts
index 06df06f42de..2bad51a304a 100644
--- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts
+++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts
@@ -28,7 +28,7 @@ suite('NotebookTextModel', () => {
[['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }]
],
(editor, viewModel, textModel) => {
- textModel.applyEdit(textModel.versionId, [
+ textModel.$applyEdit(textModel.versionId, [
{ editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] },
{ editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [])] },
]);
@@ -53,7 +53,7 @@ suite('NotebookTextModel', () => {
[['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }]
],
(editor, viewModel, textModel) => {
- textModel.applyEdit(textModel.versionId, [
+ textModel.$applyEdit(textModel.versionId, [
{ editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] },
{ editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 6, ['var f = 6;'], 'javascript', CellKind.Code, [])] },
]);
@@ -78,7 +78,7 @@ suite('NotebookTextModel', () => {
[['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }]
],
(editor, viewModel, textModel) => {
- textModel.applyEdit(textModel.versionId, [
+ textModel.$applyEdit(textModel.versionId, [
{ editType: CellEditType.Delete, index: 1, count: 1 },
{ editType: CellEditType.Delete, index: 3, count: 1 },
]);
@@ -101,7 +101,7 @@ suite('NotebookTextModel', () => {
[['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }]
],
(editor, viewModel, textModel) => {
- textModel.applyEdit(textModel.versionId, [
+ textModel.$applyEdit(textModel.versionId, [
{ editType: CellEditType.Delete, index: 1, count: 1 },
{ editType: CellEditType.Insert, index: 3, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] },
]);
@@ -126,7 +126,7 @@ suite('NotebookTextModel', () => {
[['var d = 4;'], 'javascript', CellKind.Code, [], { editable: false }]
],
(editor, viewModel, textModel) => {
- textModel.applyEdit(textModel.versionId, [
+ textModel.$applyEdit(textModel.versionId, [
{ editType: CellEditType.Delete, index: 1, count: 1 },
{ editType: CellEditType.Insert, index: 1, cells: [new TestCell(viewModel.viewType, 5, ['var e = 5;'], 'javascript', CellKind.Code, [])] },
]);
diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts
index ff45bcd9773..f979e6ac246 100644
--- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts
+++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts
@@ -19,7 +19,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v
import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
-import { CellKind, CellUri, INotebookEditorModel, IOutput, NotebookCellMetadata, INotebookKernelInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, INotebookKernelInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon';
@@ -30,7 +30,7 @@ export class TestCell extends NotebookCellTextModel {
public source: string[],
language: string,
cellKind: CellKind,
- outputs: IOutput[]
+ outputs: IProcessedOutput[]
) {
super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined);
}
@@ -201,10 +201,10 @@ export class TestNotebookEditor implements INotebookEditor {
// throw new Error('Method not implemented.');
return;
}
- createInset(cell: CellViewModel, output: IOutput, shadowContent: string, offset: number): void {
+ createInset(cell: CellViewModel, output: IProcessedOutput, shadowContent: string, offset: number): void {
// throw new Error('Method not implemented.');
}
- removeInset(output: IOutput): void {
+ removeInset(output: IProcessedOutput): void {
// throw new Error('Method not implemented.');
}
triggerScroll(event: IMouseWheelEvent): void {
@@ -277,7 +277,7 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi
}
}
-export function withTestNotebook(instantiationService: IInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) {
+export function withTestNotebook(instantiationService: IInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IProcessedOutput[], NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => void) {
const viewType = 'notebook';
const editor = new TestNotebookEditor();
const notebook = new NotebookTextModel(0, viewType, URI.parse('test'));
diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts
index 3ee582fcc0d..a3ba6665808 100644
--- a/src/vs/workbench/contrib/output/browser/outputView.ts
+++ b/src/vs/workbench/contrib/output/browser/outputView.ts
@@ -280,7 +280,7 @@ class SwitchOutputActionViewItem extends SelectActionViewItem {
@IThemeService private readonly themeService: IThemeService,
@IContextViewService contextViewService: IContextViewService
) {
- super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', 'Output Channels.') });
+ super(null, action, [], 0, contextViewService, { ariaLabel: nls.localize('outputChannels', 'Output Channels.'), optionsAsChildren: true });
let outputChannelRegistry = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels);
this._register(outputChannelRegistry.onDidRegisterChannel(() => this.updateOtions()));
diff --git a/src/vs/workbench/contrib/remote/common/showCandidate.ts b/src/vs/workbench/contrib/remote/common/showCandidate.ts
index 7ab26b73803..6fa5f01943c 100644
--- a/src/vs/workbench/contrib/remote/common/showCandidate.ts
+++ b/src/vs/workbench/contrib/remote/common/showCandidate.ts
@@ -14,9 +14,10 @@ export class ShowCandidateContribution extends Disposable implements IWorkbenchC
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
) {
super();
- if (workbenchEnvironmentService.options && workbenchEnvironmentService.options.showCandidate) {
+ const showPortCandidate = workbenchEnvironmentService.options?.tunnelProvider?.showPortCandidate;
+ if (showPortCandidate) {
this._register(remoteExplorerService.setCandidateFilter(async (candidates: { host: string, port: number, detail: string }[]): Promise<{ host: string, port: number, detail: string }[]> => {
- const filters: boolean[] = await Promise.all(candidates.map(candidate => workbenchEnvironmentService.options!.showCandidate!(candidate.host, candidate.port, candidate.detail)));
+ const filters: boolean[] = await Promise.all(candidates.map(candidate => showPortCandidate(candidate.host, candidate.port, candidate.detail)));
const filteredCandidates: { host: string, port: number, detail: string }[] = [];
if (filters.length !== candidates.length) {
return candidates;
diff --git a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts
index 9414ee117d4..2562cb661fe 100644
--- a/src/vs/workbench/contrib/remote/common/tunnelFactory.ts
+++ b/src/vs/workbench/contrib/remote/common/tunnelFactory.ts
@@ -14,10 +14,11 @@ export class TunnelFactoryContribution extends Disposable implements IWorkbenchC
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
) {
super();
- if (workbenchEnvironmentService.options && workbenchEnvironmentService.options.tunnelFactory) {
+ const tunnelFactory = workbenchEnvironmentService.options?.tunnelProvider?.tunnelFactory;
+ if (tunnelFactory) {
this._register(tunnelService.setTunnelProvider({
forwardPort: (tunnelOptions: TunnelOptions): Promise<RemoteTunnel> | undefined => {
- const tunnelPromise = workbenchEnvironmentService.options!.tunnelFactory!(tunnelOptions);
+ const tunnelPromise = tunnelFactory(tunnelOptions);
if (!tunnelPromise) {
return undefined;
}
diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts
index 6f28920018f..f8f4eeafa94 100644
--- a/src/vs/workbench/contrib/search/browser/search.contribution.ts
+++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts
@@ -831,6 +831,10 @@ configurationRegistry.registerConfiguration({
default: false,
markdownDescription: nls.localize('search.searchEditor.reusePriorSearchConfiguration', "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor")
},
+ 'search.searchEditor.defaultShowContextValue': {
+ type: ['number', 'null'],
+ default: null,
+ },
'search.sortOrder': {
'type': 'string',
'enum': [SearchSortOrder.Default, SearchSortOrder.FileNames, SearchSortOrder.Type, SearchSortOrder.Modified, SearchSortOrder.CountDescending, SearchSortOrder.CountAscending],
diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts
index b2d8dc6834c..59a80ce0c51 100644
--- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts
+++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts
@@ -304,11 +304,20 @@ export const getOrMakeSearchEditorInput = (
const storageService = accessor.get(IStorageService);
const configurationService = accessor.get(IConfigurationService);
- const reuseOldSettings = configurationService.getValue<ISearchConfigurationProperties>('search').searchEditor?.reusePriorSearchConfiguration;
+ const searchEditorSettings = configurationService.getValue<ISearchConfigurationProperties>('search').searchEditor;
+
+ const reuseOldSettings = searchEditorSettings?.reusePriorSearchConfiguration;
+ const defaultShowContextValue = searchEditorSettings?.defaultShowContextValue;
+
const priorConfig: SearchConfiguration = reuseOldSettings ? new Memento(SearchEditorInput.ID, storageService).getMemento(StorageScope.WORKSPACE).searchConfig : {};
const defaultConfig = defaultSearchConfig();
+
let config = { ...defaultConfig, ...priorConfig, ...existingData.config };
+ if (defaultShowContextValue !== null && defaultShowContextValue !== undefined) {
+ config.contextLines = defaultShowContextValue;
+ }
+
const modelUri = existingData.modelUri ?? URI.from({ scheme: SearchEditorScheme, fragment: `${Math.random()}` });
const cacheKey = existingData.backingUri?.toString() ?? modelUri.toString();
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
index 330f1227a89..4aa1cc3e8cf 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
@@ -343,7 +343,7 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem {
@IThemeService private readonly _themeService: IThemeService,
@IContextViewService contextViewService: IContextViewService
) {
- super(null, action, getTerminalSelectOpenItems(_terminalService), _terminalService.activeTabIndex, contextViewService, { ariaLabel: localize('terminals', 'Open Terminals.') });
+ super(null, action, getTerminalSelectOpenItems(_terminalService), _terminalService.activeTabIndex, contextViewService, { ariaLabel: localize('terminals', 'Open Terminals.'), optionsAsChildren: true });
this._register(_terminalService.onInstancesChanged(this._updateItems, this));
this._register(_terminalService.onActiveTabChanged(this._updateItems, this));
diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts
index 801f7e4a6b8..eef4b20e865 100644
--- a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts
+++ b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts
@@ -11,6 +11,8 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger';
+import { IProductService } from 'vs/platform/product/common/productService';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@@ -22,8 +24,10 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@IInstantiationService instantiationService: IInstantiationService,
@IHostService hostService: IHostService,
@ITelemetryService telemetryService: ITelemetryService,
+ @IProductService productService: IProductService,
+ @IConfigurationService configurationService: IConfigurationService,
) {
- super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService);
+ super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService, productService, configurationService);
this._register(Event.debounce<string, string[]>(Event.any<string>(
Event.map(hostService.onDidChangeFocus, () => 'windowFocus'),
diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts
index e78b7edb957..f9b7763a8fd 100644
--- a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts
+++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts
@@ -78,6 +78,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
extension: reviveWebviewExtensionDescription(data.extensionId, data.extensionLocation),
iconPath: reviveIconPath(data.iconPath),
state: reviveState(data.state),
+ options: reviveOptions(data.options)
};
}
@@ -143,3 +144,10 @@ function reviveUri(data: string | UriComponents | undefined): URI | undefined {
function reviveState(state: unknown | undefined): undefined | string {
return typeof state === 'string' ? state : undefined;
}
+
+function reviveOptions(options: WebviewInputOptions): WebviewInputOptions {
+ return {
+ ...options,
+ localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)),
+ };
+}
diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
index dd9cf13f1ed..cb54735461f 100644
--- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
+++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { FindInPageOptions, OnBeforeRequestListenerDetails, OnHeadersReceivedListenerDetails, Response, WebContents, WebviewTag } from 'electron';
-import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { addDisposableListener } from 'vs/base/browser/dom';
+import { equals } from 'vs/base/common/arrays';
import { ThrottledDelayer } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { once } from 'vs/base/common/functional';
@@ -13,21 +13,22 @@ import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/
import { Schemas } from 'vs/base/common/network';
import { isMacintosh } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
+import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import * as modes from 'vs/editor/common/modes';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement';
import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping';
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { WebviewFindDelegate, WebviewFindWidget } from '../browser/webviewFindWidget';
-import { equals } from 'vs/base/common/arrays';
-
class WebviewTagHandle extends Disposable {
@@ -125,19 +126,18 @@ class WebviewProtocolProvider extends Disposable {
private readonly id: string,
private readonly extension: WebviewExtensionDescription | undefined,
initialLocalResourceRoots: ReadonlyArray<URI>,
+ private readonly _webviewManagerService: IWebviewManagerService,
) {
super();
this._localResourceRoots = initialLocalResourceRoots;
- ipcRenderer.send('vscode:registerWebview', this.id, {
+ this._ready = _webviewManagerService.registerWebview(this.id, {
extensionLocation: this.extension?.location.toJSON(),
localResourceRoots: initialLocalResourceRoots.map(x => x.toJSON()),
});
- this._ready = new Promise((resolve) => {
- ipcRenderer.once(`vscode:didRegisterWebview-${this.id}`, () => resolve());
- });
+ this._register(toDisposable(() => this._webviewManagerService.unregisterWebview(this.id)));
}
public update(localResourceRoots: ReadonlyArray<URI>) {
@@ -147,22 +147,12 @@ class WebviewProtocolProvider extends Disposable {
this._localResourceRoots = localResourceRoots;
- ipcRenderer.send('vscode:updateWebviewLocalResourceRoots', this.id, localResourceRoots.map(x => x.toJSON()));
-
- this._ready = new Promise((resolve) => {
- ipcRenderer.once(`vscode:didUpdateWebviewLocalResourceRoots-${this.id}`, () => resolve());
- });
+ this._ready = this._webviewManagerService.updateLocalResourceRoots(this.id, localResourceRoots.map(x => x.toJSON()));
}
async synchronize(): Promise<void> {
return this._ready;
}
-
- dispose() {
- super.dispose();
-
- ipcRenderer.send('vscode:unregisterWebview', this.id);
- }
}
class WebviewPortMappingProvider extends Disposable {
@@ -185,26 +175,30 @@ class WebviewPortMappingProvider extends Disposable {
class WebviewKeyboardHandler {
- private readonly _webviews = new Set<WebviewTagHandle>();
+ private readonly _webviews = new Set<WebviewTag>();
private readonly _isUsingNativeTitleBars: boolean;
- constructor(configurationService: IConfigurationService) {
+ private readonly webviewMainService: IWebviewManagerService;
+
+ constructor(
+ configurationService: IConfigurationService,
+ mainProcessService: IMainProcessService,
+ ) {
this._isUsingNativeTitleBars = configurationService.getValue<string>('window.titleBarStyle') === 'native';
+
+ this.webviewMainService = createChannelSender<IWebviewManagerService>(mainProcessService.getChannel('webview'));
}
- public add(
- webviewHandle: WebviewTagHandle,
- ): IDisposable {
- this._webviews.add(webviewHandle);
+ public add(webview: WebviewTag): IDisposable {
+ this._webviews.add(webview);
const disposables = new DisposableStore();
+
if (this.shouldToggleMenuShortcutsEnablement) {
- disposables.add(webviewHandle.onFirstLoad(() => {
- this.setIgnoreMenuShortcutsForWebview(webviewHandle, true);
- }));
+ this.setIgnoreMenuShortcutsForWebview(webview, true);
}
- disposables.add(addDisposableListener(webviewHandle.webview, 'ipc-message', (event) => {
+ disposables.add(addDisposableListener(webview, 'ipc-message', (event) => {
switch (event.channel) {
case 'did-focus':
this.setIgnoreMenuShortcuts(true);
@@ -218,7 +212,7 @@ class WebviewKeyboardHandler {
return toDisposable(() => {
disposables.dispose();
- this._webviews.delete(webviewHandle);
+ this._webviews.delete(webview);
});
}
@@ -232,12 +226,9 @@ class WebviewKeyboardHandler {
}
}
- private setIgnoreMenuShortcutsForWebview(webview: WebviewTagHandle, value: boolean) {
+ private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) {
if (this.shouldToggleMenuShortcutsEnablement) {
- const contents = webview.webContents;
- if (!contents?.isDestroyed()) {
- contents?.setIgnoreMenuShortcuts(value);
- }
+ this.webviewMainService.setIgnoreMenuShortcuts(webview.getWebContentsId(), value);
}
}
}
@@ -246,9 +237,12 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
private static _webviewKeyboardHandler: WebviewKeyboardHandler | undefined;
- private static getWebviewKeyboardHandler(configService: IConfigurationService) {
+ private static getWebviewKeyboardHandler(
+ configService: IConfigurationService,
+ mainProcessService: IMainProcessService,
+ ) {
if (!this._webviewKeyboardHandler) {
- this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService);
+ this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService, mainProcessService);
}
return this._webviewKeyboardHandler;
}
@@ -276,13 +270,16 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
@IEnvironmentService environementService: IEnvironmentService,
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IConfigurationService configurationService: IConfigurationService,
+ @IMainProcessService mainProcessService: IMainProcessService,
) {
super(id, options, contentOptions, extension, _webviewThemeDataProvider, telemetryService, environementService, workbenchEnvironmentService);
+ const webviewManagerService = createChannelSender<IWebviewManagerService>(mainProcessService.getChannel('webview'));
+
const webviewAndContents = this._register(new WebviewTagHandle(this.element!));
const session = this._register(new WebviewSession(webviewAndContents));
- this._protocolProvider = this._register(new WebviewProtocolProvider(id, extension, this.content.options.localResourceRoots || []));
+ this._protocolProvider = this._register(new WebviewProtocolProvider(id, extension, this.content.options.localResourceRoots || [], webviewManagerService));
this._register(new WebviewPortMappingProvider(
session,
@@ -291,7 +288,9 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
tunnelService,
));
- this._register(ElectronWebviewBasedWebview.getWebviewKeyboardHandler(configurationService).add(webviewAndContents));
+ this._register(addDisposableListener(this.element!, 'did-start-loading', once(() => {
+ this._register(ElectronWebviewBasedWebview.getWebviewKeyboardHandler(configurationService, mainProcessService).add(this.element!));
+ })));
this._domReady = new Promise(resolve => {
const subscription = this._register(this.on(WebviewMessageChannels.webviewReady, () => {
diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts
index 6f9ab62f25e..734d652d3a7 100644
--- a/src/vs/workbench/electron-browser/window.ts
+++ b/src/vs/workbench/electron-browser/window.ts
@@ -432,16 +432,16 @@ export class NativeWindow extends Disposable {
const companyName = product.crashReporter?.companyName || 'Microsoft';
const productName = product.crashReporter?.productName || product.nameShort;
- // With appCenter enabled, crashes will be uploaded
- if (product.appCenter) {
- this.setupCrashReporter(companyName, productName, product.appCenter, undefined);
- }
-
// With a provided crash reporter directory, crashes
// will be stored only locally in that folder
- else if (this.environmentService.crashReporterDirectory) {
+ if (this.environmentService.crashReporterDirectory) {
this.setupCrashReporter(companyName, productName, undefined, this.environmentService.crashReporterDirectory);
}
+
+ // With appCenter enabled, crashes will be uploaded
+ else if (product.appCenter) {
+ this.setupCrashReporter(companyName, productName, product.appCenter, undefined);
+ }
}
}
diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts
index 806f1706358..a61319c9113 100644
--- a/src/vs/workbench/services/search/common/search.ts
+++ b/src/vs/workbench/services/search/common/search.ts
@@ -350,6 +350,7 @@ export interface ISearchConfigurationProperties {
searchEditor: {
doubleClickBehaviour: 'selectWord' | 'goToLocation' | 'openLocationToSide',
reusePriorSearchConfiguration: boolean,
+ defaultShowContextValue: number | null,
experimental: {}
};
sortOrder: SearchSortOrder;
diff --git a/src/vs/workbench/services/title/electron-sandbox/titleService.ts b/src/vs/workbench/services/title/electron-sandbox/titleService.ts
index d19614e0e48..cace9e5cf37 100644
--- a/src/vs/workbench/services/title/electron-sandbox/titleService.ts
+++ b/src/vs/workbench/services/title/electron-sandbox/titleService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
-import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
+import { TitlebarPart } from 'vs/workbench/electron-sandbox/parts/titlebar/titlebarPart';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
registerSingleton(ITitleService, TitlebarPart);
diff --git a/src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts b/src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts
index 34dd86b86d5..f5b658992c7 100644
--- a/src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts
+++ b/src/vs/workbench/services/uriIdentity/common/uriIdentityService.ts
@@ -7,8 +7,18 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur
import { URI } from 'vs/base/common/uri';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
-import { binarySearch } from 'vs/base/common/arrays';
import { ExtUri, IExtUri, normalizePath } from 'vs/base/common/resources';
+import { SkipList } from 'vs/base/common/skipList';
+
+class Entry {
+ static _clock = 0;
+ time: number = Entry._clock++;
+ constructor(readonly uri: URI) { }
+ touch() {
+ this.time = Entry._clock++;
+ return this;
+ }
+}
export class UriIdentityService implements IUriIdentityService {
@@ -16,19 +26,17 @@ export class UriIdentityService implements IUriIdentityService {
readonly extUri: IExtUri;
- private _canonicalUris: URI[] = []; // use SkipList or BinaryTree instead of array...
- private readonly _limit = 10_000;
+ private readonly _canonicalUris: SkipList<URI, Entry>;
+ private readonly _limit = 2 ** 16;
constructor(@IFileService private readonly _fileService: IFileService) {
// assume path casing matters unless the file system provider spec'ed the opposite
const ignorePathCasing = (uri: URI): boolean => {
-
// perf@jrieken cache this information
if (this._fileService.canHandleResource(uri)) {
return !this._fileService.hasCapability(uri, FileSystemProviderCapabilities.PathCaseSensitive);
}
-
// this defaults to false which is a good default for
// * virtual documents
// * in-memory uris
@@ -36,6 +44,7 @@ export class UriIdentityService implements IUriIdentityService {
return false;
};
this.extUri = new ExtUri(ignorePathCasing);
+ this._canonicalUris = new SkipList((a, b) => this.extUri.compare(a, b, true), this._limit);
}
asCanonicalUri(uri: URI): URI {
@@ -53,19 +62,43 @@ export class UriIdentityService implements IUriIdentityService {
}
// (2) find the uri in its canonical form or use this uri to define it
- const idx = binarySearch(this._canonicalUris, uri, (a, b) => this.extUri.compare(a, b, true));
- if (idx >= 0) {
- return this._canonicalUris[idx].with({ fragment: uri.fragment });
+ let item = this._canonicalUris.get(uri);
+ if (item) {
+ return item.touch().uri.with({ fragment: uri.fragment });
}
- // using slice/concat is faster than splice
- // total len should be being _limit and 2*_limit
- const insertIdx = ~idx;
- const before = this._canonicalUris.slice(Math.max(0, insertIdx - this._limit), insertIdx);
- const after = this._canonicalUris.slice(insertIdx, insertIdx + this._limit);
- this._canonicalUris = before.concat(uri.with({ fragment: null }), after);
+ // this uri is first and defines the canonical form
+ this._canonicalUris.set(uri, new Entry(uri));
+ this._checkTrim();
+
return uri;
}
+
+ private _checkTrim(): void {
+ if (this._canonicalUris.size < this._limit) {
+ return;
+ }
+
+ // get all entries, sort by touch (MRU) and re-initalize
+ // the uri cache and the entry clock. this is an expensive
+ // operation and should happen rarely
+ const entries = [...this._canonicalUris.entries()].sort((a, b) => {
+ if (a[1].touch < b[1].touch) {
+ return 1;
+ } else if (a[1].touch > b[1].touch) {
+ return -1;
+ } else {
+ return 0;
+ }
+ });
+
+ Entry._clock = 0;
+ this._canonicalUris.clear();
+ const newSize = this._limit * 0.5;
+ for (let i = 0; i < newSize; i++) {
+ this._canonicalUris.set(entries[i][0], entries[i][1].touch());
+ }
+ }
}
registerSingleton(IUriIdentityService, UriIdentityService, true);
diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts
index 5503ea47f21..26b9fde6c3b 100644
--- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts
+++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts
@@ -116,7 +116,7 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [0, 0, [{
+ changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
@@ -130,7 +130,7 @@ suite('NotebookConcatDocument', function () {
language: 'test',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
@@ -155,14 +155,14 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [0, 0, [{
+ changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
assert.equal(notebook.cells.length, 1 + 1);
assert.equal(doc.version, 1);
@@ -177,14 +177,14 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [1, 0, [{
+ changes: [[1, 0, [{
handle: 2,
uri: CellUri.generate(notebook.uri, 2),
source: ['Hallo', 'Welt', 'Hallo Welt!'],
language: 'test',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
assert.equal(notebook.cells.length, 1 + 2);
@@ -200,7 +200,7 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [1, 1, []]
+ changes: [[1, 1, []]]
});
assert.equal(notebook.cells.length, 1 + 1);
assert.equal(doc.version, 3);
@@ -218,7 +218,7 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [0, 0, [{
+ changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
@@ -232,7 +232,7 @@ suite('NotebookConcatDocument', function () {
language: 'test',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
assert.equal(notebook.cells.length, 1 + 2);
assert.equal(doc.version, 1);
@@ -281,7 +281,7 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [0, 0, [{
+ changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['fooLang-document'],
@@ -295,7 +295,7 @@ suite('NotebookConcatDocument', function () {
language: 'barLang',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook, undefined);
@@ -309,14 +309,14 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [2, 0, [{
+ changes: [[2, 0, [{
handle: 3,
uri: CellUri.generate(notebook.uri, 3),
source: ['barLang-document2'],
language: 'barLang',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
assertLines(mixedDoc, 'fooLang-document', 'barLang-document', 'barLang-document2');
@@ -342,7 +342,7 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [0, 0, [{
+ changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
@@ -356,7 +356,7 @@ suite('NotebookConcatDocument', function () {
language: 'test',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
assert.equal(notebook.cells.length, 1 + 2); // markdown and code
@@ -393,7 +393,7 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [0, 0, [{
+ changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
@@ -407,7 +407,7 @@ suite('NotebookConcatDocument', function () {
language: 'test',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
assert.equal(notebook.cells.length, 1 + 2); // markdown and code
@@ -428,7 +428,7 @@ suite('NotebookConcatDocument', function () {
extHostNotebooks.$acceptModelChanged(notebookUri, {
kind: NotebookCellsChangeType.ModelChange,
versionId: notebook.versionId + 1,
- change: [0, 0, [{
+ changes: [[0, 0, [{
handle: 1,
uri: CellUri.generate(notebook.uri, 1),
source: ['Hello', 'World', 'Hello World!'],
@@ -442,7 +442,7 @@ suite('NotebookConcatDocument', function () {
language: 'test',
cellKind: CellKind.Code,
outputs: [],
- }]]
+ }]]]
});
assert.equal(notebook.cells.length, 1 + 2); // markdown and code
diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts
index f72791f8d57..74b7f7f8080 100644
--- a/src/vs/workbench/workbench.web.api.ts
+++ b/src/vs/workbench/workbench.web.api.ts
@@ -35,6 +35,17 @@ interface IExternalUriResolver {
(uri: URI): Promise<URI>;
}
+interface ITunnelProvider {
+ /**
+ * Support for creating tunnels.
+ */
+ tunnelFactory?: ITunnelFactory;
+ /**
+ * Support for filtering candidate ports
+ */
+ showPortCandidate?: IShowPortCandidate;
+}
+
interface ITunnelFactory {
(tunnelOptions: ITunnelOptions): Promise<ITunnel> | undefined;
}
@@ -198,14 +209,10 @@ interface IWorkbenchConstructionOptions {
readonly resolveExternalUri?: IExternalUriResolver;
/**
- * Support for creating tunnels.
- */
- readonly tunnelFactory?: ITunnelFactory;
-
- /**
- * Support for filtering candidate ports
+ * A provider for supplying tunneling functionality,
+ * such as creating tunnels and showing candidate ports to forward.
*/
- readonly showCandidate?: IShowPortCandidate;
+ readonly tunnelProvider?: ITunnelProvider;
//#endregion
@@ -410,6 +417,7 @@ export {
IExternalUriResolver,
// Tunnel
+ ITunnelProvider,
ITunnelFactory,
ITunnel,
ITunnelOptions,
diff --git a/test/unit/electron/index.js b/test/unit/electron/index.js
index 93a49579361..1911ffc1772 100644
--- a/test/unit/electron/index.js
+++ b/test/unit/electron/index.js
@@ -116,7 +116,8 @@ app.on('ready', () => {
nodeIntegration: true,
webSecurity: false,
webviewTag: true,
- preload: path.join(__dirname, '..', '..', '..', 'src', 'vs', 'base', 'parts', 'sandbox', 'electron-browser', 'preload.js') // ensure similar environment as VSCode as tests may depend on this
+ preload: path.join(__dirname, '..', '..', '..', 'src', 'vs', 'base', 'parts', 'sandbox', 'electron-browser', 'preload.js'), // ensure similar environment as VSCode as tests may depend on this
+ enableWebSQL: false
}
});
diff --git a/yarn.lock b/yarn.lock
index e559825232d..551040d97ce 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2715,10 +2715,10 @@ electron-to-chromium@^1.2.7:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d"
integrity sha1-eOy4o5kGYYe7N07t412ccFZagD0=
-electron@7.2.4:
- version "7.2.4"
- resolved "https://registry.yarnpkg.com/electron/-/electron-7.2.4.tgz#9fc0446dae23ead897af8742470cb18da55c6ce9"
- integrity sha512-Z+R692uTzXgP8AHrabE+kkrMlQJ6pnAYoINenwj9QSqaD2YbO8IuXU9DMCcUY0+VpA91ee09wFZJNUKYPMnCKg==
+electron@7.3.0:
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/electron/-/electron-7.3.0.tgz#b4e1bd1efe32314534dba9b60ea5cc688216f3b3"
+ integrity sha512-uG3WVhyawWXBUJN+f5GkdCq8z5r+XsvpLLAyUuPo+DL2PXt57YI1wEXc5qTJq9hzlLAcGOlU/4+8pSkYBWOn1A==
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^12.0.12"