diff --git a/.eslintrc.json b/.eslintrc.json index 8575a4bd391..08f5bfd644b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1066,7 +1066,7 @@ { "files": [ "**/vscode.d.ts", - "**/vscode.proposed.d.ts" + "**/vscode.proposed.*.d.ts" ], "rules": { "vscode-dts-create-func": "warn", diff --git a/.vscode/settings.json b/.vscode/settings.json index a6cd70e725d..d03741307f0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -86,6 +86,7 @@ "splitview", "table", "list", - "git" + "git", + "sash" ] } diff --git a/.yarnrc b/.yarnrc index 0e39a85f525..481bf7bfa46 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,4 +1,4 @@ disturl "https://electronjs.org/headers" -target "13.5.1" +target "13.5.2" runtime "electron" build_from_source "true" diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index f21bd633a76..ce62104f949 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -233,7 +233,7 @@ steps: APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) APP_NAME="`ls $APP_ROOT | head -n 1`" VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ - yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests + yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote timeoutInMinutes: 5 displayName: Run smoke tests (Remote) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 5c742c2503c..a1bcb6a8a90 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -220,7 +220,7 @@ steps: set -e APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --build "$APP_PATH" --remote --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests + yarn smoketest-no-compile --build "$APP_PATH" --remote --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote timeoutInMinutes: 5 displayName: Run smoke tests (Remote) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) diff --git a/build/azure-pipelines/publish-types/update-types.js b/build/azure-pipelines/publish-types/update-types.js index 3ceb35bdb5c..e22911b8470 100644 --- a/build/azure-pipelines/publish-types/update-types.js +++ b/build/azure-pipelines/publish-types/update-types.js @@ -13,7 +13,7 @@ try { .execSync('git describe --tags `git rev-list --tags --max-count=1`') .toString() .trim(); - const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`; + const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vscode-dts/vscode.d.ts`; const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts'); cp.execSync(`curl ${dtsUri} --output ${outPath}`); updateDTSFile(outPath, tag); diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index eae002e23a7..7ccd976dba5 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -16,7 +16,7 @@ try { .toString() .trim(); - const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`; + const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vscode-dts/vscode.d.ts`; const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts'); cp.execSync(`curl ${dtsUri} --output ${outPath}`); diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index d365c165f8b..ccd863c615b 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -216,7 +216,7 @@ steps: $ErrorActionPreference = "Stop" $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --build "$AppRoot" --remote } + exec { yarn smoketest-no-compile --build "$AppRoot" --remote --screenshots $(Build.SourcesDirectory)\.build\logs\smoke-tests-remote } displayName: Run smoke tests (Remote) timeoutInMinutes: 5 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) diff --git a/build/gulpfile.js b/build/gulpfile.js index efb00f69481..dc043d7b5a7 100644 --- a/build/gulpfile.js +++ b/build/gulpfile.js @@ -16,10 +16,10 @@ const { monacoTypecheckTask/* , monacoTypecheckWatchTask */ } = require('./gulpf const { compileExtensionsTask, watchExtensionsTask, compileExtensionMediaTask } = require('./gulpfile.extensions'); // Fast compile for development time -const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compilation.compileTask('src', 'out', false))); +const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compilation.compileApiProposalNames(), compilation.compileTask('src', 'out', false))); gulp.task(compileClientTask); -const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compilation.watchTask('out', false))); +const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), task.parallel(compilation.watchTask('out', false), compilation.watchApiProposalNames()))); gulp.task(watchClientTask); // All diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index fbf4739f2ff..e292abeda2c 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -64,6 +64,7 @@ const vscodeResources = [ 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/base/parts/sandbox/electron-browser/preload.js', 'out-build/vs/platform/environment/node/userDataPath.js', + 'out-build/vs/platform/extensions/node/extensionHostStarterWorkerMain.js', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', @@ -214,7 +215,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const license = gulp.src(['LICENSES.chromium.html', product.licenseFileName, 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.', allowEmpty: true }); // TODO the API should be copied to `out` during compile, not here - const api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts')); + const api = gulp.src('src/vscode-dts/vscode.d.ts').pipe(rename('out/vscode-dts/vscode.d.ts')); const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true }); diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 7d0c8d9b55e..143e64e087d 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -14,6 +14,9 @@ const vm = require("vm"); function bundle(entryPoints, config, callback) { const entryPointsMap = {}; entryPoints.forEach((module) => { + if (entryPointsMap[module.name]) { + throw new Error(`Cannot have two entry points with the same name '${module.name}'`); + } entryPointsMap[module.name] = module; }); const allMentionedModulesMap = {}; diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index 562b050d1fd..ce1f6e61662 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -100,6 +100,9 @@ export interface ILoaderConfig { export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callback: (err: any, result: IBundleResult | null) => void): void { const entryPointsMap: IEntryPointMap = {}; entryPoints.forEach((module: IEntryPoint) => { + if (entryPointsMap[module.name]) { + throw new Error(`Cannot have two entry points with the same name '${module.name}'`); + } entryPointsMap[module.name] = module; }); diff --git a/build/lib/compilation.js b/build/lib/compilation.js index c6e9196abce..b81e714fa41 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.watchTask = exports.compileTask = void 0; +exports.watchApiProposalNames = exports.compileApiProposalNames = exports.watchTask = exports.compileTask = void 0; const es = require("event-stream"); const fs = require("fs"); const gulp = require("gulp"); @@ -175,3 +175,65 @@ class MonacoGenerator { } } } +function apiProposalNamesGenerator() { + const stream = es.through(); + const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts/; + const dtsFolder = path.join(REPO_SRC_FOLDER, 'vscode-dts'); + const generateFile = () => { + try { + const t1 = Date.now(); + const proposalNames = []; + for (let file of fs.readdirSync(dtsFolder).sort()) { + const match = pattern.exec(file); + if (match) { + proposalNames.push([match[1], `https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/${file}`]); + } + } + const source = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the MIT License. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', + '', + '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', + '', + 'export const allApiProposals = Object.freeze({', + `${proposalNames.map(t => `\t${t[0]}: '${t[1]}'`).join(',\n')}`, + '});', + 'export type ApiProposalName = keyof typeof allApiProposals;', + '', + ].join('\n'); + const outFile = path.join(dtsFolder, '../vs/workbench/services/extensions/common/extensionsApiProposals.ts'); + if (fs.readFileSync(outFile).toString() !== source) { + fs.writeFileSync(outFile, source); + console.log(`Generated 'extensionsApiProposals.ts' in ${Date.now() - t1}ms`); + } + } + catch (err) { + stream.emit('error', err); + } + }; + let handle; + stream.on('data', () => { + clearTimeout(handle); + handle = setTimeout(generateFile, 250); + }); + return stream; +} +function compileApiProposalNames() { + return function () { + const srcPipe = gulp.src('src/vscode-dts/**', { base: 'src' }); + const proposals = apiProposalNamesGenerator(); + return srcPipe.pipe(proposals); + }; +} +exports.compileApiProposalNames = compileApiProposalNames; +function watchApiProposalNames() { + return function () { + const watchSrc = watch('src/vscode-dts/**', { base: 'src', readDelay: 200 }); + const proposals = apiProposalNamesGenerator(); + proposals.write(undefined); // send something to trigger initial generate + return watchSrc.pipe(proposals); + }; +} +exports.watchApiProposalNames = watchApiProposalNames; diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 3db8cf0f917..892acaa4216 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -211,3 +211,75 @@ class MonacoGenerator { } } } + +function apiProposalNamesGenerator() { + const stream = es.through(); + + const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts/; + const dtsFolder = path.join(REPO_SRC_FOLDER, 'vscode-dts'); + + const generateFile = () => { + + try { + + const t1 = Date.now(); + const proposalNames: [name: string, url: string][] = []; + for (let file of fs.readdirSync(dtsFolder).sort()) { + const match = pattern.exec(file); + if (match) { + proposalNames.push([match[1], `https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/${file}`]); + } + } + + const source = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the MIT License. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', + '', + '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', + '', + 'export const allApiProposals = Object.freeze({', + `${proposalNames.map(t => `\t${t[0]}: '${t[1]}'`).join(',\n')}`, + '});', + 'export type ApiProposalName = keyof typeof allApiProposals;', + '', + ].join('\n'); + + const outFile = path.join(dtsFolder, '../vs/workbench/services/extensions/common/extensionsApiProposals.ts'); + + if (fs.readFileSync(outFile).toString() !== source) { + fs.writeFileSync(outFile, source); + console.log(`Generated 'extensionsApiProposals.ts' in ${Date.now() - t1}ms`); + } + + } catch (err) { + stream.emit('error', err); + } + }; + + let handle: NodeJS.Timeout; + stream.on('data', () => { + clearTimeout(handle); + handle = setTimeout(generateFile, 250); + }); + + return stream; +} + +export function compileApiProposalNames(): () => NodeJS.ReadWriteStream { + return function () { + const srcPipe = gulp.src('src/vscode-dts/**', { base: 'src' }); + const proposals = apiProposalNamesGenerator(); + return srcPipe.pipe(proposals); + }; +} + +export function watchApiProposalNames(): () => NodeJS.ReadWriteStream { + return function () { + const watchSrc = watch('src/vscode-dts/**', { base: 'src', readDelay: 200 }); + const proposals = apiProposalNamesGenerator(); + proposals.write(undefined); // send something to trigger initial generate + return watchSrc.pipe(proposals); + }; +} diff --git a/build/lib/eslint/vscode-dts-region-comments.js b/build/lib/eslint/vscode-dts-region-comments.js index 7d37a20fb6a..2dc9487314e 100644 --- a/build/lib/eslint/vscode-dts-region-comments.js +++ b/build/lib/eslint/vscode-dts-region-comments.js @@ -7,7 +7,7 @@ module.exports = new class ApiEventNaming { constructor() { this.meta = { messages: { - comment: 'region comments should start with the GH issue link, e.g #region https://github.com/microsoft/vscode/issues/', + comment: 'region comments should start with a camel case identifier, `:`, then either a GH issue link or owner, e.g #region myProposalName: https://github.com/microsoft/vscode/issues/', } }; } @@ -15,14 +15,14 @@ module.exports = new class ApiEventNaming { const sourceCode = context.getSourceCode(); return { ['Program']: (_node) => { - for (let comment of sourceCode.getAllComments()) { + for (const comment of sourceCode.getAllComments()) { if (comment.type !== 'Line') { continue; } - if (!comment.value.match(/^\s*#region /)) { + if (!/^\s*#region /.test(comment.value)) { continue; } - if (!comment.value.match(/https:\/\/github.com\/microsoft\/vscode\/issues\/\d+/i)) { + if (!/^\s*#region ([a-z]+): (@[a-z]+|https:\/\/github.com\/microsoft\/vscode\/issues\/\d+)/i.test(comment.value)) { context.report({ node: comment, messageId: 'comment', diff --git a/build/lib/eslint/vscode-dts-region-comments.ts b/build/lib/eslint/vscode-dts-region-comments.ts index 175fb9040ab..63139a50e3b 100644 --- a/build/lib/eslint/vscode-dts-region-comments.ts +++ b/build/lib/eslint/vscode-dts-region-comments.ts @@ -9,7 +9,7 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { - comment: 'region comments should start with the GH issue link, e.g #region https://github.com/microsoft/vscode/issues/', + comment: 'region comments should start with a camel case identifier, `:`, then either a GH issue link or owner, e.g #region myProposalName: https://github.com/microsoft/vscode/issues/', } }; @@ -17,18 +17,16 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { const sourceCode = context.getSourceCode(); - return { ['Program']: (_node: any) => { - - for (let comment of sourceCode.getAllComments()) { + for (const comment of sourceCode.getAllComments()) { if (comment.type !== 'Line') { continue; } - if (!comment.value.match(/^\s*#region /)) { + if (!/^\s*#region /.test(comment.value)) { continue; } - if (!comment.value.match(/https:\/\/github.com\/microsoft\/vscode\/issues\/\d+/i)) { + if (!/^\s*#region ([a-z]+): (@[a-z]+|https:\/\/github.com\/microsoft\/vscode\/issues\/\d+)/i.test(comment.value)) { context.report({ node: comment, messageId: 'comment', diff --git a/build/monaco/package.json b/build/monaco/package.json index b987a610d72..2e30e04628e 100644 --- a/build/monaco/package.json +++ b/build/monaco/package.json @@ -1,7 +1,7 @@ { "name": "monaco-editor-core", "private": true, - "version": "0.29.2", + "version": "0.30.0", "description": "A browser based code editor", "author": "Microsoft Corporation", "license": "MIT", diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index 34618a58b68..7fb38af7121 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -8,7 +8,7 @@ let err = false; const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]); if (majorNodeVersion < 14 || majorNodeVersion >= 17) { - console.error('\033[1;31m*** Please use node.js versions >=14 and <=17.\033[0;0m'); + console.error('\033[1;31m*** Please use node.js versions >=14 and <17.\033[0;0m'); err = true; } diff --git a/extensions/configuration-editing/src/typings/ref.d.ts b/extensions/configuration-editing/src/typings/ref.d.ts deleted file mode 100644 index bc057c55878..00000000000 --- a/extensions/configuration-editing/src/typings/ref.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/configuration-editing/tsconfig.json b/extensions/configuration-editing/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/configuration-editing/tsconfig.json +++ b/extensions/configuration-editing/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/css-language-features/client/src/typings/ref.d.ts b/extensions/css-language-features/client/src/typings/ref.d.ts deleted file mode 100644 index de602d3f8d6..00000000000 --- a/extensions/css-language-features/client/src/typings/ref.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/// -/// diff --git a/extensions/css-language-features/client/tsconfig.json b/extensions/css-language-features/client/tsconfig.json index 8b4aedde27d..573b24b4aa6 100644 --- a/extensions/css-language-features/client/tsconfig.json +++ b/extensions/css-language-features/client/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./out" }, "include": [ - "src/**/*" + "src/**/*", + "../../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 7c3b8e75472..88994a67f44 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -17,7 +17,6 @@ ], "main": "./client/out/node/cssClientMain", "browser": "./client/dist/browser/cssClientMain", - "enableProposedApi": true, "capabilities": { "virtualWorkspaces": true, "untrustedWorkspaces": { diff --git a/extensions/debug-auto-launch/tsconfig.json b/extensions/debug-auto-launch/tsconfig.json index 69aa5aa3a1e..bfcf873c749 100644 --- a/extensions/debug-auto-launch/tsconfig.json +++ b/extensions/debug-auto-launch/tsconfig.json @@ -2,9 +2,13 @@ "extends": "../tsconfig.base.json", "compilerOptions": { "outDir": "./out", - "downlevelIteration": true + "downlevelIteration": true, + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index e5c69df006b..64aa227aa53 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -18,7 +18,9 @@ "supported": true } }, - "enableProposedApi": true, + "enabledApiProposals": [ + "terminalDataWriteEvent" + ], "main": "./out/extension", "scripts": { "compile": "gulp compile-extension:debug-server-ready", diff --git a/extensions/debug-server-ready/src/typings/ref.d.ts b/extensions/debug-server-ready/src/typings/ref.d.ts deleted file mode 100644 index 954bab971e3..00000000000 --- a/extensions/debug-server-ready/src/typings/ref.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// -/// diff --git a/extensions/debug-server-ready/tsconfig.json b/extensions/debug-server-ready/tsconfig.json index 69aa5aa3a1e..9bf747283ca 100644 --- a/extensions/debug-server-ready/tsconfig.json +++ b/extensions/debug-server-ready/tsconfig.json @@ -2,9 +2,14 @@ "extends": "../tsconfig.base.json", "compilerOptions": { "outDir": "./out", - "downlevelIteration": true + "downlevelIteration": true, + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts", ] } diff --git a/extensions/diff/.vscodeignore b/extensions/diff/.vscodeignore new file mode 100644 index 00000000000..d9011becfb6 --- /dev/null +++ b/extensions/diff/.vscodeignore @@ -0,0 +1,2 @@ +build/** +cgmanifest.json diff --git a/extensions/diff/cgmanifest.json b/extensions/diff/cgmanifest.json new file mode 100644 index 00000000000..04d6573c95e --- /dev/null +++ b/extensions/diff/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/diff.tmbundle", + "repositoryUrl": "https://github.com/textmate/diff.tmbundle", + "commitHash": "0593bb775eab1824af97ef2172fd38822abd97d7" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-diff.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/git/languages/diff.language-configuration.json b/extensions/diff/language-configuration.json similarity index 98% rename from extensions/git/languages/diff.language-configuration.json rename to extensions/diff/language-configuration.json index b61fbe63d34..395aff60535 100644 --- a/extensions/git/languages/diff.language-configuration.json +++ b/extensions/diff/language-configuration.json @@ -8,4 +8,4 @@ ["[", "]"], ["(", ")"] ] -} \ No newline at end of file +} diff --git a/extensions/diff/package.json b/extensions/diff/package.json new file mode 100644 index 00000000000..7d23e24ac6b --- /dev/null +++ b/extensions/diff/package.json @@ -0,0 +1,38 @@ +{ + "name": "diff", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "license": "MIT", + "engines": { + "vscode": "0.10.x" + }, + "scripts": { + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/diff.tmbundle Syntaxes/Diff.plist ./syntaxes/diff.tmLanguage.json" + }, + "contributes": { + "languages": [ + { + "id": "diff", + "aliases": [ + "Diff", + "diff" + ], + "extensions": [ + ".diff", + ".patch", + ".rej" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "diff", + "scopeName": "source.diff", + "path": "./syntaxes/diff.tmLanguage.json" + } + ] + } +} diff --git a/extensions/diff/package.nls.json b/extensions/diff/package.nls.json new file mode 100644 index 00000000000..c869050177e --- /dev/null +++ b/extensions/diff/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Diff Language Basics", + "description": "Provides syntax highlighting & bracket matching in Diff files." +} diff --git a/extensions/git/syntaxes/diff.tmLanguage.json b/extensions/diff/syntaxes/diff.tmLanguage.json similarity index 100% rename from extensions/git/syntaxes/diff.tmLanguage.json rename to extensions/diff/syntaxes/diff.tmLanguage.json diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 9e55e76a1df..45a9510acd5 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -49,7 +49,7 @@ export async function wrapWithAbbreviation(args: any): Promise { const helper = getEmmetHelper(); - const operationRanges = editor.selections.sort((a, b) => a.start.compareTo(b.start)).map(selection => { + const operationRanges = Array.from(editor.selections).sort((a, b) => a.start.compareTo(b.start)).map(selection => { let rangeToReplace: vscode.Range = selection; // wrap around the node if the selection falls inside its open or close tag { diff --git a/extensions/emmet/src/balance.ts b/extensions/emmet/src/balance.ts index 4ddddacb7d1..0b5710ca898 100644 --- a/extensions/emmet/src/balance.ts +++ b/extensions/emmet/src/balance.ts @@ -8,8 +8,8 @@ import { getHtmlFlatNode, offsetRangeToSelection, validate } from './util'; import { getRootNode } from './parseDocument'; import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode'; -let balanceOutStack: Array = []; -let lastBalancedSelections: vscode.Selection[] = []; +let balanceOutStack: Array = []; +let lastBalancedSelections: readonly vscode.Selection[] = []; export function balanceOut() { balance(true); @@ -31,10 +31,8 @@ function balance(out: boolean) { } const rangeFn = out ? getRangeToBalanceOut : getRangeToBalanceIn; - let newSelections: vscode.Selection[] = []; - editor.selections.forEach(selection => { - const range = rangeFn(document, rootNode, selection); - newSelections.push(range); + let newSelections: readonly vscode.Selection[] = editor.selections.map(selection => { + return rangeFn(document, rootNode, selection); }); // check whether we are starting a balance elsewhere @@ -122,7 +120,7 @@ function getRangeToBalanceIn(document: vscode.TextDocument, rootNode: HtmlFlatNo return offsetRangeToSelection(document, firstChild.start, firstChild.end); } -function areSameSelections(a: vscode.Selection[], b: vscode.Selection[]): boolean { +function areSameSelections(a: readonly vscode.Selection[], b: readonly vscode.Selection[]): boolean { if (a.length !== b.length) { return false; } diff --git a/extensions/emmet/src/mergeLines.ts b/extensions/emmet/src/mergeLines.ts index ef2f37f7ddc..4e5fa81915f 100644 --- a/extensions/emmet/src/mergeLines.ts +++ b/extensions/emmet/src/mergeLines.ts @@ -21,7 +21,7 @@ export function mergeLines() { } return editor.edit(editBuilder => { - editor.selections.reverse().forEach(selection => { + Array.from(editor.selections).reverse().forEach(selection => { const textEdit = getRangesToReplace(editor.document, selection, rootNode); if (textEdit) { editBuilder.replace(textEdit.range, textEdit.newText); diff --git a/extensions/emmet/src/removeTag.ts b/extensions/emmet/src/removeTag.ts index 89f91e6b026..5e46eff08e6 100644 --- a/extensions/emmet/src/removeTag.ts +++ b/extensions/emmet/src/removeTag.ts @@ -19,7 +19,7 @@ export function removeTag() { return; } - let finalRangesToRemove = editor.selections.reverse() + let finalRangesToRemove = Array.from(editor.selections).reverse() .reduce((prev, selection) => prev.concat(getRangesToRemove(editor.document, rootNode, selection)), []); diff --git a/extensions/emmet/src/splitJoinTag.ts b/extensions/emmet/src/splitJoinTag.ts index 0ff66211d24..2b478f68ae4 100644 --- a/extensions/emmet/src/splitJoinTag.ts +++ b/extensions/emmet/src/splitJoinTag.ts @@ -21,7 +21,7 @@ export function splitJoinTag() { } return editor.edit(editBuilder => { - editor.selections.reverse().forEach(selection => { + Array.from(editor.selections).reverse().forEach(selection => { const documentText = document.getText(); const offset = document.offsetAt(selection.start); const nodeToUpdate = getHtmlFlatNode(documentText, rootNode, offset, true); diff --git a/extensions/emmet/src/toggleComment.ts b/extensions/emmet/src/toggleComment.ts index 1afb8fe2239..e40f675c3eb 100644 --- a/extensions/emmet/src/toggleComment.ts +++ b/extensions/emmet/src/toggleComment.ts @@ -28,7 +28,7 @@ export function toggleComment(): Thenable | undefined { return editor.edit(editBuilder => { let allEdits: vscode.TextEdit[][] = []; - editor.selections.reverse().forEach(selection => { + Array.from(editor.selections).reverse().forEach(selection => { const edits = isStyleSheet(editor.document.languageId) ? toggleCommentStylesheet(editor.document, selection, rootNode) : toggleCommentHTML(editor.document, selection, rootNode!); if (edits.length > 0) { allEdits.push(edits); diff --git a/extensions/emmet/src/typings/refs.d.ts b/extensions/emmet/src/typings/refs.d.ts index bc057c55878..8ea9f802a47 100644 --- a/extensions/emmet/src/typings/refs.d.ts +++ b/extensions/emmet/src/typings/refs.d.ts @@ -3,5 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// /// diff --git a/extensions/emmet/src/updateImageSize.ts b/extensions/emmet/src/updateImageSize.ts index 21b999b595a..dadfe49fd89 100644 --- a/extensions/emmet/src/updateImageSize.ts +++ b/extensions/emmet/src/updateImageSize.ts @@ -23,7 +23,7 @@ export function updateImageSize(): Promise | undefined { } const editor = window.activeTextEditor; - const allUpdatesPromise = editor.selections.reverse().map(selection => { + const allUpdatesPromise = Array.from(editor.selections).reverse().map(selection => { const position = selection.isReversed ? selection.active : selection.anchor; if (!isStyleSheet(editor.document.languageId)) { return updateImageSizeHTML(editor, position); diff --git a/extensions/emmet/src/updateTag.ts b/extensions/emmet/src/updateTag.ts index 0211df153bd..bfb76671993 100644 --- a/extensions/emmet/src/updateTag.ts +++ b/extensions/emmet/src/updateTag.ts @@ -25,7 +25,7 @@ export async function updateTag(tagName: string | undefined): Promise((prev, selection) => prev.concat(getRangesToUpdate(document, selection, rootNode)), []); if (!rangesToUpdate.length) { diff --git a/extensions/emmet/tsconfig.json b/extensions/emmet/tsconfig.json index 22cffd9cde0..994fa239537 100644 --- a/extensions/emmet/tsconfig.json +++ b/extensions/emmet/tsconfig.json @@ -8,6 +8,7 @@ ".vscode-test" ], "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/extension-editing/src/typings/ref.d.ts b/extensions/extension-editing/src/typings/ref.d.ts deleted file mode 100644 index 216911a680e..00000000000 --- a/extensions/extension-editing/src/typings/ref.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// diff --git a/extensions/extension-editing/tsconfig.json b/extensions/extension-editing/tsconfig.json index 5a23aaef103..3714bd09958 100644 --- a/extensions/extension-editing/tsconfig.json +++ b/extensions/extension-editing/tsconfig.json @@ -7,6 +7,7 @@ ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/git/build/update-grammars.js b/extensions/git/build/update-grammars.js index a5dae9f6850..ad326bff374 100644 --- a/extensions/git/build/update-grammars.js +++ b/extensions/git/build/update-grammars.js @@ -8,9 +8,3 @@ var updateGrammar = require('vscode-grammar-updater'); updateGrammar.update('textmate/git.tmbundle', 'Syntaxes/Git%20Commit%20Message.tmLanguage', './syntaxes/git-commit.tmLanguage.json'); updateGrammar.update('textmate/git.tmbundle', 'Syntaxes/Git%20Rebase%20Message.tmLanguage', './syntaxes/git-rebase.tmLanguage.json'); -updateGrammar.update('textmate/diff.tmbundle', 'Syntaxes/Diff.plist', './syntaxes/diff.tmLanguage.json'); - - - - - diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json index e8081d6472e..256966aba20 100644 --- a/extensions/git/cgmanifest.json +++ b/extensions/git/cgmanifest.json @@ -33,34 +33,7 @@ ], "license": "MIT", "version": "0.0.0" - }, - { - "component": { - "type": "git", - "git": { - "name": "textmate/diff.tmbundle", - "repositoryUrl": "https://github.com/textmate/diff.tmbundle", - "commitHash": "0593bb775eab1824af97ef2172fd38822abd97d7" - } - }, - "licenseDetail": [ - "Copyright (c) textmate-diff.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ], - "license": "TextMate Bundle License", - "version": "0.0.0" } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/git/package.json b/extensions/git/package.json index b778540aa91..d6f792d939e 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -9,7 +9,14 @@ "vscode": "^1.5.0" }, "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "enableProposedApi": true, + "enabledApiProposals": [ + "diffCommand", + "contribViewsWelcome", + "scmActionButton", + "scmSelectedProvider", + "scmValidation", + "timeline" + ], "categories": [ "Other" ], @@ -2314,19 +2321,6 @@ ], "configuration": "./languages/git-rebase.language-configuration.json" }, - { - "id": "diff", - "aliases": [ - "Diff", - "diff" - ], - "extensions": [ - ".diff", - ".patch", - ".rej" - ], - "configuration": "./languages/diff.language-configuration.json" - }, { "id": "ignore", "aliases": [ @@ -2351,11 +2345,6 @@ "scopeName": "text.git-rebase", "path": "./syntaxes/git-rebase.tmLanguage.json" }, - { - "language": "diff", - "scopeName": "source.diff", - "path": "./syntaxes/diff.tmLanguage.json" - }, { "language": "ignore", "scopeName": "source.ignore", diff --git a/extensions/git/src/askpass.sh b/extensions/git/src/askpass.sh index d19b62affa3..4536224764d 100755 --- a/extensions/git/src/askpass.sh +++ b/extensions/git/src/askpass.sh @@ -1,5 +1,5 @@ #!/bin/sh VSCODE_GIT_ASKPASS_PIPE=`mktemp` -ELECTRON_RUN_AS_NODE="1" VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_MAIN" $* +ELECTRON_RUN_AS_NODE="1" VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_MAIN" $VSCODE_GIT_ASKPASS_EXTRA_ARGS $* cat $VSCODE_GIT_ASKPASS_PIPE rm $VSCODE_GIT_ASKPASS_PIPE diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 7fc29a371ad..e6c21efa9cb 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -83,6 +83,7 @@ export class Askpass implements IIPCHandler { ...this.ipc.getEnv(), GIT_ASKPASS: path.join(__dirname, 'askpass.sh'), VSCODE_GIT_ASKPASS_NODE: process.execPath, + VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js') }; } diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 2db9bf84c9d..c2af4a575b5 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -49,7 +49,7 @@ export function applyLineChanges(original: TextDocument, modified: TextDocument, return result.join(''); } -export function toLineRanges(selections: Selection[], textDocument: TextDocument): Range[] { +export function toLineRanges(selections: readonly Selection[], textDocument: TextDocument): Range[] { const lineRanges = selections.map(s => { const startLine = textDocument.lineAt(s.start.line); const endLine = textDocument.lineAt(s.end.line); diff --git a/extensions/git/src/test/smoke.test.ts b/extensions/git/src/test/smoke.test.ts index 2ba3da99aa6..9005bb49563 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -56,7 +56,9 @@ suite('git smoke test', function () { git = ext!.exports.getAPI(1); if (git.repositories.length === 0) { - await eventToPromise(git.onDidOpenRepository); + const onDidOpenRepository = eventToPromise(git.onDidOpenRepository); + await commands.executeCommand('git.openRepository', cwd); + await onDidOpenRepository; } assert.strictEqual(git.repositories.length, 1); diff --git a/extensions/git/src/typings/refs.d.ts b/extensions/git/src/typings/refs.d.ts deleted file mode 100644 index 312efe5a30f..00000000000 --- a/extensions/git/src/typings/refs.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// -/// diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 4e4f1252ee0..99129079e48 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -8,6 +8,13 @@ ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", + "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", + "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", + "../../src/vscode-dts/vscode.proposed.timeline.d.ts", + "../types/lib.textEncoder.d.ts" ] } diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 45c6a4b5175..2dbf469fd26 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -9,7 +9,6 @@ "vscode": "^1.41.0" }, "icon": "images/icon.png", - "enableProposedApi": true, "categories": [ "Other" ], diff --git a/extensions/github-authentication/tsconfig.json b/extensions/github-authentication/tsconfig.json index 4e4f1252ee0..d7aed1836ee 100644 --- a/extensions/github-authentication/tsconfig.json +++ b/extensions/github-authentication/tsconfig.json @@ -8,6 +8,7 @@ ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index a272bf6ec8e..3628d956fc8 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -31,11 +31,11 @@ asynckit@^0.4.0: integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= axios@^0.21.1: - version "0.21.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: - follow-redirects "^1.10.0" + follow-redirects "^1.14.0" combined-stream@^1.0.8: version "1.0.8" @@ -49,10 +49,10 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -follow-redirects@^1.10.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== +follow-redirects@^1.14.0: + version "1.14.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381" + integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== form-data@^3.0.0: version "3.0.0" diff --git a/extensions/github/package.json b/extensions/github/package.json index f21080ab6f1..5e3e72a7b1c 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -9,7 +9,6 @@ "vscode": "^1.41.0" }, "icon": "images/icon.png", - "enableProposedApi": true, "categories": [ "Other" ], diff --git a/extensions/github/src/typings/ref.d.ts b/extensions/github/src/typings/ref.d.ts index 04ba79d86bf..578bc1d0577 100644 --- a/extensions/github/src/typings/ref.d.ts +++ b/extensions/github/src/typings/ref.d.ts @@ -3,7 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// - declare module 'tunnel'; diff --git a/extensions/github/tsconfig.json b/extensions/github/tsconfig.json index 4e4f1252ee0..d7aed1836ee 100644 --- a/extensions/github/tsconfig.json +++ b/extensions/github/tsconfig.json @@ -8,6 +8,7 @@ ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/grunt/src/typings/refs.d.ts b/extensions/grunt/src/typings/refs.d.ts deleted file mode 100644 index bc057c55878..00000000000 --- a/extensions/grunt/src/typings/refs.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/grunt/tsconfig.json b/extensions/grunt/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/grunt/tsconfig.json +++ b/extensions/grunt/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/gulp/src/typings/refs.d.ts b/extensions/gulp/src/typings/refs.d.ts deleted file mode 100644 index bc057c55878..00000000000 --- a/extensions/gulp/src/typings/refs.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/gulp/tsconfig.json b/extensions/gulp/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/gulp/tsconfig.json +++ b/extensions/gulp/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/html-language-features/client/src/typings/ref.d.ts b/extensions/html-language-features/client/src/typings/ref.d.ts deleted file mode 100644 index be1d1b0b776..00000000000 --- a/extensions/html-language-features/client/src/typings/ref.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/html-language-features/client/tsconfig.json b/extensions/html-language-features/client/tsconfig.json index 8b4aedde27d..573b24b4aa6 100644 --- a/extensions/html-language-features/client/tsconfig.json +++ b/extensions/html-language-features/client/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./out" }, "include": [ - "src/**/*" + "src/**/*", + "../../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index ddaf33bcb65..07bbfe29e9a 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -1,5 +1,4 @@ { - "enableProposedApi": true, "name": "html-language-features", "displayName": "%displayName%", "description": "%description%", diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index cf26d7405ee..00a218de43e 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -22,7 +22,7 @@ "html.format.wrapAttributes.preservealigned": "Preserve wrapping of attributes but align.", "html.format.templating.desc": "Honor django, erb, handlebars and php templating language tags.", "html.format.unformattedContentDelimiter.desc": "Keep text content together between this string.", - "html.format.wrapAttributesIndentSize.desc": "Alignment size when using 'force aligned' and 'aligned multiple' in `#html.format.wrapAttributes#` or `null` to use the default indent size.", + "html.format.wrapAttributesIndentSize.desc": "Indent wrapped attributes to after N characters. Use `null` to use the default indent size. Ignored if `#html.format.wrapAttributes#` is set to 'aligned'.", "html.suggest.html5.desc": "Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.", "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index c9aa3d23a85..8b730de9790 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -9,7 +9,6 @@ "version": "1.0.0", "publisher": "vscode", "icon": "icon.png", - "enableProposedApi": true, "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { diff --git a/extensions/image-preview/src/typings/ref.d.ts b/extensions/image-preview/src/typings/ref.d.ts deleted file mode 100644 index c9849d48e08..00000000000 --- a/extensions/image-preview/src/typings/ref.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/image-preview/tsconfig.json b/extensions/image-preview/tsconfig.json index 67181035234..c5194e2e33c 100644 --- a/extensions/image-preview/tsconfig.json +++ b/extensions/image-preview/tsconfig.json @@ -5,6 +5,7 @@ "types": [] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index cffba92357d..b31a322dff3 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -8,7 +8,10 @@ "engines": { "vscode": "^1.57.0" }, - "enableProposedApi": true, + "enabledApiProposals": [ + "notebookEditor", + "notebookEditorEdit" + ], "activationEvents": [ "onNotebook:jupyter-notebook" ], @@ -36,16 +39,16 @@ ] } ], - "grammars": [ - { - "language": "jupyter", - "scopeName": "source.jupyter", - "path": "./syntaxes/jupyter.tmLanguage.json", - "embeddedLanguages": { - "source.json": "json" - } - } - ], + "grammars": [ + { + "language": "jupyter", + "scopeName": "source.jupyter", + "path": "./syntaxes/jupyter.tmLanguage.json", + "embeddedLanguages": { + "source.json": "json" + } + } + ], "notebooks": [ { "type": "jupyter-notebook", @@ -59,10 +62,10 @@ } ] }, - "scripts": { - "compile": "npx gulp compile-extension:ipynb", - "watch": "npx gulp watch-extension:ipynb" - }, + "scripts": { + "compile": "npx gulp compile-extension:ipynb", + "watch": "npx gulp watch-extension:ipynb" + }, "dependencies": { "@enonic/fnv-plus": "^1.3.0", "detect-indent": "^6.0.0", @@ -72,8 +75,8 @@ "@jupyterlab/coreutils": "^3.1.0", "@types/uuid": "^8.3.1" }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } + "repository": { + "type": "git", + "url": "https://github.com/microsoft/vscode.git" + } } diff --git a/extensions/ipynb/src/types.d.ts b/extensions/ipynb/src/types.d.ts index ac4e1dce678..18339391ecc 100644 --- a/extensions/ipynb/src/types.d.ts +++ b/extensions/ipynb/src/types.d.ts @@ -3,8 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -/// -/// - declare module '@enonic/fnv-plus'; diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index 357d5f065f9..2032bf87b0d 100644 --- a/extensions/ipynb/tsconfig.json +++ b/extensions/ipynb/tsconfig.json @@ -7,6 +7,9 @@ ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts", + "../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts", ] } diff --git a/extensions/jake/src/typings/refs.d.ts b/extensions/jake/src/typings/refs.d.ts deleted file mode 100644 index bc057c55878..00000000000 --- a/extensions/jake/src/typings/refs.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/jake/tsconfig.json b/extensions/jake/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/jake/tsconfig.json +++ b/extensions/jake/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/json-language-features/client/src/typings/ref.d.ts b/extensions/json-language-features/client/src/typings/ref.d.ts deleted file mode 100644 index be1d1b0b776..00000000000 --- a/extensions/json-language-features/client/src/typings/ref.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/json-language-features/client/tsconfig.json b/extensions/json-language-features/client/tsconfig.json index 8b4aedde27d..573b24b4aa6 100644 --- a/extensions/json-language-features/client/tsconfig.json +++ b/extensions/json-language-features/client/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./out" }, "include": [ - "src/**/*" + "src/**/*", + "../../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 91dcbb8d554..6a6fb61c9a6 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -16,7 +16,6 @@ ], "main": "./client/out/node/jsonClientMain", "browser": "./client/dist/browser/jsonClientMain", - "enableProposedApi": true, "capabilities": { "virtualWorkspaces": true, "untrustedWorkspaces": { diff --git a/extensions/markdown-language-features/notebook/index.ts b/extensions/markdown-language-features/notebook/index.ts index 2586457e2c2..fe25756c0ea 100644 --- a/extensions/markdown-language-features/notebook/index.ts +++ b/extensions/markdown-language-features/notebook/index.ts @@ -29,10 +29,6 @@ export const activate: ActivationFunction = (ctx) => { const style = document.createElement('style'); style.textContent = ` - #preview { - font-size: 1.1em; - } - .emptyMarkdownCell::before { content: "${document.documentElement.style.getPropertyValue('--notebook-cell-markup-empty-content')}"; font-style: italic; @@ -165,7 +161,6 @@ export const activate: ActivationFunction = (ctx) => { pre code { font-family: var(--vscode-editor-font-family); - font-size: var(--vscode-editor-font-size); line-height: 1.357em; white-space: pre-wrap; diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index ba2157fde37..3734ba11aaf 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -5,7 +5,6 @@ "version": "1.0.0", "icon": "icon.png", "publisher": "vscode", - "enableProposedApi": true, "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 8d15bfca351..faaa12cb974 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -134,6 +134,7 @@ window.addEventListener('message', async event => { root.replaceWith(newContent.querySelector('.markdown-body')!); documentResource = event.data.source; } else { + // Compare two elements but skip `data-line` const areEqual = (a: Element, b: Element): boolean => { if (a.isEqualNode(b)) { return true; @@ -143,6 +144,23 @@ window.addEventListener('message', async event => { return false; } + const aAttrs = a.attributes; + const bAttrs = b.attributes; + if (aAttrs.length !== bAttrs.length) { + return false; + } + + for (let i = 0; i < aAttrs.length; ++i) { + const aAttr = aAttrs[i]; + const bAttr = bAttrs[i]; + if (aAttr.name !== bAttr.name) { + return false; + } + if (aAttr.value !== bAttr.value && aAttr.name !== 'data-line') { + return false; + } + } + const aChildren = Array.from(a.children); const bChildren = Array.from(b.children); diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index ff5f00cb949..499f168afff 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -120,6 +120,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { private imageInfo: { readonly id: string, readonly width: number, readonly height: number; }[] = []; private readonly _fileWatchersBySrc = new Map(); + private readonly _onScrollEmitter = this._register(new vscode.EventEmitter()); public readonly onScroll = this._onScrollEmitter.event; @@ -262,13 +263,13 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { * The first call immediately refreshes the preview, * calls happening shortly thereafter are debounced. */ - public refresh() { + public refresh(forceUpdate: boolean = false) { // Schedule update if none is pending if (!this.throttleTimer) { if (this.firstUpdate) { this.updatePreview(true); } else { - this.throttleTimer = setTimeout(() => this.updatePreview(true), this.delay); + this.throttleTimer = setTimeout(() => this.updatePreview(forceUpdate), this.delay); } } @@ -333,7 +334,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { return; } - const shouldReloadPage = !this.currentVersion || this.currentVersion.resource.toString() !== pendingVersion.resource.toString(); + const shouldReloadPage = forceUpdate || !this.currentVersion || this.currentVersion.resource.toString() !== pendingVersion.resource.toString(); this.currentVersion = pendingVersion; const content = await (shouldReloadPage @@ -429,7 +430,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { if (uri && uri.scheme === 'file' && !this._fileWatchersBySrc.has(src)) { const watcher = vscode.workspace.createFileSystemWatcher(uri.fsPath); watcher.onDidChange(() => { - this.refresh(); + this.refresh(true); }); this._fileWatchersBySrc.set(src, watcher); } diff --git a/extensions/markdown-language-features/src/test/documentLink.test.ts b/extensions/markdown-language-features/src/test/documentLink.test.ts index 0914ab2d49b..8919f6a1ecf 100644 --- a/extensions/markdown-language-features/src/test/documentLink.test.ts +++ b/extensions/markdown-language-features/src/test/documentLink.test.ts @@ -15,7 +15,10 @@ function workspaceFile(...segments: string[]) { } async function getLinksForFile(file: vscode.Uri): Promise { - return (await vscode.commands.executeCommand('vscode.executeLinkProvider', file))!; + console.log('getting links', file.toString(), Date.now()); + const r = (await vscode.commands.executeCommand('vscode.executeLinkProvider', file))!; + console.log('got links', file.toString(), Date.now()); + return r; } suite('Markdown Document links', () => { @@ -94,7 +97,6 @@ suite('Markdown Document links', () => { assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1); }); - test('Should navigate to line number within non-md file', async () => { await withFileContents(testFileA, '[b](sub/foo.txt#L3)'); @@ -147,15 +149,21 @@ function assertActiveDocumentUri(expectedUri: vscode.Uri) { } async function withFileContents(file: vscode.Uri, contents: string): Promise { + console.log('openTextDocument', file.toString(), Date.now()); const document = await vscode.workspace.openTextDocument(file); + console.log('showTextDocument', file.toString(), Date.now()); const editor = await vscode.window.showTextDocument(document); + console.log('editTextDocument', file.toString(), Date.now()); await editor.edit(edit => { edit.replace(new vscode.Range(0, 0, 1000, 0), contents); }); + console.log('opened done', vscode.window.activeTextEditor?.document.toString(), Date.now()); } async function executeLink(link: vscode.DocumentLink) { + console.log('executeingLink', link.target?.toString(), Date.now()); + const args = JSON.parse(decodeURIComponent(link.target!.query)); await vscode.commands.executeCommand(link.target!.path, args); + console.log('executedLink', vscode.window.activeTextEditor?.document.toString(), Date.now()); } - diff --git a/extensions/markdown-language-features/src/typings/ref.d.ts b/extensions/markdown-language-features/src/typings/ref.d.ts index 70164f82d42..d16ef8dc397 100644 --- a/extensions/markdown-language-features/src/typings/ref.d.ts +++ b/extensions/markdown-language-features/src/typings/ref.d.ts @@ -3,7 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// - declare module 'markdown-it-front-matter'; diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 070854d6913..fcd79775de5 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./out" }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/markdown-math/src/types.d.ts b/extensions/markdown-math/src/types.d.ts deleted file mode 100644 index 7aa684dcb6e..00000000000 --- a/extensions/markdown-math/src/types.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/// diff --git a/extensions/markdown-math/tsconfig.json b/extensions/markdown-math/tsconfig.json index 67181035234..c5194e2e33c 100644 --- a/extensions/markdown-math/tsconfig.json +++ b/extensions/markdown-math/tsconfig.json @@ -5,6 +5,7 @@ "types": [] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/merge-conflict/src/typings/refs.d.ts b/extensions/merge-conflict/src/typings/refs.d.ts deleted file mode 100644 index bc057c55878..00000000000 --- a/extensions/merge-conflict/src/typings/refs.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/merge-conflict/tsconfig.json b/extensions/merge-conflict/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/merge-conflict/tsconfig.json +++ b/extensions/merge-conflict/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index ef97ae07a9b..33e5eb749fc 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -12,7 +12,6 @@ "categories": [ "Other" ], - "enableProposedApi": true, "activationEvents": [ "onAuthenticationRequest:microsoft" ], diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 4e6be890442..5558b908ccb 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -45,6 +45,7 @@ interface ITokenClaims { tid: string; email?: string; unique_name?: string; + exp?: number; preferred_username?: string; oid?: string; altsecid?: string; @@ -182,6 +183,7 @@ export class AzureActiveDirectoryService { }; }); + Logger.trace('storing data into keychain...'); await this._keychain.setToken(JSON.stringify(serializedData)); } @@ -240,6 +242,7 @@ export class AzureActiveDirectoryService { } if (added.length || removed.length) { + Logger.info(`Sending change event with ${added.length} added and ${removed.length} removed`); onDidChangeSessions.fire({ added: added, removed: removed, changed: [] }); } } @@ -380,7 +383,7 @@ export class AzureActiveDirectoryService { throw codeRes.err; } token = await this.exchangeCodeForToken(codeRes.code, codeVerifier, scope); - this.setToken(token, scope); + await this.setToken(token, scope); Logger.info(`Login successful for scopes: ${scope}`); res.writeHead(302, { Location: '/' }); const session = await this.convertToSession(token); @@ -491,7 +494,7 @@ export class AzureActiveDirectoryService { } const token = await this.exchangeCodeForToken(code, verifier, scope); - this.setToken(token, scope); + await this.setToken(token, scope); const session = await this.convertToSession(token); resolve(session); @@ -509,6 +512,7 @@ export class AzureActiveDirectoryService { } private async setToken(token: IToken, scope: string): Promise { + Logger.info(`Setting token for scopes: ${scope}`); const existingTokenIndex = this._tokens.findIndex(t => t.sessionId === token.sessionId); if (existingTokenIndex > -1) { this._tokens.splice(existingTokenIndex, 1, token); @@ -522,6 +526,7 @@ export class AzureActiveDirectoryService { this._refreshTimeouts.set(token.sessionId, setTimeout(async () => { try { const refreshedToken = await this.refreshToken(token.refreshToken, scope, token.sessionId); + Logger.info('Triggering change session event...'); onDidChangeSessions.fire({ added: [], removed: [], changed: [this.convertToSessionSync(refreshedToken)] }); } catch (e) { if (e.message === REFRESH_NETWORK_FAILURE) { @@ -534,10 +539,11 @@ export class AzureActiveDirectoryService { onDidChangeSessions.fire({ added: [], removed: [this.convertToSessionSync(token)], changed: [] }); } } - }, 1000 * (token.expiresIn - 30))); + // For details on why this is set to 2/3... see https://github.com/microsoft/vscode/issues/133201#issuecomment-966668197 + }, 1000 * (token.expiresIn * 2 / 3))); } - this.storeTokenData(); + await this.storeTokenData(); } private getTokenFromResponse(json: ITokenResponse, scope: string, existingId?: string): IToken { @@ -649,7 +655,7 @@ export class AzureActiveDirectoryService { if (result.ok) { const json = await result.json(); const token = this.getTokenFromResponse(json, scope, sessionId); - this.setToken(token, scope); + await this.setToken(token, scope); Logger.info(`Token refresh success for scopes: ${token.scope}`); return token; } else { diff --git a/extensions/microsoft-authentication/src/typings/refs.d.ts b/extensions/microsoft-authentication/src/typings/refs.d.ts deleted file mode 100644 index c9849d48e08..00000000000 --- a/extensions/microsoft-authentication/src/typings/refs.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json index 6dabc0879a9..51edc522bc6 100644 --- a/extensions/microsoft-authentication/tsconfig.json +++ b/extensions/microsoft-authentication/tsconfig.json @@ -17,6 +17,7 @@ "node_modules" ], "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 3e85fc13ae1..ce506759aa4 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -8,7 +8,6 @@ "engines": { "vscode": "0.10.x" }, - "enableProposedApi": true, "icon": "images/npm_icon.png", "categories": [ "Other" @@ -73,7 +72,8 @@ "name": "%view.name%", "when": "npm:showScriptExplorer", "icon": "$(json)", - "visibility": "hidden" + "visibility": "hidden", + "contextualTitle": "%view.name%" } ] }, diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 553558de030..7f06a994f70 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -76,9 +76,14 @@ type ExplorerCommands = 'open' | 'run'; class NpmScript extends TreeItem { task: Task; package: PackageJSON; - - constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task, public taskLocation?: Location) { - super(task.name, TreeItemCollapsibleState.None); + taskLocation?: Location; + + constructor(_context: ExtensionContext, packageJson: PackageJSON, task: TaskWithLocation) { + const name = packageJson.path.length > 0 + ? task.task.name.substring(0, task.task.name.length - packageJson.path.length - 2) + : task.task.name; + super(name, TreeItemCollapsibleState.None); + this.taskLocation = task.location; const command: ExplorerCommands = workspace.getConfiguration('npm').get('scriptExplorerAction') || 'open'; const commandList = { @@ -86,9 +91,9 @@ class NpmScript extends TreeItem { title: 'Edit Script', command: 'vscode.open', arguments: [ - taskLocation?.uri, - taskLocation ? { - selection: new Range(taskLocation.range.start, taskLocation.range.start) + this.taskLocation?.uri, + this.taskLocation ? { + selection: new Range(this.taskLocation.range.start, this.taskLocation.range.start) } : undefined ] }, @@ -100,16 +105,17 @@ class NpmScript extends TreeItem { }; this.contextValue = 'script'; this.package = packageJson; - this.task = task; + this.task = task.task; this.command = commandList[command]; - if (task.group && task.group === TaskGroup.Clean) { + if (this.task.group && this.task.group === TaskGroup.Clean) { this.iconPath = new ThemeIcon('wrench-subaction'); } else { this.iconPath = new ThemeIcon('wrench'); } - if (task.detail) { - this.tooltip = task.detail; + if (this.task.detail) { + this.tooltip = this.task.detail; + this.description = this.task.detail; } } @@ -301,7 +307,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { folder.addPackage(packageJson); packages.set(fullPath, packageJson); } - let script = new NpmScript(this.extensionContext, packageJson, each.task, each.location); + let script = new NpmScript(this.extensionContext, packageJson, each); packageJson.addScript(script); } }); diff --git a/extensions/npm/src/typings/refs.d.ts b/extensions/npm/src/typings/refs.d.ts deleted file mode 100644 index 954bab971e3..00000000000 --- a/extensions/npm/src/typings/refs.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// -/// diff --git a/extensions/npm/tsconfig.json b/extensions/npm/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/npm/tsconfig.json +++ b/extensions/npm/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index 8447f019786..03176a05573 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -9,7 +9,6 @@ "engines": { "vscode": "0.10.x" }, - "enableProposedApi": true, "activationEvents": [ "onLanguage:php" ], diff --git a/extensions/php-language-features/src/typings/refs.d.ts b/extensions/php-language-features/src/typings/refs.d.ts deleted file mode 100644 index 57b50520144..00000000000 --- a/extensions/php-language-features/src/typings/refs.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// -/// diff --git a/extensions/php-language-features/tsconfig.json b/extensions/php-language-features/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/php-language-features/tsconfig.json +++ b/extensions/php-language-features/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index d7f9aa4a138..fe64cfa8fe0 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -3,7 +3,6 @@ "displayName": "%displayName%", "description": "%description%", "version": "1.0.0", - "enableProposedApi": true, "publisher": "vscode", "license": "MIT", "icon": "images/icon.png", diff --git a/extensions/search-result/src/typings/refs.d.ts b/extensions/search-result/src/typings/refs.d.ts deleted file mode 100644 index c9849d48e08..00000000000 --- a/extensions/search-result/src/typings/refs.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/search-result/tsconfig.json b/extensions/search-result/tsconfig.json index 08dcb31c2ee..f0f7c00adf5 100644 --- a/extensions/search-result/tsconfig.json +++ b/extensions/search-result/tsconfig.json @@ -4,6 +4,7 @@ "outDir": "./out", }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index a1b2560bbc9..ea0fd90265b 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -2,7 +2,9 @@ "name": "simple-browser", "displayName": "%displayName%", "description": "%description%", - "enableProposedApi": true, + "enabledApiProposals": [ + "externalUriOpener" + ], "version": "1.0.0", "icon": "media/icon.png", "publisher": "vscode", diff --git a/extensions/simple-browser/src/typings/ref.d.ts b/extensions/simple-browser/src/typings/ref.d.ts deleted file mode 100644 index c9849d48e08..00000000000 --- a/extensions/simple-browser/src/typings/ref.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// diff --git a/extensions/simple-browser/tsconfig.json b/extensions/simple-browser/tsconfig.json index 67181035234..bd370826678 100644 --- a/extensions/simple-browser/tsconfig.json +++ b/extensions/simple-browser/tsconfig.json @@ -5,6 +5,8 @@ "types": [] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.externalUriOpener.d.ts", ] } diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 934456be2f6..3eaf31c0c5d 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -7,7 +7,12 @@ "publisher": "vscode", "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "enableProposedApi": true, + "enabledApiProposals": [ + "inlayHints", + "languageStatus", + "resolvers", + "workspaceTrust" + ], "capabilities": { "virtualWorkspaces": { "supported": "limited", diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 055ffd316ff..07d4066f89b 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -3,14 +3,16 @@ "compilerOptions": { "outDir": "./out", "experimentalDecorators": true, - "types": ["node"], - "paths": { - "vscode": [ - "../../src/vs/vscode.d.ts" - ] - } + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.inlayHints.d.ts", + "../../src/vscode-dts/vscode.proposed.languageStatus.d.ts", + "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", + "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", ] } diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index dcdd8b608a9..a4b8c32909a 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -4,7 +4,54 @@ "version": "0.0.1", "publisher": "vscode", "license": "MIT", - "enableProposedApi": true, + "enabledApiProposals": [ + "authSession", + "customEditorMove", + "diffCommand", + "documentFiltersExclusive", + "editorInsets", + "extensionRuntime", + "externalUriOpener", + "fileSearchProvider", + "findTextInFiles", + "fsChunks", + "inlayHints", + "inlineCompletions", + "languageStatus", + "notebookCellExecutionState", + "notebookConcatTextDocument", + "notebookContentProvider", + "notebookControllerKind", + "notebookDebugOptions", + "notebookDeprecated", + "notebookEditor", + "notebookEditorDecorationType", + "notebookEditorEdit", + "notebookLiveShare", + "notebookMessaging", + "notebookMime", + "portsAttributes", + "quickPickSortByLabel", + "resolvers", + "scmActionButton", + "scmSelectedProvider", + "scmValidation", + "tabs", + "taskPresentationGroup", + "terminalDataWriteEvent", + "terminalDimensions", + "terminalLocation", + "terminalNameChangeEvent", + "testCoverage", + "testObserver", + "textDocumentNotebook", + "textSearchProvider", + "timeline", + "tokenInformation", + "treeViewDragAndDrop", + "treeViewReveal", + "workspaceTrust" + ], "private": true, "activationEvents": [], "main": "./out/extension", diff --git a/extensions/vscode-api-tests/tsconfig.json b/extensions/vscode-api-tests/tsconfig.json index 070854d6913..3ef85d919ec 100644 --- a/extensions/vscode-api-tests/tsconfig.json +++ b/extensions/vscode-api-tests/tsconfig.json @@ -1,9 +1,14 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index b2ce2e9d9cf..b27439bf8b6 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -9,7 +9,6 @@ "onLanguage:json" ], "main": "./out/colorizerTestMain", - "enableProposedApi": true, "engines": { "vscode": "*" }, diff --git a/extensions/git/test/colorize-fixtures/example.diff b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.diff similarity index 100% rename from extensions/git/test/colorize-fixtures/example.diff rename to extensions/vscode-colorize-tests/test/colorize-fixtures/test.diff diff --git a/extensions/git/test/colorize-results/example_diff.json b/extensions/vscode-colorize-tests/test/colorize-results/test_diff.json similarity index 100% rename from extensions/git/test/colorize-results/example_diff.json rename to extensions/vscode-colorize-tests/test/colorize-results/test_diff.json diff --git a/extensions/vscode-colorize-tests/tsconfig.json b/extensions/vscode-colorize-tests/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/vscode-colorize-tests/tsconfig.json +++ b/extensions/vscode-colorize-tests/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/vscode-custom-editor-tests/package.json b/extensions/vscode-custom-editor-tests/package.json index 997c3a94da6..778f5c56cf0 100644 --- a/extensions/vscode-custom-editor-tests/package.json +++ b/extensions/vscode-custom-editor-tests/package.json @@ -9,7 +9,6 @@ "onCustomEditor:testWebviewEditor.abc" ], "main": "./out/extension", - "enableProposedApi": true, "engines": { "vscode": "^1.48.0" }, @@ -22,6 +21,7 @@ "p-limit": "^3.0.2" }, "devDependencies": { + "@types/mocha": "^8.2.0", "@types/node": "14.x", "@types/p-limit": "^2.2.0" }, diff --git a/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts b/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts index 654afb84cd7..3474de57425 100644 --- a/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts +++ b/extensions/vscode-custom-editor-tests/src/test/customEditor.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'mocha'; import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; diff --git a/extensions/vscode-custom-editor-tests/tsconfig.json b/extensions/vscode-custom-editor-tests/tsconfig.json index 070854d6913..7234fdfeb97 100644 --- a/extensions/vscode-custom-editor-tests/tsconfig.json +++ b/extensions/vscode-custom-editor-tests/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/vscode-custom-editor-tests/yarn.lock b/extensions/vscode-custom-editor-tests/yarn.lock index 76cc46da4b7..52588b92820 100644 --- a/extensions/vscode-custom-editor-tests/yarn.lock +++ b/extensions/vscode-custom-editor-tests/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@types/mocha@^8.2.0": + version "8.2.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.3.tgz#bbeb55fbc73f28ea6de601fbfa4613f58d785323" + integrity sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw== + "@types/node@14.x": version "14.14.43" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.43.tgz#26bcbb0595b305400e8ceaf9a127a7f905ae49c8" diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index cc0264bb1a3..3cf93067634 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -9,7 +9,20 @@ "*" ], "main": "./out/extension", - "enableProposedApi": true, + "enabledApiProposals": [ + "notebookCellExecutionState", + "notebookConcatTextDocument", + "notebookContentProvider", + "notebookControllerKind", + "notebookDebugOptions", + "notebookDeprecated", + "notebookEditor", + "notebookEditorDecorationType", + "notebookEditorEdit", + "notebookLiveShare", + "notebookMessaging", + "notebookMime" + ], "engines": { "vscode": "^1.25.0" }, diff --git a/extensions/vscode-notebook-tests/tsconfig.json b/extensions/vscode-notebook-tests/tsconfig.json index 070854d6913..c9c35c63299 100644 --- a/extensions/vscode-notebook-tests/tsconfig.json +++ b/extensions/vscode-notebook-tests/tsconfig.json @@ -1,9 +1,14 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.notebook*.d.ts" ] } diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 99a836fd6c2..28cd1f1faef 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -5,6 +5,9 @@ "publisher": "vscode", "license": "MIT", "enableProposedApi": true, + "enabledApiProposals": [ + "resolvers" + ], "private": true, "engines": { "vscode": "^1.25.0" diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index 63efe6d8bf9..9e54bd40dbe 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -83,6 +83,10 @@ export function activate(context: vscode.ExtensionContext) { const commandArgs = ['--port=0', '--disable-telemetry']; const env = getNewEnv(); const remoteDataDir = process.env['TESTRESOLVER_DATA_FOLDER'] || path.join(os.homedir(), serverDataFolderName || `${dataFolderName}-testresolver`); + const logsDir = process.env['TESTRESOLVER_LOGS_FOLDER']; + if (logsDir) { + commandArgs.push('--logsPath', logsDir); + } env['VSCODE_AGENT_FOLDER'] = remoteDataDir; outputChannel.appendLine(`Using data folder at ${remoteDataDir}`); diff --git a/extensions/vscode-test-resolver/src/typings/ref.d.ts b/extensions/vscode-test-resolver/src/typings/ref.d.ts deleted file mode 100644 index e3e47385d66..00000000000 --- a/extensions/vscode-test-resolver/src/typings/ref.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// -/// -/// diff --git a/extensions/vscode-test-resolver/tsconfig.json b/extensions/vscode-test-resolver/tsconfig.json index 070854d6913..f9a183ef9e1 100644 --- a/extensions/vscode-test-resolver/tsconfig.json +++ b/extensions/vscode-test-resolver/tsconfig.json @@ -1,9 +1,14 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "types": [ + "node" + ] }, "include": [ - "src/**/*" + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.resolvers.d.ts" ] } diff --git a/package.json b/package.json index 7c6cf3da05f..368265d0742 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "58644f0352f74eff29f9d920573b8e6fffb1cfac", + "distro": "cc8976e5470edb06e4b3abd29841ffe7bf5042d2", "author": { "name": "Microsoft Corporation" }, @@ -40,7 +40,7 @@ "download-builtin-extensions-cg": "node build/lib/builtInExtensionsCG.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", "tsec-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.tsec.json", - "vscode-dts-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.vscode-dts.json && node node_modules/tsec/bin/tsec -p src/tsconfig.vscode-proposed-dts.json", + "vscode-dts-compile-check": "tsc -p src/tsconfig.vscode-dts.json && tsc -p src/tsconfig.vscode-proposed-dts.json", "valid-layers-check": "node build/lib/layersChecker.js", "update-distro": "node build/npm/update-distro.js", "web": "node resources/web/code-web.js", @@ -59,7 +59,7 @@ }, "dependencies": { "@microsoft/applicationinsights-web": "^2.6.4", - "@parcel/watcher": "2.0.0", + "@parcel/watcher": "2.0.1", "@vscode/sqlite3": "4.0.12", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", diff --git a/remote/package.json b/remote/package.json index fe96ab7237f..501827c3f79 100644 --- a/remote/package.json +++ b/remote/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@microsoft/applicationinsights-web": "^2.6.4", - "@parcel/watcher": "2.0.0", + "@parcel/watcher": "2.0.1", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", "cookie": "^0.4.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index b63e411e7ec..6eb9ea16e1b 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -83,10 +83,10 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.4.tgz#40e1c0ad20743fcee1604a7df2c57faf0aa1af87" integrity sha512-Ot53G927ykMF8cQ3/zq4foZtdk+Tt1YpX7aUTHxBU7UHNdkEiBvBfZSq+rnlUmKCJ19VatwPG4mNzvcGpBj4og== -"@parcel/watcher@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.0.tgz#ebe992a4838b35c3da9a568eb95a71cb26ddf551" - integrity sha512-ByalKmRRXNNAhwZ0X1r0XeIhh1jG8zgdlvjgHk9ZV3YxiersEGNQkwew+RfqJbIL4gOJfvC2ey6lg5kaeRainw== +"@parcel/watcher@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.1.tgz#ec4bb6c43d9588a1ffd3d2abe6df5b501463c62d" + integrity sha512-XegFF4L8sFn1RzU5KKOZxXUuzgOSwd6+X2ez3Cy6MVhYMbiLZ1moceMTqDhuT3N8DNbdumK3zP1wojsIsnX40w== dependencies: node-addon-api "^3.2.1" node-gyp-build "^4.3.0" diff --git a/resources/darwin/bin/code.sh b/resources/darwin/bin/code.sh index 6a9a9ec737c..20aee89edf9 100755 --- a/resources/darwin/bin/code.sh +++ b/resources/darwin/bin/code.sh @@ -7,5 +7,5 @@ function realpath() { python -c "import os,sys; print(os.path.realpath(sys.argv[ CONTENTS="$(dirname "$(dirname "$(dirname "$(dirname "$(realpath "$0")")")")")" ELECTRON="$CONTENTS/MacOS/Electron" CLI="$CONTENTS/Resources/app/out/cli.js" -ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --ms-enable-electron-run-as-node "$@" exit $? diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 06973937f14..73ef78f62dc 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -50,5 +50,5 @@ fi ELECTRON="$VSCODE_PATH/@@NAME@@" CLI="$VSCODE_PATH/resources/app/out/cli.js" -ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --ms-enable-electron-run-as-node "$@" exit $? diff --git a/resources/server/test/test-remote-integration.bat b/resources/server/test/test-remote-integration.bat index 47ee286f120..c31dff424d2 100644 --- a/resources/server/test/test-remote-integration.bat +++ b/resources/server/test/test-remote-integration.bat @@ -21,8 +21,9 @@ IF "%VSCODEUSERDATADIR%" == "" ( set REMOTE_VSCODE=%AUTHORITY%%EXT_PATH% set VSCODECRASHDIR=%~dp0\..\..\..\.build\crashes -set VSCODELOGSDIR=%~dp0\..\..\..\.build\logs\remote-integration-tests +set VSCODELOGSDIR=%~dp0\..\..\..\.build\logs\integration-tests-remote set TESTRESOLVER_DATA_FOLDER=%TMP%\testresolverdatafolder-%RANDOM%-%TIME:~6,5% +set TESTRESOLVER_LOGS_FOLDER=%VSCODELOGSDIR%\server if "%VSCODE_REMOTE_SERVER_PATH%"=="" ( echo "Using remote server out of sources for integration tests" diff --git a/resources/server/test/test-remote-integration.sh b/resources/server/test/test-remote-integration.sh index 09d14d50c95..71fee7327b4 100755 --- a/resources/server/test/test-remote-integration.sh +++ b/resources/server/test/test-remote-integration.sh @@ -29,7 +29,7 @@ fi export REMOTE_VSCODE=$AUTHORITY$EXT_PATH VSCODECRASHDIR=$ROOT/.build/crashes -VSCODELOGSDIR=$ROOT/.build/logs/remote-integration-tests +VSCODELOGSDIR=$ROOT/.build/logs/integration-tests-remote # Figure out which Electron to use for running tests if [ -z "$INTEGRATION_TEST_ELECTRON_PATH" ] @@ -74,6 +74,7 @@ else fi export TESTRESOLVER_DATA_FOLDER=$TESTRESOLVER_DATA_FOLDER +export TESTRESOLVER_LOGS_FOLDER=$VSCODELOGSDIR/server # Figure out which remote server to use for running tests if [ -z "$VSCODE_REMOTE_SERVER_PATH" ] diff --git a/resources/win32/bin/code.cmd b/resources/win32/bin/code.cmd index 33c640f5dd1..c72e9e28333 100644 --- a/resources/win32/bin/code.cmd +++ b/resources/win32/bin/code.cmd @@ -2,5 +2,5 @@ setlocal set VSCODE_DEV= set ELECTRON_RUN_AS_NODE=1 -"%~dp0..\@@NAME@@.exe" "%~dp0..\resources\app\out\cli.js" %* -endlocal \ No newline at end of file +"%~dp0..\@@NAME@@.exe" "%~dp0..\resources\app\out\cli.js" --ms-enable-electron-run-as-node %* +endlocal diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 23fbbc9bf20..999a5b5445c 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -43,7 +43,7 @@ if [ $IN_WSL = true ]; then # use the Remote WSL extension if installed WSL_EXT_ID="ms-vscode-remote.remote-wsl" - ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null /tmp/remote-wsl-loc.txt 2>/dev/null index.d.ts - sed "1,4d" ./src/vs/vscode.d.ts >> index.d.ts + sed "1,4d" ./src/vscode-dts/vscode.d.ts >> index.d.ts echo "Generated index.d.ts for version ${1}." else - echo "Can't find ./src/vs/vscode.d.ts. Run this script at vscode root." + echo "Can't find ./src/vscode-dts/vscode.d.ts. Run this script at vscode root." fi diff --git a/scripts/node-electron.bat b/scripts/node-electron.bat index 4ddb95b3cae..c67e2ea607d 100644 --- a/scripts/node-electron.bat +++ b/scripts/node-electron.bat @@ -10,9 +10,9 @@ set NAMESHORT=%NAMESHORT: "=% set NAMESHORT=%NAMESHORT:"=%.exe set CODE=".build\electron\%NAMESHORT%" -%CODE% %* +%CODE% %* --ms-enable-electron-run-as-node popd endlocal -exit /b %errorlevel% \ No newline at end of file +exit /b %errorlevel% diff --git a/scripts/node-electron.sh b/scripts/node-electron.sh index 0a822b6c383..2bf50817c94 100755 --- a/scripts/node-electron.sh +++ b/scripts/node-electron.sh @@ -26,9 +26,11 @@ export VSCODE_DEV=1 if [[ "$OSTYPE" == "darwin"* ]]; then ulimit -n 4096 ; ELECTRON_RUN_AS_NODE=1 \ "$CODE" \ - "$@" + "$@" \ + --ms-enable-electron-run-as-node else ELECTRON_RUN_AS_NODE=1 \ "$CODE" \ - "$@" + "$@" \ + --ms-enable-electron-run-as-node fi diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 5210ccc55ac..3da6d3c9e62 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -26,6 +26,7 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( compile-extension:markdown-language-features^ compile-extension:typescript-language-features^ compile-extension:vscode-custom-editor-tests^ + compile-extension:vscode-notebook-tests^ compile-extension:emmet^ compile-extension:css-language-features-server^ compile-extension:html-language-features-server^ diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 72e8fdf4d1f..2eb8ce50a73 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -32,6 +32,7 @@ else yarn gulp compile-extension:vscode-api-tests \ compile-extension:vscode-colorize-tests \ compile-extension:vscode-custom-editor-tests \ + compile-extension:vscode-notebook-tests \ compile-extension:markdown-language-features \ compile-extension:typescript-language-features \ compile-extension:emmet \ diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 009ea7aae67..08c487263d6 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -45,11 +45,13 @@ async function load(modulePaths, resultCallback, options) { const isDev = !!safeProcess.env['VSCODE_DEV']; - // Error handler (TODO@sandbox non-sandboxed only) + // Error handler (node.js enabled renderers only) let showDevtoolsOnError = isDev; - safeProcess.on('uncaughtException', function (/** @type {string | Error} */ error) { - onUnexpectedError(error, showDevtoolsOnError); - }); + if (!safeProcess.sandboxed) { + safeProcess.on('uncaughtException', function (/** @type {string | Error} */ error) { + onUnexpectedError(error, showDevtoolsOnError); + }); + } // Await window configuration from preload const timeout = setTimeout(() => { console.error(`[resolve window config] Could not resolve window configuration within 10 seconds, but will continue to wait...`); }, 10000); @@ -83,7 +85,7 @@ developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding); } - // Enable ASAR support (TODO@sandbox non-sandboxed only) + // Enable ASAR support (node.js enabled renderers only) if (!safeProcess.sandboxed) { globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); } @@ -100,9 +102,12 @@ window.document.documentElement.setAttribute('lang', locale); - // Replace the patched electron fs with the original node fs for all AMD code (TODO@sandbox non-sandboxed only) + // Define `fs` as `original-fs` to disable ASAR support + // in fs-operations (node.js enabled renderers only) if (!safeProcess.sandboxed) { - require.define('fs', [], function () { return require.__$__nodeRequire('original-fs'); }); + require.define('fs', [], function () { + return require.__$__nodeRequire('original-fs'); + }); } window['MonacoEnvironment'] = {}; @@ -140,8 +145,9 @@ 'tas-client-umd': `${baseNodeModulesPath}/tas-client-umd/lib/tas-client-umd.js` }; - // For priviledged renderers, allow to load built-in and other node.js - // modules via AMD which has a fallback to using node.js `require` + // Allow to load built-in and other node.js modules via AMD + // which has a fallback to using node.js `require` + // (node.js enabled renderers only) if (!safeProcess.sandboxed) { loaderConfig.amdModulesPattern = /(^vs\/)|(^vscode-textmate$)|(^vscode-oniguruma$)|(^xterm$)|(^xterm-addon-search$)|(^xterm-addon-unicode11$)|(^xterm-addon-webgl$)|(^iconv-lite-umd$)|(^jschardet$)|(^@vscode\/vscode-languagedetection$)|(^tas-client-umd$)/; } diff --git a/src/bootstrap.js b/src/bootstrap.js index f3e933de34a..85e35ab67f7 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -42,9 +42,6 @@ //#region Add support for using node_modules.asar /** - * TODO@sandbox remove the support for passing in `appRoot` once - * sandbox is fully enabled - * * @param {string=} appRoot */ function enableASARSupport(appRoot) { @@ -100,7 +97,7 @@ } if (!asarPathAdded && appRoot) { // Assuming that adding just `NODE_MODULES_ASAR_PATH` is sufficient - // because nodejs should find it even if it has a different driver letter case + // because nodejs should find it even if it has a different drive letter case paths.push(NODE_MODULES_ASAR_PATH); } } diff --git a/src/buildfile.js b/src/buildfile.js index 0dfc26c5935..5b9b061df45 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -5,13 +5,22 @@ const { createModuleDescription, createEditorWorkerModuleDescription } = require('./vs/base/buildfile'); -exports.base = [{ - name: 'vs/base/common/worker/simpleWorker', - include: ['vs/editor/common/services/editorSimpleWorker'], - prepend: ['vs/loader.js', 'vs/nls.js'], - append: ['vs/base/worker/workerMain'], - dest: 'vs/base/worker/workerMain.js' -}]; +exports.base = [ + { + name: 'vs/editor/common/services/editorSimpleWorker', + include: ['vs/base/common/worker/simpleWorker'], + prepend: ['vs/loader.js', 'vs/nls.js'], + append: ['vs/base/worker/workerMain'], + dest: 'vs/base/worker/workerMain.js' + }, + { + name: 'vs/base/common/worker/simpleWorker', + }, + { + name: 'vs/platform/extensions/node/extensionHostStarterWorker', + exclude: ['vs/base/common/worker/simpleWorker'] + } +]; exports.workerExtensionHost = [createEditorWorkerModuleDescription('vs/workbench/services/extensions/worker/extensionHostWorker')]; exports.workerNotebook = [createEditorWorkerModuleDescription('vs/workbench/contrib/notebook/common/services/notebookSimpleWorker')]; diff --git a/src/tsconfig.json b/src/tsconfig.json index 425d1e815ac..eaaa3fb52b8 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -24,6 +24,8 @@ }, "include": [ "./typings", - "./vs" + "./vs", + "vscode-dts/vscode.proposed.*.d.ts", + "vscode-dts/vscode.d.ts" ] } diff --git a/src/tsconfig.vscode-dts.json b/src/tsconfig.vscode-dts.json index 1ff4ea3efa0..4ae9bc7643a 100644 --- a/src/tsconfig.vscode-dts.json +++ b/src/tsconfig.vscode-dts.json @@ -17,6 +17,6 @@ ], }, "include": [ - "vs/vscode.d.ts" + "vscode-dts/vscode.d.ts" ] } diff --git a/src/tsconfig.vscode-proposed-dts.json b/src/tsconfig.vscode-proposed-dts.json index 4a641eabdb5..dd12f3bca28 100644 --- a/src/tsconfig.vscode-proposed-dts.json +++ b/src/tsconfig.vscode-proposed-dts.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.vscode-dts.json", "include": [ - "vs/vscode.d.ts", - "vs/vscode.proposed.d.ts", + "vscode-dts/vscode.d.ts", + "vscode-dts/vscode.proposed.*.d.ts", ] } diff --git a/src/vs/base/browser/dompurify/dompurify.js b/src/vs/base/browser/dompurify/dompurify.js index 08e22e087aa..7563b15ba44 100644 --- a/src/vs/base/browser/dompurify/dompurify.js +++ b/src/vs/base/browser/dompurify/dompurify.js @@ -1382,5 +1382,3 @@ define(function () { return purify; }); // export const removeHooks = purify.removeHooks; // export const removeAllHooks = purify.removeAllHooks; // ESM-uncomment-end - -//# sourceMappingURL=purify.es.js.map diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 1517b4a4357..fbad9465c9a 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { commonPrefixLength } from 'vs/base/common/arrays'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { CSSIcon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -35,8 +35,6 @@ export interface IBreadcrumbsItemEvent { payload: any; } -const breadcrumbSeparatorIcon = registerCodicon('breadcrumb-separator', Codicon.chevronRight); - export class BreadcrumbsWidget { private readonly _disposables = new DisposableStore(); @@ -55,6 +53,7 @@ export class BreadcrumbsWidget { private readonly _items = new Array(); private readonly _nodes = new Array(); private readonly _freeNodes = new Array(); + private readonly _separatorIcon: CSSIcon; private _enabled: boolean = true; private _focusedItemIdx: number = -1; @@ -66,6 +65,7 @@ export class BreadcrumbsWidget { constructor( container: HTMLElement, horizontalScrollbarSize: number, + separatorIcon: CSSIcon ) { this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs'; @@ -78,6 +78,7 @@ export class BreadcrumbsWidget { useShadows: false, scrollYToX: true }); + this._separatorIcon = separatorIcon; this._disposables.add(this._scrollable); this._disposables.add(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e))); container.appendChild(this._scrollable.getDomNode()); @@ -327,7 +328,7 @@ export class BreadcrumbsWidget { container.tabIndex = -1; container.setAttribute('role', 'listitem'); container.classList.add('monaco-breadcrumb-item'); - const iconContainer = dom.$(breadcrumbSeparatorIcon.cssSelector); + const iconContainer = dom.$(CSSIcon.asCSSSelector(this._separatorIcon)); container.appendChild(iconContainer); } diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index fb5ecfe279a..a3dd8237400 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -10,7 +10,7 @@ import { ButtonBar, ButtonWithDescription, IButtonStyles } from 'vs/base/browser import { ISimpleCheckboxStyles, SimpleCheckbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { Action } from 'vs/base/common/actions'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -65,11 +65,6 @@ interface ButtonMapEntry { readonly index: number; } -const dialogErrorIcon = registerCodicon('dialog-error', Codicon.error); -const dialogWarningIcon = registerCodicon('dialog-warning', Codicon.warning); -const dialogInfoIcon = registerCodicon('dialog-info', Codicon.info); -const dialogCloseIcon = registerCodicon('dialog-close', Codicon.close); - export class Dialog extends Disposable { private readonly element: HTMLElement; private readonly shadowElement: HTMLElement; @@ -350,17 +345,17 @@ export class Dialog extends Disposable { const spinModifierClassName = 'codicon-modifier-spin'; - this.iconElement.classList.remove(...dialogErrorIcon.classNamesArray, ...dialogWarningIcon.classNamesArray, ...dialogInfoIcon.classNamesArray, ...Codicon.loading.classNamesArray, spinModifierClassName); + this.iconElement.classList.remove(...Codicon.dialogError.classNamesArray, ...Codicon.dialogWarning.classNamesArray, ...Codicon.dialogInfo.classNamesArray, ...Codicon.loading.classNamesArray, spinModifierClassName); if (this.options.icon) { this.iconElement.classList.add(...this.options.icon.classNamesArray); } else { switch (this.options.type) { case 'error': - this.iconElement.classList.add(...dialogErrorIcon.classNamesArray); + this.iconElement.classList.add(...Codicon.dialogError.classNamesArray); break; case 'warning': - this.iconElement.classList.add(...dialogWarningIcon.classNamesArray); + this.iconElement.classList.add(...Codicon.dialogWarning.classNamesArray); break; case 'pending': this.iconElement.classList.add(...Codicon.loading.classNamesArray, spinModifierClassName); @@ -369,7 +364,7 @@ export class Dialog extends Disposable { case 'info': case 'question': default: - this.iconElement.classList.add(...dialogInfoIcon.classNamesArray); + this.iconElement.classList.add(...Codicon.dialogInfo.classNamesArray); break; } } @@ -378,7 +373,7 @@ export class Dialog extends Disposable { if (!this.options.disableCloseAction) { const actionBar = this._register(new ActionBar(this.toolbarContainer, {})); - const action = this._register(new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), dialogCloseIcon.classNames, true, async () => { + const action = this._register(new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), Codicon.dialogClose.classNames, true, async () => { resolve({ button: this.options.cancelId || 0, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index e1224e9b4c3..457976d1317 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -9,6 +9,9 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import 'vs/css!./gridview'; import { Box, GridView, IBoundarySashes, IGridViewOptions, IGridViewStyles, IView as IGridViewView, IViewSize, orthogonal, Sizing as GridViewSizing } from './gridview'; +import type { GridLocation } from 'vs/base/browser/ui/grid/gridview'; +///@ts-ignore +import type { SplitView } from 'vs/base/browser/ui/splitview/splitview'; export { IViewSize, LayoutPriority, Orientation, orthogonal } from './gridview'; @@ -28,9 +31,22 @@ function oppositeDirection(direction: Direction): Direction { } } +/** + * The interface to implement for views within a {@link Grid}. + */ export interface IView extends IGridViewView { - readonly preferredHeight?: number; + + /** + * The preferred width for when the user double clicks a sash + * adjacent to this view. + */ readonly preferredWidth?: number; + + /** + * The preferred height for when the user double clicks a sash + * adjacent to this view. + */ + readonly preferredHeight?: number; } export interface GridLeafNode { @@ -50,7 +66,7 @@ export function isGridBranchNode(node: GridNode): node is Gr return !!(node as any).children; } -function getGridNode(node: GridNode, location: number[]): GridNode { +function getGridNode(node: GridNode, location: GridLocation): GridNode { if (location.length === 0) { return node; } @@ -113,7 +129,7 @@ function findAdjacentBoxLeafNodes(boxNode: GridNode, directi return result; } -function getLocationOrientation(rootOrientation: Orientation, location: number[]): Orientation { +function getLocationOrientation(rootOrientation: Orientation, location: GridLocation): Orientation { return location.length % 2 === 0 ? orthogonal(rootOrientation) : rootOrientation; } @@ -121,7 +137,7 @@ function getDirectionOrientation(direction: Direction): Orientation { return direction === Direction.Up || direction === Direction.Down ? Orientation.VERTICAL : Orientation.HORIZONTAL; } -export function getRelativeLocation(rootOrientation: Orientation, location: number[], direction: Direction): number[] { +export function getRelativeLocation(rootOrientation: Orientation, location: GridLocation, direction: Direction): GridLocation { const orientation = getLocationOrientation(rootOrientation, location); const directionOrientation = getDirectionOrientation(direction); @@ -163,7 +179,7 @@ function indexInParent(element: HTMLElement): number { * * This will break as soon as DOM structures of the Splitview or Gridview change. */ -function getGridLocation(element: HTMLElement): number[] { +function getGridLocation(element: HTMLElement): GridLocation { const parentElement = element.parentElement; if (!parentElement) { @@ -191,40 +207,93 @@ export namespace Sizing { } export interface IGridStyles extends IGridViewStyles { } +export interface IGridOptions extends IGridViewOptions { } -export interface IGridOptions extends IGridViewOptions { - readonly firstViewVisibleCachedSize?: number; -} - +/** + * The {@link Grid} exposes a Grid widget in a friendlier API than the underlying + * {@link GridView} widget. Namely, all mutation operations are addressed by the + * model elements, rather than indexes. + * + * It support the same features as the {@link GridView}. + */ export class Grid extends Disposable { protected gridview: GridView; private views = new Map(); + + /** + * The orientation of the grid. Matches the orientation of the root + * {@link SplitView} in the grid's {@link GridLocation} model. + */ get orientation(): Orientation { return this.gridview.orientation; } set orientation(orientation: Orientation) { this.gridview.orientation = orientation; } + /** + * The width of the grid. + */ get width(): number { return this.gridview.width; } + + /** + * The height of the grid. + */ get height(): number { return this.gridview.height; } + /** + * The minimum width of the grid. + */ get minimumWidth(): number { return this.gridview.minimumWidth; } + + /** + * The minimum height of the grid. + */ get minimumHeight(): number { return this.gridview.minimumHeight; } + + /** + * The maximum width of the grid. + */ get maximumWidth(): number { return this.gridview.maximumWidth; } + + /** + * The maximum height of the grid. + */ get maximumHeight(): number { return this.gridview.maximumHeight; } + /** + * Fires whenever a view within the grid changes its size constraints. + */ readonly onDidChange: Event<{ width: number; height: number; } | undefined>; + + /** + * Fires whenever the user scrolls a {@link SplitView} within + * the grid. + */ readonly onDidScroll: Event; + /** + * A collection of sashes perpendicular to each edge of the grid. + * Corner sashes will be created for each intersection. + */ get boundarySashes(): IBoundarySashes { return this.gridview.boundarySashes; } set boundarySashes(boundarySashes: IBoundarySashes) { this.gridview.boundarySashes = boundarySashes; } + /** + * Enable/disable edge snapping across all grid views. + */ set edgeSnapping(edgeSnapping: boolean) { this.gridview.edgeSnapping = edgeSnapping; } + /** + * The DOM element for this view. + */ get element(): HTMLElement { return this.gridview.element; } private didLayout = false; - constructor(gridview: GridView, options?: IGridOptions); - constructor(view: T, options?: IGridOptions); + /** + * Create a new {@link Grid}. A grid must *always* have a view + * inside. + * + * @param view An initial view for this Grid. + */ constructor(view: T | GridView, options: IGridOptions = {}) { super(); @@ -238,12 +307,8 @@ export class Grid extends Disposable { this._register(this.gridview); this._register(this.gridview.onDidSashReset(this.onDidSashReset, this)); - const size: number | GridViewSizing = typeof options.firstViewVisibleCachedSize === 'number' - ? GridViewSizing.Invisible(options.firstViewVisibleCachedSize) - : 0; - if (!(view instanceof GridView)) { - this._addView(view, size, [0]); + this._addView(view, 0, [0]); } this.onDidChange = this.gridview.onDidChange; @@ -254,15 +319,67 @@ export class Grid extends Disposable { this.gridview.style(styles); } + /** + * Layout the {@link Grid}. + * + * Optionally provide a `top` and `left` positions, those will propagate + * as an origin for positions passed to {@link IView.layout}. + * + * @param width The width of the {@link Grid}. + * @param height The height of the {@link Grid}. + * @param top Optional, the top location of the {@link Grid}. + * @param left Optional, the left location of the {@link Grid}. + */ layout(width: number, height: number, top: number = 0, left: number = 0): void { this.gridview.layout(width, height, top, left); this.didLayout = true; } - hasView(view: T): boolean { - return this.views.has(view); - } - + /** + * Add a {@link IView view} to this {@link Grid}, based on another reference view. + * + * Take this grid as an example: + * + * ``` + * +-----+---------------+ + * | A | B | + * +-----+---------+-----+ + * | C | | + * +---------------+ D | + * | E | | + * +---------------+-----+ + * ``` + * + * Calling `addView(X, Sizing.Distribute, C, Direction.Right)` will make the following + * changes: + * + * ``` + * +-----+---------------+ + * | A | B | + * +-----+-+-------+-----+ + * | C | X | | + * +-------+-------+ D | + * | E | | + * +---------------+-----+ + * ``` + * + * Or `addView(X, Sizing.Distribute, D, Direction.Down)`: + * + * ``` + * +-----+---------------+ + * | A | B | + * +-----+---------+-----+ + * | C | D | + * +---------------+-----+ + * | E | X | + * +---------------+-----+ + * ``` + * + * @param newView The view to add. + * @param size Either a fixed size, or a dynamic {@link Sizing} strategy. + * @param referenceView Another view to place this new view next to. + * @param direction The direction the new view should be placed next to the reference view. + */ addView(newView: T, size: number | Sizing, referenceView: T, direction: Direction): void { if (this.views.has(newView)) { throw new Error('Can\'t add same view twice'); @@ -293,7 +410,7 @@ export class Grid extends Disposable { this._addView(newView, viewSize, location); } - addViewAt(newView: T, size: number | DistributeSizing | InvisibleSizing, location: number[]): void { + private addViewAt(newView: T, size: number | DistributeSizing | InvisibleSizing, location: GridLocation): void { if (this.views.has(newView)) { throw new Error('Can\'t add same view twice'); } @@ -311,11 +428,17 @@ export class Grid extends Disposable { this._addView(newView, viewSize, location); } - protected _addView(newView: T, size: number | GridViewSizing, location: number[]): void { + protected _addView(newView: T, size: number | GridViewSizing, location: GridLocation): void { this.views.set(newView, newView.element); this.gridview.addView(newView, size, location); } + /** + * Remove a {@link IView view} from this {@link Grid}. + * + * @param view The {@link IView view} to remove. + * @param sizing Whether to distribute other {@link IView view}'s sizes. + */ removeView(view: T, sizing?: Sizing): void { if (this.views.size === 1) { throw new Error('Can\'t remove last view'); @@ -326,6 +449,16 @@ export class Grid extends Disposable { this.views.delete(view); } + /** + * Move a {@link IView view} to another location in the grid. + * + * @remarks See {@link Grid.addView}. + * + * @param view The {@link IView view} to move. + * @param sizing Either a fixed size, or a dynamic {@link Sizing} strategy. + * @param referenceView Another view to place the view next to. + * @param direction The direction the view should be placed next to the reference view. + */ moveView(view: T, sizing: number | Sizing, referenceView: T, direction: Direction): void { const sourceLocation = this.getViewLocation(view); const [sourceParentLocation, from] = tail(sourceLocation); @@ -342,7 +475,16 @@ export class Grid extends Disposable { } } - moveViewTo(view: T, location: number[]): void { + /** + * Move a {@link IView view} to another location in the grid. + * + * @remarks Internal method, do not use without knowing what you're doing. + * @remarks See {@link GridView.moveView}. + * + * @param view The {@link IView view} to move. + * @param location The {@link GridLocation location} to insert the view on. + */ + moveViewTo(view: T, location: GridLocation): void { const sourceLocation = this.getViewLocation(view); const [sourceParentLocation, from] = tail(sourceLocation); const [targetParentLocation, to] = tail(location); @@ -362,17 +504,35 @@ export class Grid extends Disposable { } } + /** + * Swap two {@link IView views} within the {@link Grid}. + * + * @param from One {@link IView view}. + * @param to Another {@link IView view}. + */ swapViews(from: T, to: T): void { const fromLocation = this.getViewLocation(from); const toLocation = this.getViewLocation(to); return this.gridview.swapViews(fromLocation, toLocation); } + /** + * Resize a {@link IView view}. + * + * @param view The {@link IView view} to resize. + * @param size The size the view should be. + */ resizeView(view: T, size: IViewSize): void { const location = this.getViewLocation(view); return this.gridview.resizeView(location, size); } + /** + * Get the size of a {@link IView view}. + * + * @param view The {@link IView view}. Provide `undefined` to get the size + * of the grid itself. + */ getViewSize(view?: T): IViewSize { if (!view) { return this.gridview.getViewSize(); @@ -382,34 +542,71 @@ export class Grid extends Disposable { return this.gridview.getViewSize(location); } + /** + * Get the cached visible size of a {@link IView view}. This was the size + * of the view at the moment it last became hidden. + * + * @param view The {@link IView view}. + */ getViewCachedVisibleSize(view: T): number | undefined { const location = this.getViewLocation(view); return this.gridview.getViewCachedVisibleSize(location); } + /** + * Maximize the size of a {@link IView view} by collapsing all other views + * to their minimum sizes. + * + * @param view The {@link IView view}. + */ maximizeViewSize(view: T): void { const location = this.getViewLocation(view); this.gridview.maximizeViewSize(location); } + /** + * Distribute the size among all {@link IView views} within the entire + * grid or within a single {@link SplitView}. + */ distributeViewSizes(): void { this.gridview.distributeViewSizes(); } + /** + * Returns whether a {@link IView view} is visible. + * + * @param view The {@link IView view}. + */ isViewVisible(view: T): boolean { const location = this.getViewLocation(view); return this.gridview.isViewVisible(location); } + /** + * Set the visibility state of a {@link IView view}. + * + * @param view The {@link IView view}. + */ setViewVisible(view: T, visible: boolean): void { const location = this.getViewLocation(view); this.gridview.setViewVisible(location, visible); } + /** + * Returns a descriptor for the entire grid. + */ getViews(): GridBranchNode { return this.gridview.getView() as GridBranchNode; } + /** + * Utility method to return the collection all views which intersect + * a view's edge. + * + * @param view The {@link IView view}. + * @param direction Which direction edge to be considered. + * @param wrap Whether the grid wraps around (from right to left, from bottom to top). + */ getNeighborViews(view: T, direction: Direction, wrap: boolean = false): T[] { if (!this.didLayout) { throw new Error('Can\'t call getNeighborViews before first layout'); @@ -436,7 +633,7 @@ export class Grid extends Disposable { .map(node => node.view); } - getViewLocation(view: T): number[] { + private getViewLocation(view: T): GridLocation { const element = this.views.get(view); if (!element) { @@ -446,8 +643,8 @@ export class Grid extends Disposable { return getGridLocation(element); } - private onDidSashReset(location: number[]): void { - const resizeToPreferredSize = (location: number[]): boolean => { + private onDidSashReset(location: GridLocation): void { + const resizeToPreferredSize = (location: GridLocation): boolean => { const node = this.gridview.getView(location) as GridNode; if (isGridBranchNode(node)) { @@ -510,6 +707,9 @@ export interface ISerializedGrid { height: number; } +/** + * A {@link Grid} which can serialize itself. + */ export class SerializableGrid extends Grid { private static serializeNode(node: GridNode, orientation: Orientation): ISerializedNode { @@ -526,6 +726,13 @@ export class SerializableGrid extends Grid { return { type: 'branch', data: node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation))), size }; } + /** + * Construct a new {@link SerializableGrid} from a JSON object. + * + * @param json The JSON object. + * @param deserializer A deserializer which can revive each view. + * @returns A new {@link SerializableGrid} instance. + */ static deserialize(json: ISerializedGrid, deserializer: IViewDeserializer, options: IGridOptions = {}): SerializableGrid { if (typeof json.orientation !== 'number') { throw new Error('Invalid JSON: \'orientation\' property must be a number.'); @@ -547,6 +754,9 @@ export class SerializableGrid extends Grid { */ private initialLayoutContext: boolean = true; + /** + * Serialize this grid into a JSON object. + */ serialize(): ISerializedGrid { return { root: SerializableGrid.serializeNode(this.getViews(), this.orientation), @@ -629,6 +839,10 @@ function getDimensions(node: ISerializedNode, orientation: Orientation): { width } } +/** + * Creates a new JSON object from a {@link GridDescriptor}, which can + * be deserialized by {@link SerializableGrid.deserialize}. + */ export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerializedGrid { sanitizeGridNodeDescriptor(gridDescriptor, true); diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index bd1e2b4d548..b80ffaecb29 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -5,7 +5,7 @@ import { $ } from 'vs/base/browser/dom'; import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; -import { ISplitViewStyles, IView as ISplitView, LayoutPriority, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; +import { DistributeSizing, ISplitViewStyles, IView as ISplitView, LayoutPriority, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; import { equals as arrayEquals, tail2 as tail } from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; import { Emitter, Event, Relay } from 'vs/base/common/event'; @@ -17,6 +17,12 @@ import 'vs/css!./gridview'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export { LayoutPriority, Sizing } from 'vs/base/browser/ui/splitview/splitview'; +export interface IGridViewStyles extends ISplitViewStyles { } + +const defaultStyles: IGridViewStyles = { + separatorBorder: Color.transparent +}; + export interface IViewSize { readonly width: number; readonly height: number; @@ -36,17 +42,95 @@ export interface IBoundarySashes { readonly left?: Sash; } +/** + * The interface to implement for views within a {@link GridView}. + */ export interface IView { + + /** + * The DOM element for this view. + */ readonly element: HTMLElement; + + /** + * A minimum width for this view. + * + * @remarks If none, set it to `0`. + */ readonly minimumWidth: number; + + /** + * A minimum width for this view. + * + * @remarks If none, set it to `Number.POSITIVE_INFINITY`. + */ readonly maximumWidth: number; + + /** + * A minimum height for this view. + * + * @remarks If none, set it to `0`. + */ readonly minimumHeight: number; + + /** + * A minimum height for this view. + * + * @remarks If none, set it to `Number.POSITIVE_INFINITY`. + */ readonly maximumHeight: number; - readonly onDidChange: Event; + + /** + * The priority of the view when the {@link GridView} layout algorithm + * runs. Views with higher priority will be resized first. + * + * @remarks Only used when `proportionalLayout` is false. + */ readonly priority?: LayoutPriority; + + /** + * Whether the view will snap whenever the user reaches its minimum size or + * attempts to grow it beyond the minimum size. + * + * @defaultValue `false` + */ readonly snap?: boolean; + + /** + * View instances are supposed to fire this event whenever any of the constraint + * properties have changed: + * + * - {@link IView.minimumWidth} + * - {@link IView.maximumWidth} + * - {@link IView.minimumHeight} + * - {@link IView.maximumHeight} + * - {@link IView.priority} + * - {@link IView.snap} + * + * The {@link GridView} will relayout whenever that happens. The event can + * optionally emit the view's preferred size for that relayout. + */ + readonly onDidChange: Event; + + /** + * This will be called by the {@link GridView} during layout. A view meant to + * pass along the layout information down to its descendants. + */ layout(width: number, height: number, top: number, left: number): void; + + /** + * This will be called by the {@link GridView} whenever this view is made + * visible or hidden. + * + * @param visible Whether the view becomes visible. + */ setVisible?(visible: boolean): void; + + /** + * This will be called by the {@link GridView} whenever this view is on + * an edge of the grid and the grid's + * {@link GridView.boundarySashes boundary sashes} change. + */ setBoundarySashes?(sashes: IBoundarySashes): void; } @@ -108,29 +192,23 @@ export function isGridBranchNode(node: GridNode): node is GridBranchNode { return !!(node as any).children; } -export interface IGridViewStyles extends ISplitViewStyles { } - -const defaultStyles: IGridViewStyles = { - separatorBorder: Color.transparent -}; - -export interface ILayoutController { - readonly isLayoutEnabled: boolean; -} - -export class LayoutController implements ILayoutController { +class LayoutController { constructor(public isLayoutEnabled: boolean) { } } -export class MultiplexLayoutController implements ILayoutController { - get isLayoutEnabled(): boolean { return this.layoutControllers.every(l => l.isLayoutEnabled); } - constructor(private layoutControllers: ILayoutController[]) { } -} - export interface IGridViewOptions { + + /** + * Styles overriding the {@link defaultStyles default ones}. + */ readonly styles?: IGridViewStyles; + + /** + * Resize each view proportionally when resizing the {@link GridView}. + * + * @defaultValue `true` + */ readonly proportionalLayout?: boolean; // default true - readonly layoutController?: ILayoutController; } interface ILayoutContext { @@ -249,8 +327,8 @@ class BranchNode implements ISplitView, IDisposable { private childrenChangeDisposable: IDisposable = Disposable.None; - private readonly _onDidSashReset = new Emitter(); - readonly onDidSashReset: Event = this._onDidSashReset.event; + private readonly _onDidSashReset = new Emitter(); + readonly onDidSashReset: Event = this._onDidSashReset.event; private splitviewSashResetDisposable: IDisposable = Disposable.None; private childrenSashResetDisposable: IDisposable = Disposable.None; @@ -296,7 +374,7 @@ class BranchNode implements ISplitView, IDisposable { constructor( readonly orientation: Orientation, - readonly layoutController: ILayoutController, + readonly layoutController: LayoutController, styles: IGridViewStyles, readonly proportionalLayout: boolean, size: number = 0, @@ -685,7 +763,7 @@ class LeafNode implements ISplitView, IDisposable { private absoluteOrthogonalOffset: number = 0; readonly onDidScroll: Event = Event.None; - readonly onDidSashReset: Event = Event.None; + readonly onDidSashReset: Event = Event.None; private _onDidLinkedWidthNodeChange = new Relay(); private _linkedWidthNode: LeafNode | undefined = undefined; @@ -712,7 +790,7 @@ class LeafNode implements ISplitView, IDisposable { constructor( readonly view: IView, readonly orientation: Orientation, - readonly layoutController: ILayoutController, + readonly layoutController: LayoutController, orthogonalSize: number, size: number = 0 ) { @@ -871,21 +949,88 @@ function flipNode(node: T, size: number, orthogonalSize: number) } } +/** + * The location of a {@link IView view} within a {@link GridView}. + * + * A GridView is a tree composition of multiple {@link SplitView} instances, orthogonal + * between one another. Here's an example: + * + * ``` + * +-----+---------------+ + * | A | B | + * +-----+---------+-----+ + * | C | | + * +---------------+ D | + * | E | | + * +---------------+-----+ + * ``` + * + * The above grid's tree structure is: + * + * ``` + * Vertical SplitView + * +-Horizontal SplitView + * | +-A + * | +-B + * +- Horizontal SplitView + * +-Vertical SplitView + * | +-C + * | +-E + * +-D + * ``` + * + * So, {@link IView views} within a {@link GridView} can be referenced by + * a sequence of indexes, each index referencing each SplitView. Here are + * each view's locations, from the example above: + * + * - `A`: `[0,0]` + * - `B`: `[0,1]` + * - `C`: `[1,0,0]` + * - `D`: `[1,1]` + * - `E`: `[1,0,1]` + */ +export type GridLocation = number[]; + +/** + * The {@link GridView} is the UI component which implements a two dimensional + * flex-like layout algorithm for a collection of {@link IView} instances, which + * are mostly HTMLElement instances with size constraints. A {@link GridView} is a + * tree composition of multiple {@link SplitView} instances, orthogonal between + * one another. It will respect view's size contraints, just like the SplitView. + * + * It has a low-level index based API, allowing for fine grain performant operations. + * Look into the {@link Grid} widget for a higher-level API. + * + * Features: + * - flex-like layout algorithm + * - snap support + * - corner sash support + * - Alt key modifier behavior, macOS style + * - layout (de)serialization + */ export class GridView implements IDisposable { + /** + * The DOM element for this view. + */ readonly element: HTMLElement; + private styles: IGridViewStyles; private proportionalLayout: boolean; - private _root!: BranchNode; - private onDidSashResetRelay = new Relay(); - readonly onDidSashReset: Event = this.onDidSashResetRelay.event; + private onDidSashResetRelay = new Relay(); + private _onDidScroll = new Relay(); + private _onDidChange = new Relay(); + private _boundarySashes: IBoundarySashes = {}; + /** + * The layout controller makes sure layout only propagates + * to the views after the very first call to {@link GridView.layout}. + */ + private layoutController: LayoutController; private disposable2x2: IDisposable = Disposable.None; - private get root(): BranchNode { - return this._root; - } + private get root(): BranchNode { return this._root; } private set root(root: BranchNode) { const oldRoot = this._root; @@ -902,92 +1047,131 @@ export class GridView implements IDisposable { this._onDidScroll.input = root.onDidScroll; } - get orientation(): Orientation { - return this._root.orientation; - } + /** + * Fires whenever the user double clicks a {@link Sash sash}. + */ + readonly onDidSashReset = this.onDidSashResetRelay.event; - set orientation(orientation: Orientation) { - if (this._root.orientation === orientation) { - return; - } + /** + * Fires whenever the user scrolls a {@link SplitView} within + * the grid. + */ + readonly onDidScroll = this._onDidScroll.event; - const { size, orthogonalSize } = this._root; - this.root = flipNode(this._root, orthogonalSize, size); - this.root.layout(size, 0, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize }); - this.boundarySashes = this.boundarySashes; - } + /** + * Fires whenever a view within the grid changes its size constraints. + */ + readonly onDidChange = this._onDidChange.event; + /** + * The width of the grid. + */ get width(): number { return this.root.width; } + + /** + * The height of the grid. + */ get height(): number { return this.root.height; } + /** + * The minimum width of the grid. + */ get minimumWidth(): number { return this.root.minimumWidth; } + + /** + * The minimum height of the grid. + */ get minimumHeight(): number { return this.root.minimumHeight; } + + /** + * The maximum width of the grid. + */ get maximumWidth(): number { return this.root.maximumHeight; } + + /** + * The maximum height of the grid. + */ get maximumHeight(): number { return this.root.maximumHeight; } - private _onDidScroll = new Relay(); - readonly onDidScroll = this._onDidScroll.event; + get orientation(): Orientation { return this._root.orientation; } + get boundarySashes(): IBoundarySashes { return this._boundarySashes; } - private _onDidChange = new Relay(); - readonly onDidChange = this._onDidChange.event; + /** + * The orientation of the grid. Matches the orientation of the root + * {@link SplitView} in the grid's tree model. + */ + set orientation(orientation: Orientation) { + if (this._root.orientation === orientation) { + return; + } - private _boundarySashes: IBoundarySashes = {}; - get boundarySashes(): IBoundarySashes { return this._boundarySashes; } + const { size, orthogonalSize } = this._root; + this.root = flipNode(this._root, orthogonalSize, size); + this.root.layout(size, 0, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize }); + this.boundarySashes = this.boundarySashes; + } + + /** + * A collection of sashes perpendicular to each edge of the grid. + * Corner sashes will be created for each intersection. + */ set boundarySashes(boundarySashes: IBoundarySashes) { this._boundarySashes = boundarySashes; this.root.boundarySashes = fromAbsoluteBoundarySashes(boundarySashes, this.orientation); } + /** + * Enable/disable edge snapping across all grid views. + */ set edgeSnapping(edgeSnapping: boolean) { this.root.edgeSnapping = edgeSnapping; } /** - * The first layout controller makes sure layout only propagates - * to the views after the very first call to gridview.layout() + * Create a new {@link GridView} instance. + * + * @remarks It's the caller's responsibility to append the + * {@link GridView.element} to the page's DOM. */ - private firstLayoutController: LayoutController; - private layoutController: LayoutController; - constructor(options: IGridViewOptions = {}) { this.element = $('.monaco-grid-view'); this.styles = options.styles || defaultStyles; this.proportionalLayout = typeof options.proportionalLayout !== 'undefined' ? !!options.proportionalLayout : true; - - this.firstLayoutController = new LayoutController(false); - this.layoutController = new MultiplexLayoutController([ - this.firstLayoutController, - ...(options.layoutController ? [options.layoutController] : []) - ]); - + this.layoutController = new LayoutController(false); this.root = new BranchNode(Orientation.VERTICAL, this.layoutController, this.styles, this.proportionalLayout); } - getViewMap(map: Map, node?: Node): void { - if (!node) { - node = this.root; - } - - if (node instanceof BranchNode) { - node.children.forEach(child => this.getViewMap(map, child)); - } else { - map.set(node.view, node.element); - } - } - style(styles: IGridViewStyles): void { this.styles = styles; this.root.style(styles); } + /** + * Layout the {@link GridView}. + * + * Optionally provide a `top` and `left` positions, those will propagate + * as an origin for positions passed to {@link IView.layout}. + * + * @param width The width of the {@link GridView}. + * @param height The height of the {@link GridView}. + * @param top Optional, the top location of the {@link GridView}. + * @param left Optional, the left location of the {@link GridView}. + */ layout(width: number, height: number, top: number = 0, left: number = 0): void { - this.firstLayoutController.isLayoutEnabled = true; + this.layoutController.isLayoutEnabled = true; const [size, orthogonalSize, offset, orthogonalOffset] = this.root.orientation === Orientation.HORIZONTAL ? [height, width, top, left] : [width, height, left, top]; this.root.layout(size, offset, { orthogonalSize, absoluteOffset: offset, absoluteOrthogonalOffset: orthogonalOffset, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize }); } - addView(view: IView, size: number | Sizing, location: number[]): void { + /** + * Add a {@link IView view} to this {@link GridView}. + * + * @param view The view to add. + * @param size Either a fixed size, or a dynamic {@link Sizing} strategy. + * @param location The {@link GridLocation location} to insert the view on. + */ + addView(view: IView, size: number | Sizing, location: GridLocation): void { this.disposable2x2.dispose(); this.disposable2x2 = Disposable.None; @@ -1028,7 +1212,13 @@ export class GridView implements IDisposable { this.trySet2x2(); } - removeView(location: number[], sizing?: Sizing): IView { + /** + * Remove a {@link IView view} from this {@link GridView}. + * + * @param location The {@link GridLocation location} of the {@link IView view}. + * @param sizing Whether to distribute other {@link IView view}'s sizes. + */ + removeView(location: GridLocation, sizing?: DistributeSizing): IView { this.disposable2x2.dispose(); this.disposable2x2 = Disposable.None; @@ -1102,7 +1292,14 @@ export class GridView implements IDisposable { return node.view; } - moveView(parentLocation: number[], from: number, to: number): void { + /** + * Move a {@link IView view} within its parent. + * + * @param parentLocation The {@link GridLocation location} of the {@link IView view}'s parent. + * @param from The index of the {@link IView view} to move. + * @param to The index where the {@link IView view} should move to. + */ + moveView(parentLocation: GridLocation, from: number, to: number): void { const [, parent] = this.getNode(parentLocation); if (!(parent instanceof BranchNode)) { @@ -1114,7 +1311,13 @@ export class GridView implements IDisposable { this.trySet2x2(); } - swapViews(from: number[], to: number[]): void { + /** + * Swap two {@link IView views} within the {@link GridView}. + * + * @param from The {@link GridLocation location} of one view. + * @param to The {@link GridLocation location} of another view. + */ + swapViews(from: GridLocation, to: GridLocation): void { const [fromRest, fromIndex] = tail(from); const [, fromParent] = this.getNode(fromRest); @@ -1156,7 +1359,13 @@ export class GridView implements IDisposable { this.trySet2x2(); } - resizeView(location: number[], { width, height }: Partial): void { + /** + * Resize a {@link IView view}. + * + * @param location The {@link GridLocation location} of the view. + * @param size The size the view should be. Optionally provide a single dimension. + */ + resizeView(location: GridLocation, size: Partial): void { const [rest, index] = tail(location); const [pathToParent, parent] = this.getNode(rest); @@ -1164,11 +1373,11 @@ export class GridView implements IDisposable { throw new Error('Invalid location'); } - if (!width && !height) { + if (!size.width && !size.height) { return; } - const [parentSize, grandParentSize] = parent.orientation === Orientation.HORIZONTAL ? [width, height] : [height, width]; + const [parentSize, grandParentSize] = parent.orientation === Orientation.HORIZONTAL ? [size.width, size.height] : [size.height, size.width]; if (typeof grandParentSize === 'number' && pathToParent.length > 0) { const [, grandParent] = tail(pathToParent); @@ -1184,7 +1393,13 @@ export class GridView implements IDisposable { this.trySet2x2(); } - getViewSize(location?: number[]): IViewSize { + /** + * Get the size of a {@link IView view}. + * + * @param location The {@link GridLocation location} of the view. Provide `undefined` to get + * the size of the grid itself. + */ + getViewSize(location?: GridLocation): IViewSize { if (!location) { return { width: this.root.width, height: this.root.height }; } @@ -1193,7 +1408,13 @@ export class GridView implements IDisposable { return { width: node.width, height: node.height }; } - getViewCachedVisibleSize(location: number[]): number | undefined { + /** + * Get the cached visible size of a {@link IView view}. This was the size + * of the view at the moment it last became hidden. + * + * @param location The {@link GridLocation location} of the view. + */ + getViewCachedVisibleSize(location: GridLocation): number | undefined { const [rest, index] = tail(location); const [, parent] = this.getNode(rest); @@ -1204,7 +1425,13 @@ export class GridView implements IDisposable { return parent.getChildCachedVisibleSize(index); } - maximizeViewSize(location: number[]): void { + /** + * Maximize the size of a {@link IView view} by collapsing all other views + * to their minimum sizes. + * + * @param location The {@link GridLocation location} of the view. + */ + maximizeViewSize(location: GridLocation): void { const [ancestors, node] = this.getNode(location); if (!(node instanceof LeafNode)) { @@ -1216,7 +1443,16 @@ export class GridView implements IDisposable { } } - distributeViewSizes(location?: number[]): void { + /** + * Distribute the size among all {@link IView views} within the entire + * grid or within a single {@link SplitView}. + * + * @param location The {@link GridLocation location} of a view containing + * children views, which will have their sizes distributed within the parent + * view's size. Provide `undefined` to recursively distribute all views' sizes + * in the entire grid. + */ + distributeViewSizes(location?: GridLocation): void { if (!location) { this.root.distributeViewSizes(true); return; @@ -1232,7 +1468,12 @@ export class GridView implements IDisposable { this.trySet2x2(); } - isViewVisible(location: number[]): boolean { + /** + * Returns whether a {@link IView view} is visible. + * + * @param location The {@link GridLocation location} of the view. + */ + isViewVisible(location: GridLocation): boolean { const [rest, index] = tail(location); const [, parent] = this.getNode(rest); @@ -1243,7 +1484,12 @@ export class GridView implements IDisposable { return parent.isChildVisible(index); } - setViewVisible(location: number[], visible: boolean): void { + /** + * Set the visibility state of a {@link IView view}. + * + * @param location The {@link GridLocation location} of the view. + */ + setViewVisible(location: GridLocation, visible: boolean): void { const [rest, index] = tail(location); const [, parent] = this.getNode(rest); @@ -1254,13 +1500,31 @@ export class GridView implements IDisposable { parent.setChildVisible(index, visible); } + /** + * Returns a descriptor for the entire grid. + */ getView(): GridBranchNode; - getView(location?: number[]): GridNode; - getView(location?: number[]): GridNode { + + /** + * Returns a descriptor for a {@link GridLocation subtree} within the + * {@link GridView}. + * + * @param location The {@link GridLocation location} of the root of + * the {@link GridLocation subtree}. + */ + getView(location: GridLocation): GridNode; + getView(location?: GridLocation): GridNode { const node = location ? this.getNode(location)[1] : this._root; return this._getViews(node, this.orientation); } + /** + * Construct a new {@link GridView} from a JSON object. + * + * @param json The JSON object. + * @param deserializer A deserializer which can revive each view. + * @returns A new {@link GridView} instance. + */ static deserialize(json: ISerializedGridView, deserializer: IViewDeserializer, options: IGridViewOptions = {}): GridView { if (typeof json.orientation !== 'number') { throw new Error('Invalid JSON: \'orientation\' property must be a number.'); @@ -1323,7 +1587,7 @@ export class GridView implements IDisposable { return { children, box }; } - private getNode(location: number[], node: Node = this.root, path: BranchNode[] = []): [BranchNode[], Node] { + private getNode(location: GridLocation, node: Node = this.root, path: BranchNode[] = []): [BranchNode[], Node] { if (location.length === 0) { return [path, node]; } @@ -1344,6 +1608,13 @@ export class GridView implements IDisposable { return this.getNode(rest, child, path); } + /** + * Attempt to lock the {@link Sash sashes} in this {@link GridView} so + * the grid behaves as a 2x2 matrix, with a corner sash in the middle. + * + * In case the grid isn't a 2x2 grid _and_ all sashes are not aligned, + * this method is a no-op. + */ trySet2x2(): void { this.disposable2x2.dispose(); this.disposable2x2 = Disposable.None; @@ -1361,6 +1632,22 @@ export class GridView implements IDisposable { this.disposable2x2 = first.trySet2x2(second); } + /** + * Populate a map with views to DOM nodes. + * @remarks To be used internally only. + */ + getViewMap(map: Map, node?: Node): void { + if (!node) { + node = this.root; + } + + if (node instanceof BranchNode) { + node.children.forEach(child => this.getViewMap(map, child)); + } else { + map.set(node.view, node.element); + } + } + dispose(): void { this.onDidSashResetRelay.dispose(); this.root.dispose(); diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 80f22f3e590..3b69763252e 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -7,39 +7,72 @@ import * as dom from 'vs/base/browser/dom'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import * as objects from 'vs/base/common/objects'; +/** + * A range to be highlighted. + */ export interface IHighlight { start: number; end: number; - extraClasses?: string; + extraClasses?: string[]; } +export interface IOptions { + + /** + * Whether + */ + readonly supportIcons?: boolean; +} + +/** + * A widget which can render a label with substring highlights, often + * originating from a filter function like the fuzzy matcher. + */ export class HighlightedLabel { private readonly domNode: HTMLElement; private text: string = ''; private title: string = ''; private highlights: IHighlight[] = []; + private supportIcons: boolean; private didEverRender: boolean = false; - constructor(container: HTMLElement, private supportIcons: boolean) { - this.domNode = document.createElement('span'); - this.domNode.className = 'monaco-highlighted-label'; - - container.appendChild(this.domNode); + /** + * Create a new {@link HighlightedLabel}. + * + * @param container The parent container to append to. + */ + constructor(container: HTMLElement, options?: IOptions) { + this.supportIcons = options?.supportIcons ?? false; + this.domNode = dom.append(container, dom.$('span.monaco-highlighted-label')); } + /** + * The label's DOM node. + */ get element(): HTMLElement { return this.domNode; } + /** + * Set the label and highlights. + * + * @param text The label to display. + * @param highlights The ranges to highlight. + * @param title An optional title for the hover tooltip. + * @param escapeNewLines Whether to escape new lines. + * @returns + */ set(text: string | undefined, highlights: IHighlight[] = [], title: string = '', escapeNewLines?: boolean) { if (!text) { text = ''; } + if (escapeNewLines) { // adjusts highlights inplace text = HighlightedLabel.escapeNewLines(text, highlights); } + if (this.didEverRender && this.text === text && this.title === title && objects.equals(this.highlights, highlights)) { return; } @@ -59,6 +92,7 @@ export class HighlightedLabel { if (highlight.end === highlight.start) { continue; } + if (pos < highlight.start) { const substring = this.text.substring(pos, highlight.start); children.push(dom.$('span', undefined, ...this.supportIcons ? renderLabelWithIcons(substring) : [substring])); @@ -67,9 +101,11 @@ export class HighlightedLabel { const substring = this.text.substring(highlight.start, highlight.end); const element = dom.$('span.highlight', undefined, ...this.supportIcons ? renderLabelWithIcons(substring) : [substring]); + if (highlight.extraClasses) { - element.classList.add(highlight.extraClasses); + element.classList.add(...highlight.extraClasses); } + children.push(element); pos = highlight.end; } @@ -80,16 +116,17 @@ export class HighlightedLabel { } dom.reset(this.domNode, ...children); + if (this.title) { this.domNode.title = this.title; } else { this.domNode.removeAttribute('title'); } + this.didEverRender = true; } static escapeNewLines(text: string, highlights: IHighlight[]): string { - let total = 0; let extra = 0; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index c88dabd3892..32f1d433d48 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -118,7 +118,7 @@ export class IconLabel extends Disposable { } if (options?.supportDescriptionHighlights) { - this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.descriptionContainer.element, dom.$('span.label-description')), !!options.supportIcons); + this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.descriptionContainer.element, dom.$('span.label-description')), { supportIcons: !!options.supportIcons }); } else { this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.descriptionContainer.element, dom.$('span.label-description')))); } @@ -281,7 +281,7 @@ class LabelWithHighlights { if (!this.singleLabel) { this.container.innerText = ''; this.container.classList.remove('multiple'); - this.singleLabel = new HighlightedLabel(dom.append(this.container, dom.$('a.label-name', { id: options?.domId })), this.supportIcons); + this.singleLabel = new HighlightedLabel(dom.append(this.container, dom.$('a.label-name', { id: options?.domId })), { supportIcons: this.supportIcons }); } this.singleLabel.set(label, options?.matches, undefined, options?.labelEscapeNewLines); @@ -299,7 +299,7 @@ class LabelWithHighlights { const id = options?.domId && `${options?.domId}_${i}`; const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' }); - const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), this.supportIcons); + const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), { supportIcons: this.supportIcons }); highlightedLabel.set(l, m, undefined, options?.labelEscapeNewLines); if (i < label.length - 1) { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 1f8e1b2f6f6..5f767fad029 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -203,6 +203,16 @@ class ListViewAccessibilityProvider implements Required implements ISpliceable, IDisposable { private static InstanceCount = 0; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 6579ff8fbeb..4c7ad3b6272 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1206,6 +1206,21 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } } +/** + * The {@link List} is a virtual scrolling widget, built on top of the {@link ListView} + * widget. + * + * Features: + * - Customizable keyboard and mouse support + * - Element traits: focus, selection, achor + * - Accessibility support + * - Touch support + * - Performant template-based rendering + * - Horizontal scrolling + * - Variable element height support + * - Dynamic element height support + * - Drag-and-drop support + */ export class List implements ISpliceable, IThemable, IDisposable { private focus = new Trait('focused'); diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index b17805f6081..dd3ec826f13 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -15,7 +15,7 @@ import { AnchorAlignment, layout, LayoutAnchorPosition } from 'vs/base/browser/u import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { EmptySubmenuAction, IAction, IActionRunner, Separator, SubmenuAction } from 'vs/base/common/actions'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Event } from 'vs/base/common/event'; import { stripIcons } from 'vs/base/common/iconLabels'; @@ -30,8 +30,7 @@ import * as nls from 'vs/nls'; export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/; export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\s&])/g; -const menuSelectionIcon = registerCodicon('menu-selection', Codicon.check); -const menuSubmenuIcon = registerCodicon('menu-submenu', Codicon.chevronRight); + export enum Direction { Right, @@ -520,7 +519,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } } - this.check = append(this.item, $('span.menu-item-check' + menuSelectionIcon.cssSelector)); + this.check = append(this.item, $('span.menu-item-check' + Codicon.menuSelection.cssSelector)); this.check.setAttribute('role', 'none'); this.label = append(this.item, $('span.action-label')); @@ -771,7 +770,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.item.tabIndex = 0; this.item.setAttribute('aria-haspopup', 'true'); this.updateAriaExpanded('false'); - this.submenuIndicator = append(this.item, $('span.submenu-indicator' + menuSubmenuIcon.cssSelector)); + this.submenuIndicator = append(this.item, $('span.submenu-indicator' + Codicon.menuSubmenu.cssSelector)); this.submenuIndicator.setAttribute('aria-hidden', 'true'); } @@ -1023,8 +1022,8 @@ let MENU_WIDGET_CSS: string = /* css */` } -${formatRule(menuSelectionIcon)} -${formatRule(menuSubmenuIcon)} +${formatRule(Codicon.menuSelection)} +${formatRule(Codicon.menuSubmenu)} .monaco-menu .monaco-action-bar { text-align: right; diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 3d187ded8cc..1e64ff88c77 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -12,7 +12,7 @@ import { cleanMnemonic, Direction, IMenuOptions, IMenuStyles, Menu, MENU_ESCAPED import { ActionRunner, IAction, IActionRunner, Separator, SubmenuAction } from 'vs/base/common/actions'; import { asArray } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod, ScanCode, ScanCodeUtils } from 'vs/base/common/keyCodes'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; @@ -25,8 +25,6 @@ import * as nls from 'vs/nls'; const $ = DOM.$; -const menuBarMoreIcon = registerCodicon('menubar-more', Codicon.more); - export interface IMenuBarOptions { enableMnemonics?: boolean; disableAltFocus?: boolean; @@ -34,6 +32,7 @@ export interface IMenuBarOptions { getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined; alwaysOnMnemonics?: boolean; compactMode?: Direction; + actionRunner?: IActionRunner; getCompactMenuActions?: () => IAction[] } @@ -109,7 +108,7 @@ export class MenuBar extends Disposable { this.menuUpdater = this._register(new RunOnceScheduler(() => this.update(), 200)); - this.actionRunner = this._register(new ActionRunner()); + this.actionRunner = this.options.actionRunner ?? this._register(new ActionRunner()); this._register(this.actionRunner.onBeforeRun(() => { this.setUnfocusedState(); })); @@ -316,7 +315,7 @@ export class MenuBar extends Disposable { const label = this.isCompact ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More'); const title = this.isCompact ? label : undefined; const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': this.isCompact ? 0 : -1, 'aria-label': label, 'title': title, 'aria-haspopup': true }); - const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + menuBarMoreIcon.cssSelector, { 'role': 'none', 'aria-hidden': true }); + const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + Codicon.menuBarMore.cssSelector, { 'role': 'none', 'aria-hidden': true }); buttonElement.appendChild(titleElement); this.container.appendChild(buttonElement); diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index aa9bcfffc0a..d0e68ce6bd9 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -13,29 +13,39 @@ import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecy import { isMacintosh } from 'vs/base/common/platform'; import 'vs/css!./sash'; +/** + * Allow the sashes to be visible at runtime. + * @remark Use for development purposes only. + */ let DEBUG = false; // DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this -export interface ISashLayoutProvider { } - -export interface IVerticalSashLayoutProvider extends ISashLayoutProvider { +/** + * A vertical sash layout provider provides position and height for a sash. + */ +export interface IVerticalSashLayoutProvider { getVerticalSashLeft(sash: Sash): number; getVerticalSashTop?(sash: Sash): number; getVerticalSashHeight?(sash: Sash): number; } -export interface IHorizontalSashLayoutProvider extends ISashLayoutProvider { +/** + * A vertical sash layout provider provides position and width for a sash. + */ +export interface IHorizontalSashLayoutProvider { getHorizontalSashTop(sash: Sash): number; getHorizontalSashLeft?(sash: Sash): number; getHorizontalSashWidth?(sash: Sash): number; } +type ISashLayoutProvider = IVerticalSashLayoutProvider | IHorizontalSashLayoutProvider; + export interface ISashEvent { - startX: number; - currentX: number; - startY: number; - currentY: number; - altKey: boolean; + readonly startX: number; + readonly currentX: number; + readonly startY: number; + readonly currentY: number; + readonly altKey: boolean; } export enum OrthogonalEdge { @@ -46,10 +56,41 @@ export enum OrthogonalEdge { } export interface ISashOptions { + + /** + * Whether a sash is horizontal or vertical. + */ readonly orientation: Orientation; + + /** + * The width or height of a vertical or horizontal sash, respectively. + */ + readonly size?: number; + + /** + * A reference to another sash, perpendicular to this one, which + * aligns at the start of this one. A corner sash will be created + * automatically at that location. + * + * The start of a horizontal sash is its left-most position. + * The start of a vertical sash is its top-most position. + */ readonly orthogonalStartSash?: Sash; + + /** + * A reference to another sash, perpendicular to this one, which + * aligns at the end of this one. A corner sash will be created + * automatically at that location. + * + * The end of a horizontal sash is its right-most position. + * The end of a vertical sash is its bottom-most position. + */ readonly orthogonalEndSash?: Sash; - readonly size?: number; + + /** + * Provides a hint as to what mouse cursor to use whenever the user + * hovers over a corner sash provided by this and an orthogonal sash. + */ readonly orthogonalEdge?: OrthogonalEdge; } @@ -67,9 +108,31 @@ export const enum Orientation { } export const enum SashState { + + /** + * Disable any UI interaction. + */ Disabled, - Minimum, - Maximum, + + /** + * Allow dragging down or to the right, depending on the sash orientation. + * + * Some OSs allow customizing the mouse cursor differently whenever + * some resizable component can't be any smaller, but can be larger. + */ + AtMinimum, + + /** + * Allow dragging up or to the left, depending on the sash orientation. + * + * Some OSs allow customizing the mouse cursor differently whenever + * some resizable component can't be any larger, but can be smaller. + */ + AtMaximum, + + /** + * Enable dragging. + */ Enabled } @@ -159,52 +222,101 @@ class OrthogonalPointerEventFactory implements IPointerEventFactory { } } +/** + * The {@link Sash} is the UI component which allows the user to resize other + * components. It's usually an invisible horizontal or vertical line which, when + * hovered, becomes highlighted and can be dragged along the perpendicular dimension + * to its direction. + * + * Features: + * - Touch event handling + * - Corner sash support + * - Hover with different mouse cursor support + * - Configurable hover size + * - Linked sash support, for 2x2 corner sashes + */ export class Sash extends Disposable { private el: HTMLElement; private layoutProvider: ISashLayoutProvider; - private orientation!: Orientation; + private orientation: Orientation; private size: number; private hoverDelay = globalHoverDelay; private hoverDelayer = this._register(new Delayer(this.hoverDelay)); private _state: SashState = SashState.Enabled; + private readonly onDidEnablementChange = this._register(new Emitter()); + private readonly _onDidStart = this._register(new Emitter()); + private readonly _onDidChange = this._register(new Emitter()); + private readonly _onDidReset = this._register(new Emitter()); + private readonly _onDidEnd = this._register(new Emitter()); + private readonly orthogonalStartSashDisposables = this._register(new DisposableStore()); + private _orthogonalStartSash: Sash | undefined; + private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore()); + private _orthogonalStartDragHandle: HTMLElement | undefined; + private readonly orthogonalEndSashDisposables = this._register(new DisposableStore()); + private _orthogonalEndSash: Sash | undefined; + private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore()); + private _orthogonalEndDragHandle: HTMLElement | undefined; + get state(): SashState { return this._state; } + get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; } + get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; } + + /** + * The state of a sash defines whether it can be interacted with by the user + * as well as what mouse cursor to use, when hovered. + */ set state(state: SashState) { if (this._state === state) { return; } this.el.classList.toggle('disabled', state === SashState.Disabled); - this.el.classList.toggle('minimum', state === SashState.Minimum); - this.el.classList.toggle('maximum', state === SashState.Maximum); + this.el.classList.toggle('minimum', state === SashState.AtMinimum); + this.el.classList.toggle('maximum', state === SashState.AtMaximum); this._state = state; - this._onDidEnablementChange.fire(state); + this.onDidEnablementChange.fire(state); } - private readonly _onDidEnablementChange = this._register(new Emitter()); - readonly onDidEnablementChange: Event = this._onDidEnablementChange.event; - - private readonly _onDidStart = this._register(new Emitter()); + /** + * An event which fires whenever the user starts dragging this sash. + */ readonly onDidStart: Event = this._onDidStart.event; - private readonly _onDidChange = this._register(new Emitter()); + /** + * An event which fires whenever the user moves the mouse while + * dragging this sash. + */ readonly onDidChange: Event = this._onDidChange.event; - private readonly _onDidReset = this._register(new Emitter()); + /** + * An event which fires whenever the user double clicks this sash. + */ readonly onDidReset: Event = this._onDidReset.event; - private readonly _onDidEnd = this._register(new Emitter()); + /** + * An event which fires whenever the user stops dragging this sash. + */ readonly onDidEnd: Event = this._onDidEnd.event; + /** + * A linked sash will be forwarded the same user interactions and events + * so it moves exactly the same way as this sash. + * + * Useful in 2x2 grids. Not meant for widespread usage. + */ linkedSash: Sash | undefined = undefined; - private readonly orthogonalStartSashDisposables = this._register(new DisposableStore()); - private _orthogonalStartSash: Sash | undefined; - private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore()); - private _orthogonalStartDragHandle: HTMLElement | undefined; - get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; } + /** + * A reference to another sash, perpendicular to this one, which + * aligns at the start of this one. A corner sash will be created + * automatically at that location. + * + * The start of a horizontal sash is its left-most position. + * The start of a vertical sash is its top-most position. + */ set orthogonalStartSash(sash: Sash | undefined) { this.orthogonalStartDragHandleDisposables.clear(); this.orthogonalStartSashDisposables.clear(); @@ -223,18 +335,22 @@ export class Sash extends Disposable { } }; - this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange(onChange, this)); + this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange.event(onChange, this)); onChange(sash.state); } this._orthogonalStartSash = sash; } - private readonly orthogonalEndSashDisposables = this._register(new DisposableStore()); - private _orthogonalEndSash: Sash | undefined; - private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore()); - private _orthogonalEndDragHandle: HTMLElement | undefined; - get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; } + /** + * A reference to another sash, perpendicular to this one, which + * aligns at the end of this one. A corner sash will be created + * automatically at that location. + * + * The end of a horizontal sash is its right-most position. + * The end of a vertical sash is its bottom-most position. + */ + set orthogonalEndSash(sash: Sash | undefined) { this.orthogonalEndDragHandleDisposables.clear(); this.orthogonalEndSashDisposables.clear(); @@ -253,15 +369,30 @@ export class Sash extends Disposable { } }; - this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange(onChange, this)); + this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange.event(onChange, this)); onChange(sash.state); } this._orthogonalEndSash = sash; } - constructor(container: HTMLElement, layoutProvider: IVerticalSashLayoutProvider, options: ISashOptions); - constructor(container: HTMLElement, layoutProvider: IHorizontalSashLayoutProvider, options: ISashOptions); + /** + * Create a new vertical sash. + * + * @param container A DOM node to append the sash to. + * @param verticalLayoutProvider A vertical layout provider. + * @param options The options. + */ + constructor(container: HTMLElement, verticalLayoutProvider: IVerticalSashLayoutProvider, options: IVerticalSashOptions); + + /** + * Create a new horizontal sash. + * + * @param container A DOM node to append the sash to. + * @param horizontalLayoutProvider A horizontal layout provider. + * @param options The options. + */ + constructor(container: HTMLElement, horizontalLayoutProvider: IHorizontalSashLayoutProvider, options: IHorizontalSashOptions); constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions) { super(); @@ -381,17 +512,17 @@ export class Sash extends Disposable { if (isMultisashResize) { cursor = 'all-scroll'; } else if (this.orientation === Orientation.HORIZONTAL) { - if (this.state === SashState.Minimum) { + if (this.state === SashState.AtMinimum) { cursor = 's-resize'; - } else if (this.state === SashState.Maximum) { + } else if (this.state === SashState.AtMaximum) { cursor = 'n-resize'; } else { cursor = isMacintosh ? 'row-resize' : 'ns-resize'; } } else { - if (this.state === SashState.Minimum) { + if (this.state === SashState.AtMinimum) { cursor = 'e-resize'; - } else if (this.state === SashState.Maximum) { + } else if (this.state === SashState.AtMaximum) { cursor = 'w-resize'; } else { cursor = isMacintosh ? 'col-resize' : 'ew-resize'; @@ -406,7 +537,7 @@ export class Sash extends Disposable { updateStyle(); if (!isMultisashResize) { - this.onDidEnablementChange(updateStyle, null, disposables); + this.onDidEnablementChange.event(updateStyle, null, disposables); } const onPointerMove = (e: PointerEvent) => { @@ -472,10 +603,19 @@ export class Sash extends Disposable { } } + /** + * Forcefully stop any user interactions with this sash. + * Useful when hiding a parent component, while the user is still + * interacting with the sash. + */ clearSashHoverState(): void { Sash.onMouseLeave(this); } + /** + * Layout the sash. The sash will size and position itself + * based on its provided {@link ISashLayoutProvider layout provider}. + */ layout(): void { if (this.orientation === Orientation.VERTICAL) { const verticalProvider = (this.layoutProvider); diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 8a32aa60f10..2816a12c8e4 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -8,12 +8,11 @@ import { AbstractScrollbar, ISimplifiedMouseEvent, ScrollbarHost } from 'vs/base import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { INewScrollPosition, Scrollable, ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; -const scrollbarButtonLeftIcon = registerCodicon('scrollbar-button-left', Codicon.triangleLeft); -const scrollbarButtonRightIcon = registerCodicon('scrollbar-button-right', Codicon.triangleRight); + export class HorizontalScrollbar extends AbstractScrollbar { @@ -43,7 +42,7 @@ export class HorizontalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'scra', - icon: scrollbarButtonLeftIcon, + icon: Codicon.scrollbarButtonLeft, top: scrollbarDelta, left: arrowDelta, bottom: undefined, @@ -55,7 +54,7 @@ export class HorizontalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'scra', - icon: scrollbarButtonRightIcon, + icon: Codicon.scrollbarButtonRight, top: scrollbarDelta, left: undefined, bottom: undefined, diff --git a/src/vs/base/browser/ui/scrollbar/media/scrollbars.css b/src/vs/base/browser/ui/scrollbar/media/scrollbars.css index 5d7a2dc705a..d50aa58526c 100644 --- a/src/vs/base/browser/ui/scrollbar/media/scrollbars.css +++ b/src/vs/base/browser/ui/scrollbar/media/scrollbars.css @@ -36,7 +36,6 @@ left: 3px; height: 3px; width: 100%; - box-shadow: #DDD 0 6px 6px -6px inset; } .monaco-scrollable-element > .shadow.left { display: block; @@ -44,7 +43,6 @@ left: 0; height: 100%; width: 3px; - box-shadow: #DDD 6px 0 6px -6px inset; } .monaco-scrollable-element > .shadow.top-left-corner { display: block; @@ -53,59 +51,3 @@ height: 3px; width: 3px; } -.monaco-scrollable-element > .shadow.top.left { - box-shadow: #DDD 6px 6px 6px -6px inset; -} - -/* ---------- Default Style ---------- */ - -.vs .monaco-scrollable-element > .scrollbar > .slider { - background: rgba(100, 100, 100, .4); -} -.vs-dark .monaco-scrollable-element > .scrollbar > .slider { - background: rgba(121, 121, 121, .4); -} -.hc-black .monaco-scrollable-element > .scrollbar > .slider { - background: rgba(111, 195, 223, .6); -} - -.monaco-scrollable-element > .scrollbar > .slider:hover { - background: rgba(100, 100, 100, .7); -} -.hc-black .monaco-scrollable-element > .scrollbar > .slider:hover { - background: rgba(111, 195, 223, .8); -} - -.monaco-scrollable-element > .scrollbar > .slider.active { - background: rgba(0, 0, 0, .6); -} -.vs-dark .monaco-scrollable-element > .scrollbar > .slider.active { - background: rgba(191, 191, 191, .4); -} -.hc-black .monaco-scrollable-element > .scrollbar > .slider.active { - background: rgba(111, 195, 223, 1); -} - -.vs-dark .monaco-scrollable-element .shadow.top { - box-shadow: none; -} - -.vs-dark .monaco-scrollable-element .shadow.left { - box-shadow: #000 6px 0 6px -6px inset; -} - -.vs-dark .monaco-scrollable-element .shadow.top.left { - box-shadow: #000 6px 6px 6px -6px inset; -} - -.hc-black .monaco-scrollable-element .shadow.top { - box-shadow: none; -} - -.hc-black .monaco-scrollable-element .shadow.left { - box-shadow: none; -} - -.hc-black .monaco-scrollable-element .shadow.top.left { - box-shadow: none; -} diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index 4403bacf831..69f5984704d 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -8,11 +8,10 @@ import { AbstractScrollbar, ISimplifiedMouseEvent, ScrollbarHost } from 'vs/base import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { INewScrollPosition, Scrollable, ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; -const scrollbarButtonUpIcon = registerCodicon('scrollbar-button-up', Codicon.triangleUp); -const scrollbarButtonDownIcon = registerCodicon('scrollbar-button-down', Codicon.triangleDown); + export class VerticalScrollbar extends AbstractScrollbar { @@ -43,7 +42,7 @@ export class VerticalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'scra', - icon: scrollbarButtonUpIcon, + icon: Codicon.scrollbarButtonUp, top: arrowDelta, left: scrollbarDelta, bottom: undefined, @@ -55,7 +54,7 @@ export class VerticalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'scra', - icon: scrollbarButtonDownIcon, + icon: Codicon.scrollbarButtonDown, top: undefined, left: scrollbarDelta, bottom: arrowDelta, diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 31fb4c9da79..12d1505e17b 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -17,45 +17,178 @@ import 'vs/css!./splitview'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export interface ISplitViewStyles { - separatorBorder: Color; + readonly separatorBorder: Color; } const defaultStyles: ISplitViewStyles = { separatorBorder: Color.transparent }; -export interface ISplitViewOptions { - readonly orientation?: Orientation; // default Orientation.VERTICAL - readonly styles?: ISplitViewStyles; - readonly orthogonalStartSash?: Sash; - readonly orthogonalEndSash?: Sash; - readonly inverseAltBehavior?: boolean; - readonly proportionalLayout?: boolean; // default true, - readonly descriptor?: ISplitViewDescriptor; - readonly scrollbarVisibility?: ScrollbarVisibility; - readonly getSashOrthogonalSize?: () => number; -} - -/** - * Only used when `proportionalLayout` is false. - */ export const enum LayoutPriority { Normal, Low, High } +/** + * The interface to implement for views within a {@link SplitView}. + * + * An optional {@link TLayoutContext layout context type} may be used in order to + * pass along layout contextual data from the {@link SplitView.layout} method down + * to each view's {@link IView.layout} calls. + */ export interface IView { + + /** + * The DOM element for this view. + */ readonly element: HTMLElement; + + /** + * A minimum size for this view. + * + * @remarks If none, set it to `0`. + */ readonly minimumSize: number; + + /** + * A minimum size for this view. + * + * @remarks If none, set it to `Number.POSITIVE_INFINITY`. + */ readonly maximumSize: number; - readonly onDidChange: Event; + + /** + * The priority of the view when the {@link SplitView.resize layout} algorithm + * runs. Views with higher priority will be resized first. + * + * @remarks Only used when `proportionalLayout` is false. + */ readonly priority?: LayoutPriority; + + /** + * Whether the view will snap whenever the user reaches its minimum size or + * attempts to grow it beyond the minimum size. + * + * @defaultValue `false` + */ readonly snap?: boolean; + + /** + * View instances are supposed to fire the {@link IView.onDidChange} event whenever + * any of the constraint properties have changed: + * + * - {@link IView.minimumSize} + * - {@link IView.maximumSize} + * - {@link IView.priority} + * - {@link IView.snap} + * + * The SplitView will relayout whenever that happens. The event can optionally emit + * the view's preferred size for that relayout. + */ + readonly onDidChange: Event; + + /** + * This will be called by the {@link SplitView} during layout. A view meant to + * pass along the layout information down to its descendants. + * + * @param size The size of this view, in pixels. + * @param offset The offset of this view, relative to the start of the {@link SplitView}. + * @param context The optional {@link IView layout context} passed to {@link SplitView.layout}. + */ layout(size: number, offset: number, context: TLayoutContext | undefined): void; + + /** + * This will be called by the {@link SplitView} whenever this view is made + * visible or hidden. + * + * @param visible Whether the view becomes visible. + */ setVisible?(visible: boolean): void; } +/** + * A descriptor for a {@link SplitView} instance. + */ +export interface ISplitViewDescriptor { + + /** + * The layout size of the {@link SplitView}. + */ + readonly size: number; + + /** + * Descriptors for each {@link IView view}. + */ + readonly views: { + + /** + * Whether the {@link IView view} is visible. + * + * @defaultValue `true` + */ + readonly visible?: boolean; + + /** + * The size of the {@link IView view}. + * + * @defaultValue `true` + */ + readonly size: number; + + /** + * The size of the {@link IView view}. + * + * @defaultValue `true` + */ + readonly view: IView; + }[]; +} + +export interface ISplitViewOptions { + + /** + * Which axis the views align on. + * + * @defaultValue `Orientation.VERTICAL` + */ + readonly orientation?: Orientation; + + /** + * Styles overriding the {@link defaultStyles default ones}. + */ + readonly styles?: ISplitViewStyles; + + /** + * Make Alt-drag the default drag operation. + */ + readonly inverseAltBehavior?: boolean; + + /** + * Resize each view proportionally when resizing the SplitView. + * + * @defaultValue `true` + */ + readonly proportionalLayout?: boolean; + + /** + * An initial description of this {@link SplitView} instance, allowing + * to initialze all views within the ctor. + */ + readonly descriptor?: ISplitViewDescriptor; + + /** + * The scrollbar visibility setting for whenever the views within + * the {@link SplitView} overflow. + */ + readonly scrollbarVisibility?: ScrollbarVisibility; + + /** + * Override the orthogonal size of sashes. + */ + readonly getSashOrthogonalSize?: () => number; +} + interface ISashEvent { readonly sash: Sash; readonly start: number; @@ -190,30 +323,89 @@ enum State { Busy } +/** + * When adding or removing views, distribute the delta space among + * all other views. + */ export type DistributeSizing = { type: 'distribute' }; + +/** + * When adding or removing views, split the delta space with another + * specific view, indexed by the provided `index`. + */ export type SplitSizing = { type: 'split', index: number }; + +/** + * When adding or removing views, assume the view is invisible. + */ export type InvisibleSizing = { type: 'invisible', cachedVisibleSize: number }; + +/** + * When adding or removing views, the sizing provides fine grained + * control over how other views get resized. + */ export type Sizing = DistributeSizing | SplitSizing | InvisibleSizing; export namespace Sizing { + + /** + * When adding or removing views, distribute the delta space among + * all other views. + */ export const Distribute: DistributeSizing = { type: 'distribute' }; + + /** + * When adding or removing views, split the delta space with another + * specific view, indexed by the provided `index`. + */ export function Split(index: number): SplitSizing { return { type: 'split', index }; } - export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; } -} -export interface ISplitViewDescriptor { - size: number; - views: { - visible?: boolean; - size: number; - view: IView; - }[]; + /** + * When adding or removing views, assume the view is invisible. + */ + export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; } } +/** + * The {@link SplitView} is the UI component which implements a one dimensional + * flex-like layout algorithm for a collection of {@link IView} instances, which + * are essentially HTMLElement instances with the following size constraints: + * + * - {@link IView.minimumSize} + * - {@link IView.maximumSize} + * - {@link IView.priority} + * - {@link IView.snap} + * + * In case the SplitView doesn't have enough size to fit all views, it will overflow + * its content with a scrollbar. + * + * In between each pair of views there will be a {@link Sash} allowing the user + * to resize the views, making sure the constraints are respected. + * + * An optional {@link TLayoutContext layout context type} may be used in order to + * pass along layout contextual data from the {@link SplitView.layout} method down + * to each view's {@link IView.layout} calls. + * + * Features: + * - Flex-like layout algorithm + * - Snap support + * - Orthogonal sash support, for corner sashes + * - View hide/show support + * - View swap/move support + * - Alt key modifier behavior, macOS style + */ export class SplitView extends Disposable { + /** + * This {@link SplitView}'s orientation. + */ readonly orientation: Orientation; + + /** + * The DOM element representing this {@link SplitView}. + */ readonly el: HTMLElement; + private sashContainer: HTMLElement; private viewContainer: HTMLElement; private scrollable: Scrollable; @@ -231,27 +423,58 @@ export class SplitView extends Disposable { private readonly getSashOrthogonalSize: { (): number } | undefined; private _onDidSashChange = this._register(new Emitter()); + private _onDidSashReset = this._register(new Emitter()); + private _orthogonalStartSash: Sash | undefined; + private _orthogonalEndSash: Sash | undefined; + private _startSnappingEnabled = true; + private _endSnappingEnabled = true; + + /** + * Fires whenever the user resizes a {@link Sash sash}. + */ readonly onDidSashChange = this._onDidSashChange.event; - private _onDidSashReset = this._register(new Emitter()); + /** + * Fires whenever the user double clicks a {@link Sash sash}. + */ readonly onDidSashReset = this._onDidSashReset.event; + /** + * Fires whenever the split view is scrolled. + */ readonly onDidScroll: Event; + /** + * The amount of views in this {@link SplitView}. + */ get length(): number { return this.viewItems.length; } + /** + * The minimum size of this {@link SplitView}. + */ get minimumSize(): number { return this.viewItems.reduce((r, item) => r + item.minimumSize, 0); } + /** + * The maximum size of this {@link SplitView}. + */ get maximumSize(): number { return this.length === 0 ? Number.POSITIVE_INFINITY : this.viewItems.reduce((r, item) => r + item.maximumSize, 0); } - private _orthogonalStartSash: Sash | undefined; get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; } + get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; } + get startSnappingEnabled(): boolean { return this._startSnappingEnabled; } + get endSnappingEnabled(): boolean { return this._endSnappingEnabled; } + + /** + * A reference to a sash, perpendicular to all sashes in this {@link SplitView}, + * located at the left- or top-most side of the SplitView. + * Corner sashes will be created automatically at the intersections. + */ set orthogonalStartSash(sash: Sash | undefined) { for (const sashItem of this.sashItems) { sashItem.sash.orthogonalStartSash = sash; @@ -260,8 +483,11 @@ export class SplitView extends Disposable { this._orthogonalStartSash = sash; } - private _orthogonalEndSash: Sash | undefined; - get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; } + /** + * A reference to a sash, perpendicular to all sashes in this {@link SplitView}, + * located at the right- or bottom-most side of the SplitView. + * Corner sashes will be created automatically at the intersections. + */ set orthogonalEndSash(sash: Sash | undefined) { for (const sashItem of this.sashItems) { sashItem.sash.orthogonalEndSash = sash; @@ -270,12 +496,16 @@ export class SplitView extends Disposable { this._orthogonalEndSash = sash; } - get sashes(): Sash[] { + /** + * The internal sashes within this {@link SplitView}. + */ + get sashes(): readonly Sash[] { return this.sashItems.map(s => s.sash); } - private _startSnappingEnabled = true; - get startSnappingEnabled(): boolean { return this._startSnappingEnabled; } + /** + * Enable/disable snapping at the beginning of this {@link SplitView}. + */ set startSnappingEnabled(startSnappingEnabled: boolean) { if (this._startSnappingEnabled === startSnappingEnabled) { return; @@ -285,8 +515,9 @@ export class SplitView extends Disposable { this.updateSashEnablement(); } - private _endSnappingEnabled = true; - get endSnappingEnabled(): boolean { return this._endSnappingEnabled; } + /** + * Enable/disable snapping at the end of this {@link SplitView}. + */ set endSnappingEnabled(endSnappingEnabled: boolean) { if (this._endSnappingEnabled === endSnappingEnabled) { return; @@ -296,12 +527,15 @@ export class SplitView extends Disposable { this.updateSashEnablement(); } + /** + * Create a new {@link SplitView} instance. + */ constructor(container: HTMLElement, options: ISplitViewOptions = {}) { super(); - this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation; - this.inverseAltBehavior = !!options.inverseAltBehavior; - this.proportionalLayout = types.isUndefined(options.proportionalLayout) ? true : !!options.proportionalLayout; + this.orientation = options.orientation ?? Orientation.VERTICAL; + this.inverseAltBehavior = options.inverseAltBehavior ?? false; + this.proportionalLayout = options.proportionalLayout ?? true; this.getSashOrthogonalSize = options.getSashOrthogonalSize; this.el = document.createElement('div'); @@ -354,10 +588,24 @@ export class SplitView extends Disposable { } } + /** + * Add a {@link IView view} to this {@link SplitView}. + * + * @param view The view to add. + * @param size Either a fixed size, or a dynamic {@link Sizing} strategy. + * @param index The index to insert the view on. + * @param skipLayout Whether layout should be skipped. + */ addView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void { this.doAddView(view, size, index, skipLayout); } + /** + * Remove a {@link IView view} from this {@link SplitView}. + * + * @param index The index where the {@link IView view} is located. + * @param sizing Whether to distribute other {@link IView view}'s sizes. + */ removeView(index: number, sizing?: Sizing): IView { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); @@ -383,13 +631,19 @@ export class SplitView extends Disposable { this.relayout(); this.state = State.Idle; - if (sizing && sizing.type === 'distribute') { + if (sizing?.type === 'distribute') { this.distributeViewSizes(); } return view; } + /** + * Move a {@link IView view} to a different index. + * + * @param from The source index. + * @param to The target index. + */ moveView(from: number, to: number): void { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); @@ -401,6 +655,13 @@ export class SplitView extends Disposable { this.addView(view, sizing, to); } + + /** + * Swap two {@link IView views}. + * + * @param from The source index. + * @param to The target index. + */ swapViews(from: number, to: number): void { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); @@ -419,6 +680,11 @@ export class SplitView extends Disposable { this.addView(fromView, toSize, to); } + /** + * Returns whether the {@link IView view} is visible. + * + * @param index The {@link IView view} index. + */ isViewVisible(index: number): boolean { if (index < 0 || index >= this.viewItems.length) { throw new Error('Index out of bounds'); @@ -428,6 +694,12 @@ export class SplitView extends Disposable { return viewItem.visible; } + /** + * Set a {@link IView view}'s visibility. + * + * @param index The {@link IView view} index. + * @param visible Whether the {@link IView view} should be visible. + */ setViewVisible(index: number, visible: boolean): void { if (index < 0 || index >= this.viewItems.length) { throw new Error('Index out of bounds'); @@ -441,6 +713,11 @@ export class SplitView extends Disposable { this.saveProportions(); } + /** + * Returns the {@link IView view}'s size previously to being hidden. + * + * @param index The {@link IView view} index. + */ getViewCachedVisibleSize(index: number): number | undefined { if (index < 0 || index >= this.viewItems.length) { throw new Error('Index out of bounds'); @@ -450,6 +727,12 @@ export class SplitView extends Disposable { return viewItem.cachedVisibleSize; } + /** + * Layout the {@link SplitView}. + * + * @param size The entire size of the {@link SplitView}. + * @param layoutContext An optional layout context to pass along to {@link IView views}. + */ layout(size: number, layoutContext?: TLayoutContext): void { const previousSize = Math.max(this.size, this.contentSize); this.size = size; @@ -616,6 +899,12 @@ export class SplitView extends Disposable { } } + /** + * Resize a {@link IView view} within the {@link SplitView}. + * + * @param index The {@link IView view} index. + * @param size The {@link IView view} size. + */ resizeView(index: number, size: number): void { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); @@ -640,6 +929,9 @@ export class SplitView extends Disposable { this.state = State.Idle; } + /** + * Distribute the entire {@link SplitView} size among all {@link IView views}. + */ distributeViewSizes(): void { const flexibleViewItems: ViewItem[] = []; let flexibleSize = 0; @@ -664,6 +956,9 @@ export class SplitView extends Disposable { this.relayout(lowPriorityIndexes, highPriorityIndexes); } + /** + * Returns the size of a {@link IView view}. + */ getViewSize(index: number): number { if (index < 0 || index >= this.viewItems.length) { return -1; @@ -964,16 +1259,16 @@ export class SplitView extends Disposable { const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible; if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) { - sash.state = SashState.Minimum; + sash.state = SashState.AtMinimum; } else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) { - sash.state = SashState.Maximum; + sash.state = SashState.AtMaximum; } else { sash.state = SashState.Disabled; } } else if (min && !max) { - sash.state = SashState.Minimum; + sash.state = SashState.AtMinimum; } else if (!min && max) { - sash.state = SashState.Maximum; + sash.state = SashState.AtMaximum; } else { sash.state = SashState.Enabled; } diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 79b6aa17a1d..02c98734105 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -8,7 +8,7 @@ import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/ import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { Action, IAction, IActionRunner, SubmenuAction } from 'vs/base/common/actions'; -import { Codicon, CSSIcon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { EventMultiplexer } from 'vs/base/common/event'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -16,7 +16,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import 'vs/css!./toolbar'; import * as nls from 'vs/nls'; -const toolBarMoreIcon = registerCodicon('toolbar-more', Codicon.more); + export interface IToolBarOptions { orientation?: ActionsOrientation; @@ -73,7 +73,7 @@ export class ToolBar extends Disposable { actionViewItemProvider: this.options.actionViewItemProvider, actionRunner: this.actionRunner, keybindingProvider: this.options.getKeyBinding, - classNames: CSSIcon.asClassNameArray(options.moreIcon ?? toolBarMoreIcon), + classNames: CSSIcon.asClassNameArray(options.moreIcon ?? Codicon.toolBarMore), anchorAlignmentProvider: this.options.anchorAlignmentProvider, menuAsChild: !!this.options.renderDropdownAsChildElement } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 422647e931c..d31315514b2 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -12,9 +12,9 @@ import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { DefaultKeyboardNavigationDelegate, IListOptions, IListStyles, isInputElement, isMonacoEditor, List, MouseController } from 'vs/base/browser/ui/list/listWidget'; import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; import { ICollapseStateChangeEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeEvent, ITreeFilter, ITreeModel, ITreeModelSpliceEvent, ITreeMouseEvent, ITreeNavigator, ITreeNode, ITreeRenderer, TreeDragOverBubble, TreeFilterResult, TreeMouseEventTarget, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; -import { treeFilterClearIcon, treeFilterOnTypeOffIcon, treeFilterOnTypeOnIcon, treeItemExpandedIcon } from 'vs/base/browser/ui/tree/treeIcons'; import { distinct, equals, firstOrDefault, range } from 'vs/base/common/arrays'; import { disposableTimeout } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; import { SetMap } from 'vs/base/common/collections'; import { Emitter, Event, EventBufferer, Relay } from 'vs/base/common/event'; import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; @@ -400,7 +400,7 @@ class TreeRenderer implements IListRenderer } private renderTwistie(node: ITreeNode, templateData: ITreeListTemplateData) { - templateData.twistie.classList.remove(...treeItemExpandedIcon.classNamesArray); + templateData.twistie.classList.remove(...Codicon.treeItemExpanded.classNamesArray); let twistieRendered = false; @@ -410,7 +410,7 @@ class TreeRenderer implements IListRenderer if (node.collapsible && (!this.hideTwistiesOfChildlessElements || node.visibleChildrenCount > 0)) { if (!twistieRendered) { - templateData.twistie.classList.add(...treeItemExpandedIcon.classNamesArray); + templateData.twistie.classList.add(...Codicon.treeItemExpanded.classNamesArray); } templateData.twistie.classList.add('collapsible'); @@ -663,7 +663,7 @@ class TypeFilterController implements IDisposable { this.updateFilterOnTypeTitleAndIcon(); this.disposables.add(addDisposableListener(this.filterOnTypeDomNode, 'input', () => this.onDidChangeFilterOnType())); - this.clearDomNode = append(controls, $('button.clear' + treeFilterClearIcon.cssSelector)); + this.clearDomNode = append(controls, $('button.clear' + Codicon.treeFilterClear.cssSelector)); this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); @@ -876,12 +876,12 @@ class TypeFilterController implements IDisposable { private updateFilterOnTypeTitleAndIcon(): void { if (this.filterOnType) { - this.filterOnTypeDomNode.classList.remove(...treeFilterOnTypeOffIcon.classNamesArray); - this.filterOnTypeDomNode.classList.add(...treeFilterOnTypeOnIcon.classNamesArray); + this.filterOnTypeDomNode.classList.remove(...Codicon.treeFilterOnTypeOff.classNamesArray); + this.filterOnTypeDomNode.classList.add(...Codicon.treeFilterOnTypeOn.classNamesArray); this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); } else { - this.filterOnTypeDomNode.classList.remove(...treeFilterOnTypeOnIcon.classNamesArray); - this.filterOnTypeDomNode.classList.add(...treeFilterOnTypeOffIcon.classNamesArray); + this.filterOnTypeDomNode.classList.remove(...Codicon.treeFilterOnTypeOn.classNamesArray); + this.filterOnTypeDomNode.classList.add(...Codicon.treeFilterOnTypeOff.classNamesArray); this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); } } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index fcc3d24bc35..2650d1a2191 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -12,8 +12,8 @@ import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/ import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; import { CompressibleObjectTree, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions, ICompressibleTreeRenderer, IObjectTreeOptions, IObjectTreeSetChildrenOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { IAsyncDataSource, ICollapseStateChangeEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeElement, ITreeEvent, ITreeFilter, ITreeMouseEvent, ITreeNode, ITreeRenderer, ITreeSorter, TreeError, TreeFilterResult, TreeVisibility, WeakMapper } from 'vs/base/browser/ui/tree/tree'; -import { treeItemLoadingIcon } from 'vs/base/browser/ui/tree/treeIcons'; import { CancelablePromise, createCancelablePromise, Promises, timeout } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; @@ -109,10 +109,10 @@ class AsyncDataTreeRenderer implements IT renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { - twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + twistieElement.classList.add(...Codicon.treeItemLoading.classNamesArray); return true; } else { - twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + twistieElement.classList.remove(...Codicon.treeItemLoading.classNamesArray); return false; } } @@ -1073,10 +1073,10 @@ class CompressibleAsyncDataTreeRenderer i renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { if (element.slow) { - twistieElement.classList.add(...treeItemLoadingIcon.classNamesArray); + twistieElement.classList.add(...Codicon.treeItemLoading.classNamesArray); return true; } else { - twistieElement.classList.remove(...treeItemLoadingIcon.classNamesArray); + twistieElement.classList.remove(...Codicon.treeItemLoading.classNamesArray); return false; } } diff --git a/src/vs/base/browser/ui/tree/treeIcons.ts b/src/vs/base/browser/ui/tree/treeIcons.ts deleted file mode 100644 index 9755ae59e04..00000000000 --- a/src/vs/base/browser/ui/tree/treeIcons.ts +++ /dev/null @@ -1,14 +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 { Codicon, registerCodicon } from 'vs/base/common/codicons'; - -export const treeItemExpandedIcon = registerCodicon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation - -export const treeFilterOnTypeOnIcon = registerCodicon('tree-filter-on-type-on', Codicon.listFilter); -export const treeFilterOnTypeOffIcon = registerCodicon('tree-filter-on-type-off', Codicon.listSelection); -export const treeFilterClearIcon = registerCodicon('tree-filter-clear', Codicon.close); - -export const treeItemLoadingIcon = registerCodicon('tree-item-loading', Codicon.loading); diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 5cf51f7311d..100ec15f577 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -45,10 +45,6 @@ const _registry = new Registry(); export const iconRegistry: IIconRegistry = _registry; -export function registerCodicon(id: string, def: Codicon): Codicon { - return new Codicon(id, def); -} - // Selects all codicon names encapsulated in the `$()` syntax and wraps the // results with spaces so that screen readers can read the text better. export function getCodiconAriaLabel(text: string | undefined) { @@ -59,14 +55,532 @@ export function getCodiconAriaLabel(text: string | undefined) { return text.replace(/\$\((.*?)\)/g, (_match, codiconName) => ` ${codiconName} `).trim(); } +/** + * The Codicon library is a set of default icons that are built-in in VS Code. + * + * In the product (outside of base) Codicons should only be used as defaults. In order to have all icons in VS Code + * themeable, component should ise define new, component specific icons using `iconRegistry.registerIcon`. + * In that call a Codicon can be names as default. + */ export class Codicon implements CSSIcon { - constructor(public readonly id: string, public readonly definition: Codicon | IconDefinition, public description?: string) { + private constructor(public readonly id: string, public readonly definition: IconDefinition, public description?: string) { _registry.add(this); } public get classNames() { return 'codicon codicon-' + this.id; } // classNamesArray is useful for migrating to ES6 classlist public get classNamesArray() { return ['codicon', 'codicon-' + this.id]; } public get cssSelector() { return '.codicon.codicon-' + this.id; } + + + // built-in icons, with image name + public static readonly add = new Codicon('add', { fontCharacter: '\\ea60' }); + public static readonly plus = new Codicon('plus', Codicon.add.definition); + public static readonly gistNew = new Codicon('gist-new', Codicon.add.definition); + public static readonly repoCreate = new Codicon('repo-create', Codicon.add.definition); + public static readonly lightbulb = new Codicon('lightbulb', { fontCharacter: '\\ea61' }); + public static readonly lightBulb = new Codicon('light-bulb', { fontCharacter: '\\ea61' }); + public static readonly repo = new Codicon('repo', { fontCharacter: '\\ea62' }); + public static readonly repoDelete = new Codicon('repo-delete', { fontCharacter: '\\ea62' }); + public static readonly gistFork = new Codicon('gist-fork', { fontCharacter: '\\ea63' }); + public static readonly repoForked = new Codicon('repo-forked', { fontCharacter: '\\ea63' }); + public static readonly gitPullRequest = new Codicon('git-pull-request', { fontCharacter: '\\ea64' }); + public static readonly gitPullRequestAbandoned = new Codicon('git-pull-request-abandoned', { fontCharacter: '\\ea64' }); + public static readonly recordKeys = new Codicon('record-keys', { fontCharacter: '\\ea65' }); + public static readonly keyboard = new Codicon('keyboard', { fontCharacter: '\\ea65' }); + public static readonly tag = new Codicon('tag', { fontCharacter: '\\ea66' }); + public static readonly tagAdd = new Codicon('tag-add', { fontCharacter: '\\ea66' }); + public static readonly tagRemove = new Codicon('tag-remove', { fontCharacter: '\\ea66' }); + public static readonly person = new Codicon('person', { fontCharacter: '\\ea67' }); + public static readonly personFollow = new Codicon('person-follow', { fontCharacter: '\\ea67' }); + public static readonly personOutline = new Codicon('person-outline', { fontCharacter: '\\ea67' }); + public static readonly personFilled = new Codicon('person-filled', { fontCharacter: '\\ea67' }); + public static readonly gitBranch = new Codicon('git-branch', { fontCharacter: '\\ea68' }); + public static readonly gitBranchCreate = new Codicon('git-branch-create', { fontCharacter: '\\ea68' }); + public static readonly gitBranchDelete = new Codicon('git-branch-delete', { fontCharacter: '\\ea68' }); + public static readonly sourceControl = new Codicon('source-control', { fontCharacter: '\\ea68' }); + public static readonly mirror = new Codicon('mirror', { fontCharacter: '\\ea69' }); + public static readonly mirrorPublic = new Codicon('mirror-public', { fontCharacter: '\\ea69' }); + public static readonly star = new Codicon('star', { fontCharacter: '\\ea6a' }); + public static readonly starAdd = new Codicon('star-add', { fontCharacter: '\\ea6a' }); + public static readonly starDelete = new Codicon('star-delete', { fontCharacter: '\\ea6a' }); + public static readonly starEmpty = new Codicon('star-empty', { fontCharacter: '\\ea6a' }); + public static readonly comment = new Codicon('comment', { fontCharacter: '\\ea6b' }); + public static readonly commentAdd = new Codicon('comment-add', { fontCharacter: '\\ea6b' }); + public static readonly alert = new Codicon('alert', { fontCharacter: '\\ea6c' }); + public static readonly warning = new Codicon('warning', { fontCharacter: '\\ea6c' }); + public static readonly search = new Codicon('search', { fontCharacter: '\\ea6d' }); + public static readonly searchSave = new Codicon('search-save', { fontCharacter: '\\ea6d' }); + public static readonly logOut = new Codicon('log-out', { fontCharacter: '\\ea6e' }); + public static readonly signOut = new Codicon('sign-out', { fontCharacter: '\\ea6e' }); + public static readonly logIn = new Codicon('log-in', { fontCharacter: '\\ea6f' }); + public static readonly signIn = new Codicon('sign-in', { fontCharacter: '\\ea6f' }); + public static readonly eye = new Codicon('eye', { fontCharacter: '\\ea70' }); + public static readonly eyeUnwatch = new Codicon('eye-unwatch', { fontCharacter: '\\ea70' }); + public static readonly eyeWatch = new Codicon('eye-watch', { fontCharacter: '\\ea70' }); + public static readonly circleFilled = new Codicon('circle-filled', { fontCharacter: '\\ea71' }); + public static readonly primitiveDot = new Codicon('primitive-dot', { fontCharacter: '\\ea71' }); + public static readonly closeDirty = new Codicon('close-dirty', { fontCharacter: '\\ea71' }); + public static readonly debugBreakpoint = new Codicon('debug-breakpoint', { fontCharacter: '\\ea71' }); + public static readonly debugBreakpointDisabled = new Codicon('debug-breakpoint-disabled', { fontCharacter: '\\ea71' }); + public static readonly debugHint = new Codicon('debug-hint', { fontCharacter: '\\ea71' }); + public static readonly primitiveSquare = new Codicon('primitive-square', { fontCharacter: '\\ea72' }); + public static readonly edit = new Codicon('edit', { fontCharacter: '\\ea73' }); + public static readonly pencil = new Codicon('pencil', { fontCharacter: '\\ea73' }); + public static readonly info = new Codicon('info', { fontCharacter: '\\ea74' }); + public static readonly issueOpened = new Codicon('issue-opened', { fontCharacter: '\\ea74' }); + public static readonly gistPrivate = new Codicon('gist-private', { fontCharacter: '\\ea75' }); + public static readonly gitForkPrivate = new Codicon('git-fork-private', { fontCharacter: '\\ea75' }); + public static readonly lock = new Codicon('lock', { fontCharacter: '\\ea75' }); + public static readonly mirrorPrivate = new Codicon('mirror-private', { fontCharacter: '\\ea75' }); + public static readonly close = new Codicon('close', { fontCharacter: '\\ea76' }); + public static readonly removeClose = new Codicon('remove-close', { fontCharacter: '\\ea76' }); + public static readonly x = new Codicon('x', { fontCharacter: '\\ea76' }); + public static readonly repoSync = new Codicon('repo-sync', { fontCharacter: '\\ea77' }); + public static readonly sync = new Codicon('sync', { fontCharacter: '\\ea77' }); + public static readonly clone = new Codicon('clone', { fontCharacter: '\\ea78' }); + public static readonly desktopDownload = new Codicon('desktop-download', { fontCharacter: '\\ea78' }); + public static readonly beaker = new Codicon('beaker', { fontCharacter: '\\ea79' }); + public static readonly microscope = new Codicon('microscope', { fontCharacter: '\\ea79' }); + public static readonly vm = new Codicon('vm', { fontCharacter: '\\ea7a' }); + public static readonly deviceDesktop = new Codicon('device-desktop', { fontCharacter: '\\ea7a' }); + public static readonly file = new Codicon('file', { fontCharacter: '\\ea7b' }); + public static readonly fileText = new Codicon('file-text', { fontCharacter: '\\ea7b' }); + public static readonly more = new Codicon('more', { fontCharacter: '\\ea7c' }); + public static readonly ellipsis = new Codicon('ellipsis', { fontCharacter: '\\ea7c' }); + public static readonly kebabHorizontal = new Codicon('kebab-horizontal', { fontCharacter: '\\ea7c' }); + public static readonly mailReply = new Codicon('mail-reply', { fontCharacter: '\\ea7d' }); + public static readonly reply = new Codicon('reply', { fontCharacter: '\\ea7d' }); + public static readonly organization = new Codicon('organization', { fontCharacter: '\\ea7e' }); + public static readonly organizationFilled = new Codicon('organization-filled', { fontCharacter: '\\ea7e' }); + public static readonly organizationOutline = new Codicon('organization-outline', { fontCharacter: '\\ea7e' }); + public static readonly newFile = new Codicon('new-file', { fontCharacter: '\\ea7f' }); + public static readonly fileAdd = new Codicon('file-add', { fontCharacter: '\\ea7f' }); + public static readonly newFolder = new Codicon('new-folder', { fontCharacter: '\\ea80' }); + public static readonly fileDirectoryCreate = new Codicon('file-directory-create', { fontCharacter: '\\ea80' }); + public static readonly trash = new Codicon('trash', { fontCharacter: '\\ea81' }); + public static readonly trashcan = new Codicon('trashcan', { fontCharacter: '\\ea81' }); + public static readonly history = new Codicon('history', { fontCharacter: '\\ea82' }); + public static readonly clock = new Codicon('clock', { fontCharacter: '\\ea82' }); + public static readonly folder = new Codicon('folder', { fontCharacter: '\\ea83' }); + public static readonly fileDirectory = new Codicon('file-directory', { fontCharacter: '\\ea83' }); + public static readonly symbolFolder = new Codicon('symbol-folder', { fontCharacter: '\\ea83' }); + public static readonly logoGithub = new Codicon('logo-github', { fontCharacter: '\\ea84' }); + public static readonly markGithub = new Codicon('mark-github', { fontCharacter: '\\ea84' }); + public static readonly github = new Codicon('github', { fontCharacter: '\\ea84' }); + public static readonly terminal = new Codicon('terminal', { fontCharacter: '\\ea85' }); + public static readonly console = new Codicon('console', { fontCharacter: '\\ea85' }); + public static readonly repl = new Codicon('repl', { fontCharacter: '\\ea85' }); + public static readonly zap = new Codicon('zap', { fontCharacter: '\\ea86' }); + public static readonly symbolEvent = new Codicon('symbol-event', { fontCharacter: '\\ea86' }); + public static readonly error = new Codicon('error', { fontCharacter: '\\ea87' }); + public static readonly stop = new Codicon('stop', { fontCharacter: '\\ea87' }); + public static readonly variable = new Codicon('variable', { fontCharacter: '\\ea88' }); + public static readonly symbolVariable = new Codicon('symbol-variable', { fontCharacter: '\\ea88' }); + public static readonly array = new Codicon('array', { fontCharacter: '\\ea8a' }); + public static readonly symbolArray = new Codicon('symbol-array', { fontCharacter: '\\ea8a' }); + public static readonly symbolModule = new Codicon('symbol-module', { fontCharacter: '\\ea8b' }); + public static readonly symbolPackage = new Codicon('symbol-package', { fontCharacter: '\\ea8b' }); + public static readonly symbolNamespace = new Codicon('symbol-namespace', { fontCharacter: '\\ea8b' }); + public static readonly symbolObject = new Codicon('symbol-object', { fontCharacter: '\\ea8b' }); + public static readonly symbolMethod = new Codicon('symbol-method', { fontCharacter: '\\ea8c' }); + public static readonly symbolFunction = new Codicon('symbol-function', { fontCharacter: '\\ea8c' }); + public static readonly symbolConstructor = new Codicon('symbol-constructor', { fontCharacter: '\\ea8c' }); + public static readonly symbolBoolean = new Codicon('symbol-boolean', { fontCharacter: '\\ea8f' }); + public static readonly symbolNull = new Codicon('symbol-null', { fontCharacter: '\\ea8f' }); + public static readonly symbolNumeric = new Codicon('symbol-numeric', { fontCharacter: '\\ea90' }); + public static readonly symbolNumber = new Codicon('symbol-number', { fontCharacter: '\\ea90' }); + public static readonly symbolStructure = new Codicon('symbol-structure', { fontCharacter: '\\ea91' }); + public static readonly symbolStruct = new Codicon('symbol-struct', { fontCharacter: '\\ea91' }); + public static readonly symbolParameter = new Codicon('symbol-parameter', { fontCharacter: '\\ea92' }); + public static readonly symbolTypeParameter = new Codicon('symbol-type-parameter', { fontCharacter: '\\ea92' }); + public static readonly symbolKey = new Codicon('symbol-key', { fontCharacter: '\\ea93' }); + public static readonly symbolText = new Codicon('symbol-text', { fontCharacter: '\\ea93' }); + public static readonly symbolReference = new Codicon('symbol-reference', { fontCharacter: '\\ea94' }); + public static readonly goToFile = new Codicon('go-to-file', { fontCharacter: '\\ea94' }); + public static readonly symbolEnum = new Codicon('symbol-enum', { fontCharacter: '\\ea95' }); + public static readonly symbolValue = new Codicon('symbol-value', { fontCharacter: '\\ea95' }); + public static readonly symbolRuler = new Codicon('symbol-ruler', { fontCharacter: '\\ea96' }); + public static readonly symbolUnit = new Codicon('symbol-unit', { fontCharacter: '\\ea96' }); + public static readonly activateBreakpoints = new Codicon('activate-breakpoints', { fontCharacter: '\\ea97' }); + public static readonly archive = new Codicon('archive', { fontCharacter: '\\ea98' }); + public static readonly arrowBoth = new Codicon('arrow-both', { fontCharacter: '\\ea99' }); + public static readonly arrowDown = new Codicon('arrow-down', { fontCharacter: '\\ea9a' }); + public static readonly arrowLeft = new Codicon('arrow-left', { fontCharacter: '\\ea9b' }); + public static readonly arrowRight = new Codicon('arrow-right', { fontCharacter: '\\ea9c' }); + public static readonly arrowSmallDown = new Codicon('arrow-small-down', { fontCharacter: '\\ea9d' }); + public static readonly arrowSmallLeft = new Codicon('arrow-small-left', { fontCharacter: '\\ea9e' }); + public static readonly arrowSmallRight = new Codicon('arrow-small-right', { fontCharacter: '\\ea9f' }); + public static readonly arrowSmallUp = new Codicon('arrow-small-up', { fontCharacter: '\\eaa0' }); + public static readonly arrowUp = new Codicon('arrow-up', { fontCharacter: '\\eaa1' }); + public static readonly bell = new Codicon('bell', { fontCharacter: '\\eaa2' }); + public static readonly bold = new Codicon('bold', { fontCharacter: '\\eaa3' }); + public static readonly book = new Codicon('book', { fontCharacter: '\\eaa4' }); + public static readonly bookmark = new Codicon('bookmark', { fontCharacter: '\\eaa5' }); + public static readonly debugBreakpointConditionalUnverified = new Codicon('debug-breakpoint-conditional-unverified', { fontCharacter: '\\eaa6' }); + public static readonly debugBreakpointConditional = new Codicon('debug-breakpoint-conditional', { fontCharacter: '\\eaa7' }); + public static readonly debugBreakpointConditionalDisabled = new Codicon('debug-breakpoint-conditional-disabled', { fontCharacter: '\\eaa7' }); + public static readonly debugBreakpointDataUnverified = new Codicon('debug-breakpoint-data-unverified', { fontCharacter: '\\eaa8' }); + public static readonly debugBreakpointData = new Codicon('debug-breakpoint-data', { fontCharacter: '\\eaa9' }); + public static readonly debugBreakpointDataDisabled = new Codicon('debug-breakpoint-data-disabled', { fontCharacter: '\\eaa9' }); + public static readonly debugBreakpointLogUnverified = new Codicon('debug-breakpoint-log-unverified', { fontCharacter: '\\eaaa' }); + public static readonly debugBreakpointLog = new Codicon('debug-breakpoint-log', { fontCharacter: '\\eaab' }); + public static readonly debugBreakpointLogDisabled = new Codicon('debug-breakpoint-log-disabled', { fontCharacter: '\\eaab' }); + public static readonly briefcase = new Codicon('briefcase', { fontCharacter: '\\eaac' }); + public static readonly broadcast = new Codicon('broadcast', { fontCharacter: '\\eaad' }); + public static readonly browser = new Codicon('browser', { fontCharacter: '\\eaae' }); + public static readonly bug = new Codicon('bug', { fontCharacter: '\\eaaf' }); + public static readonly calendar = new Codicon('calendar', { fontCharacter: '\\eab0' }); + public static readonly caseSensitive = new Codicon('case-sensitive', { fontCharacter: '\\eab1' }); + public static readonly check = new Codicon('check', { fontCharacter: '\\eab2' }); + public static readonly checklist = new Codicon('checklist', { fontCharacter: '\\eab3' }); + public static readonly chevronDown = new Codicon('chevron-down', { fontCharacter: '\\eab4' }); + public static readonly dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition); + public static readonly chevronLeft = new Codicon('chevron-left', { fontCharacter: '\\eab5' }); + public static readonly chevronRight = new Codicon('chevron-right', { fontCharacter: '\\eab6' }); + public static readonly chevronUp = new Codicon('chevron-up', { fontCharacter: '\\eab7' }); + public static readonly chromeClose = new Codicon('chrome-close', { fontCharacter: '\\eab8' }); + public static readonly chromeMaximize = new Codicon('chrome-maximize', { fontCharacter: '\\eab9' }); + public static readonly chromeMinimize = new Codicon('chrome-minimize', { fontCharacter: '\\eaba' }); + public static readonly chromeRestore = new Codicon('chrome-restore', { fontCharacter: '\\eabb' }); + public static readonly circleOutline = new Codicon('circle-outline', { fontCharacter: '\\eabc' }); + public static readonly debugBreakpointUnverified = new Codicon('debug-breakpoint-unverified', { fontCharacter: '\\eabc' }); + public static readonly circleSlash = new Codicon('circle-slash', { fontCharacter: '\\eabd' }); + public static readonly circuitBoard = new Codicon('circuit-board', { fontCharacter: '\\eabe' }); + public static readonly clearAll = new Codicon('clear-all', { fontCharacter: '\\eabf' }); + public static readonly clippy = new Codicon('clippy', { fontCharacter: '\\eac0' }); + public static readonly closeAll = new Codicon('close-all', { fontCharacter: '\\eac1' }); + public static readonly cloudDownload = new Codicon('cloud-download', { fontCharacter: '\\eac2' }); + public static readonly cloudUpload = new Codicon('cloud-upload', { fontCharacter: '\\eac3' }); + public static readonly code = new Codicon('code', { fontCharacter: '\\eac4' }); + public static readonly collapseAll = new Codicon('collapse-all', { fontCharacter: '\\eac5' }); + public static readonly colorMode = new Codicon('color-mode', { fontCharacter: '\\eac6' }); + public static readonly commentDiscussion = new Codicon('comment-discussion', { fontCharacter: '\\eac7' }); + public static readonly compareChanges = new Codicon('compare-changes', { fontCharacter: '\\eafd' }); + public static readonly creditCard = new Codicon('credit-card', { fontCharacter: '\\eac9' }); + public static readonly dash = new Codicon('dash', { fontCharacter: '\\eacc' }); + public static readonly dashboard = new Codicon('dashboard', { fontCharacter: '\\eacd' }); + public static readonly database = new Codicon('database', { fontCharacter: '\\eace' }); + public static readonly debugContinue = new Codicon('debug-continue', { fontCharacter: '\\eacf' }); + public static readonly debugDisconnect = new Codicon('debug-disconnect', { fontCharacter: '\\ead0' }); + public static readonly debugPause = new Codicon('debug-pause', { fontCharacter: '\\ead1' }); + public static readonly debugRestart = new Codicon('debug-restart', { fontCharacter: '\\ead2' }); + public static readonly debugStart = new Codicon('debug-start', { fontCharacter: '\\ead3' }); + public static readonly debugStepInto = new Codicon('debug-step-into', { fontCharacter: '\\ead4' }); + public static readonly debugStepOut = new Codicon('debug-step-out', { fontCharacter: '\\ead5' }); + public static readonly debugStepOver = new Codicon('debug-step-over', { fontCharacter: '\\ead6' }); + public static readonly debugStop = new Codicon('debug-stop', { fontCharacter: '\\ead7' }); + public static readonly debug = new Codicon('debug', { fontCharacter: '\\ead8' }); + public static readonly deviceCameraVideo = new Codicon('device-camera-video', { fontCharacter: '\\ead9' }); + public static readonly deviceCamera = new Codicon('device-camera', { fontCharacter: '\\eada' }); + public static readonly deviceMobile = new Codicon('device-mobile', { fontCharacter: '\\eadb' }); + public static readonly diffAdded = new Codicon('diff-added', { fontCharacter: '\\eadc' }); + public static readonly diffIgnored = new Codicon('diff-ignored', { fontCharacter: '\\eadd' }); + public static readonly diffModified = new Codicon('diff-modified', { fontCharacter: '\\eade' }); + public static readonly diffRemoved = new Codicon('diff-removed', { fontCharacter: '\\eadf' }); + public static readonly diffRenamed = new Codicon('diff-renamed', { fontCharacter: '\\eae0' }); + public static readonly diff = new Codicon('diff', { fontCharacter: '\\eae1' }); + public static readonly discard = new Codicon('discard', { fontCharacter: '\\eae2' }); + public static readonly editorLayout = new Codicon('editor-layout', { fontCharacter: '\\eae3' }); + public static readonly emptyWindow = new Codicon('empty-window', { fontCharacter: '\\eae4' }); + public static readonly exclude = new Codicon('exclude', { fontCharacter: '\\eae5' }); + public static readonly extensions = new Codicon('extensions', { fontCharacter: '\\eae6' }); + public static readonly eyeClosed = new Codicon('eye-closed', { fontCharacter: '\\eae7' }); + public static readonly fileBinary = new Codicon('file-binary', { fontCharacter: '\\eae8' }); + public static readonly fileCode = new Codicon('file-code', { fontCharacter: '\\eae9' }); + public static readonly fileMedia = new Codicon('file-media', { fontCharacter: '\\eaea' }); + public static readonly filePdf = new Codicon('file-pdf', { fontCharacter: '\\eaeb' }); + public static readonly fileSubmodule = new Codicon('file-submodule', { fontCharacter: '\\eaec' }); + public static readonly fileSymlinkDirectory = new Codicon('file-symlink-directory', { fontCharacter: '\\eaed' }); + public static readonly fileSymlinkFile = new Codicon('file-symlink-file', { fontCharacter: '\\eaee' }); + public static readonly fileZip = new Codicon('file-zip', { fontCharacter: '\\eaef' }); + public static readonly files = new Codicon('files', { fontCharacter: '\\eaf0' }); + public static readonly filter = new Codicon('filter', { fontCharacter: '\\eaf1' }); + public static readonly flame = new Codicon('flame', { fontCharacter: '\\eaf2' }); + public static readonly foldDown = new Codicon('fold-down', { fontCharacter: '\\eaf3' }); + public static readonly foldUp = new Codicon('fold-up', { fontCharacter: '\\eaf4' }); + public static readonly fold = new Codicon('fold', { fontCharacter: '\\eaf5' }); + public static readonly folderActive = new Codicon('folder-active', { fontCharacter: '\\eaf6' }); + public static readonly folderOpened = new Codicon('folder-opened', { fontCharacter: '\\eaf7' }); + public static readonly gear = new Codicon('gear', { fontCharacter: '\\eaf8' }); + public static readonly gift = new Codicon('gift', { fontCharacter: '\\eaf9' }); + public static readonly gistSecret = new Codicon('gist-secret', { fontCharacter: '\\eafa' }); + public static readonly gist = new Codicon('gist', { fontCharacter: '\\eafb' }); + public static readonly gitCommit = new Codicon('git-commit', { fontCharacter: '\\eafc' }); + public static readonly gitCompare = new Codicon('git-compare', { fontCharacter: '\\eafd' }); + public static readonly gitMerge = new Codicon('git-merge', { fontCharacter: '\\eafe' }); + public static readonly githubAction = new Codicon('github-action', { fontCharacter: '\\eaff' }); + public static readonly githubAlt = new Codicon('github-alt', { fontCharacter: '\\eb00' }); + public static readonly globe = new Codicon('globe', { fontCharacter: '\\eb01' }); + public static readonly grabber = new Codicon('grabber', { fontCharacter: '\\eb02' }); + public static readonly graph = new Codicon('graph', { fontCharacter: '\\eb03' }); + public static readonly gripper = new Codicon('gripper', { fontCharacter: '\\eb04' }); + public static readonly heart = new Codicon('heart', { fontCharacter: '\\eb05' }); + public static readonly home = new Codicon('home', { fontCharacter: '\\eb06' }); + public static readonly horizontalRule = new Codicon('horizontal-rule', { fontCharacter: '\\eb07' }); + public static readonly hubot = new Codicon('hubot', { fontCharacter: '\\eb08' }); + public static readonly inbox = new Codicon('inbox', { fontCharacter: '\\eb09' }); + public static readonly issueClosed = new Codicon('issue-closed', { fontCharacter: '\\eba4' }); + public static readonly issueReopened = new Codicon('issue-reopened', { fontCharacter: '\\eb0b' }); + public static readonly issues = new Codicon('issues', { fontCharacter: '\\eb0c' }); + public static readonly italic = new Codicon('italic', { fontCharacter: '\\eb0d' }); + public static readonly jersey = new Codicon('jersey', { fontCharacter: '\\eb0e' }); + public static readonly json = new Codicon('json', { fontCharacter: '\\eb0f' }); + public static readonly kebabVertical = new Codicon('kebab-vertical', { fontCharacter: '\\eb10' }); + public static readonly key = new Codicon('key', { fontCharacter: '\\eb11' }); + public static readonly law = new Codicon('law', { fontCharacter: '\\eb12' }); + public static readonly lightbulbAutofix = new Codicon('lightbulb-autofix', { fontCharacter: '\\eb13' }); + public static readonly linkExternal = new Codicon('link-external', { fontCharacter: '\\eb14' }); + public static readonly link = new Codicon('link', { fontCharacter: '\\eb15' }); + public static readonly listOrdered = new Codicon('list-ordered', { fontCharacter: '\\eb16' }); + public static readonly listUnordered = new Codicon('list-unordered', { fontCharacter: '\\eb17' }); + public static readonly liveShare = new Codicon('live-share', { fontCharacter: '\\eb18' }); + public static readonly loading = new Codicon('loading', { fontCharacter: '\\eb19' }); + public static readonly location = new Codicon('location', { fontCharacter: '\\eb1a' }); + public static readonly mailRead = new Codicon('mail-read', { fontCharacter: '\\eb1b' }); + public static readonly mail = new Codicon('mail', { fontCharacter: '\\eb1c' }); + public static readonly markdown = new Codicon('markdown', { fontCharacter: '\\eb1d' }); + public static readonly megaphone = new Codicon('megaphone', { fontCharacter: '\\eb1e' }); + public static readonly mention = new Codicon('mention', { fontCharacter: '\\eb1f' }); + public static readonly milestone = new Codicon('milestone', { fontCharacter: '\\eb20' }); + public static readonly mortarBoard = new Codicon('mortar-board', { fontCharacter: '\\eb21' }); + public static readonly move = new Codicon('move', { fontCharacter: '\\eb22' }); + public static readonly multipleWindows = new Codicon('multiple-windows', { fontCharacter: '\\eb23' }); + public static readonly mute = new Codicon('mute', { fontCharacter: '\\eb24' }); + public static readonly noNewline = new Codicon('no-newline', { fontCharacter: '\\eb25' }); + public static readonly note = new Codicon('note', { fontCharacter: '\\eb26' }); + public static readonly octoface = new Codicon('octoface', { fontCharacter: '\\eb27' }); + public static readonly openPreview = new Codicon('open-preview', { fontCharacter: '\\eb28' }); + public static readonly package_ = new Codicon('package', { fontCharacter: '\\eb29' }); + public static readonly paintcan = new Codicon('paintcan', { fontCharacter: '\\eb2a' }); + public static readonly pin = new Codicon('pin', { fontCharacter: '\\eb2b' }); + public static readonly play = new Codicon('play', { fontCharacter: '\\eb2c' }); + public static readonly run = new Codicon('run', { fontCharacter: '\\eb2c' }); + public static readonly plug = new Codicon('plug', { fontCharacter: '\\eb2d' }); + public static readonly preserveCase = new Codicon('preserve-case', { fontCharacter: '\\eb2e' }); + public static readonly preview = new Codicon('preview', { fontCharacter: '\\eb2f' }); + public static readonly project = new Codicon('project', { fontCharacter: '\\eb30' }); + public static readonly pulse = new Codicon('pulse', { fontCharacter: '\\eb31' }); + public static readonly question = new Codicon('question', { fontCharacter: '\\eb32' }); + public static readonly quote = new Codicon('quote', { fontCharacter: '\\eb33' }); + public static readonly radioTower = new Codicon('radio-tower', { fontCharacter: '\\eb34' }); + public static readonly reactions = new Codicon('reactions', { fontCharacter: '\\eb35' }); + public static readonly references = new Codicon('references', { fontCharacter: '\\eb36' }); + public static readonly refresh = new Codicon('refresh', { fontCharacter: '\\eb37' }); + public static readonly regex = new Codicon('regex', { fontCharacter: '\\eb38' }); + public static readonly remoteExplorer = new Codicon('remote-explorer', { fontCharacter: '\\eb39' }); + public static readonly remote = new Codicon('remote', { fontCharacter: '\\eb3a' }); + public static readonly remove = new Codicon('remove', { fontCharacter: '\\eb3b' }); + public static readonly replaceAll = new Codicon('replace-all', { fontCharacter: '\\eb3c' }); + public static readonly replace = new Codicon('replace', { fontCharacter: '\\eb3d' }); + public static readonly repoClone = new Codicon('repo-clone', { fontCharacter: '\\eb3e' }); + public static readonly repoForcePush = new Codicon('repo-force-push', { fontCharacter: '\\eb3f' }); + public static readonly repoPull = new Codicon('repo-pull', { fontCharacter: '\\eb40' }); + public static readonly repoPush = new Codicon('repo-push', { fontCharacter: '\\eb41' }); + public static readonly report = new Codicon('report', { fontCharacter: '\\eb42' }); + public static readonly requestChanges = new Codicon('request-changes', { fontCharacter: '\\eb43' }); + public static readonly rocket = new Codicon('rocket', { fontCharacter: '\\eb44' }); + public static readonly rootFolderOpened = new Codicon('root-folder-opened', { fontCharacter: '\\eb45' }); + public static readonly rootFolder = new Codicon('root-folder', { fontCharacter: '\\eb46' }); + public static readonly rss = new Codicon('rss', { fontCharacter: '\\eb47' }); + public static readonly ruby = new Codicon('ruby', { fontCharacter: '\\eb48' }); + public static readonly saveAll = new Codicon('save-all', { fontCharacter: '\\eb49' }); + public static readonly saveAs = new Codicon('save-as', { fontCharacter: '\\eb4a' }); + public static readonly save = new Codicon('save', { fontCharacter: '\\eb4b' }); + public static readonly screenFull = new Codicon('screen-full', { fontCharacter: '\\eb4c' }); + public static readonly screenNormal = new Codicon('screen-normal', { fontCharacter: '\\eb4d' }); + public static readonly searchStop = new Codicon('search-stop', { fontCharacter: '\\eb4e' }); + public static readonly server = new Codicon('server', { fontCharacter: '\\eb50' }); + public static readonly settingsGear = new Codicon('settings-gear', { fontCharacter: '\\eb51' }); + public static readonly settings = new Codicon('settings', { fontCharacter: '\\eb52' }); + public static readonly shield = new Codicon('shield', { fontCharacter: '\\eb53' }); + public static readonly smiley = new Codicon('smiley', { fontCharacter: '\\eb54' }); + public static readonly sortPrecedence = new Codicon('sort-precedence', { fontCharacter: '\\eb55' }); + public static readonly splitHorizontal = new Codicon('split-horizontal', { fontCharacter: '\\eb56' }); + public static readonly splitVertical = new Codicon('split-vertical', { fontCharacter: '\\eb57' }); + public static readonly squirrel = new Codicon('squirrel', { fontCharacter: '\\eb58' }); + public static readonly starFull = new Codicon('star-full', { fontCharacter: '\\eb59' }); + public static readonly starHalf = new Codicon('star-half', { fontCharacter: '\\eb5a' }); + public static readonly symbolClass = new Codicon('symbol-class', { fontCharacter: '\\eb5b' }); + public static readonly symbolColor = new Codicon('symbol-color', { fontCharacter: '\\eb5c' }); + public static readonly symbolConstant = new Codicon('symbol-constant', { fontCharacter: '\\eb5d' }); + public static readonly symbolEnumMember = new Codicon('symbol-enum-member', { fontCharacter: '\\eb5e' }); + public static readonly symbolField = new Codicon('symbol-field', { fontCharacter: '\\eb5f' }); + public static readonly symbolFile = new Codicon('symbol-file', { fontCharacter: '\\eb60' }); + public static readonly symbolInterface = new Codicon('symbol-interface', { fontCharacter: '\\eb61' }); + public static readonly symbolKeyword = new Codicon('symbol-keyword', { fontCharacter: '\\eb62' }); + public static readonly symbolMisc = new Codicon('symbol-misc', { fontCharacter: '\\eb63' }); + public static readonly symbolOperator = new Codicon('symbol-operator', { fontCharacter: '\\eb64' }); + public static readonly symbolProperty = new Codicon('symbol-property', { fontCharacter: '\\eb65' }); + public static readonly wrench = new Codicon('wrench', { fontCharacter: '\\eb65' }); + public static readonly wrenchSubaction = new Codicon('wrench-subaction', { fontCharacter: '\\eb65' }); + public static readonly symbolSnippet = new Codicon('symbol-snippet', { fontCharacter: '\\eb66' }); + public static readonly tasklist = new Codicon('tasklist', { fontCharacter: '\\eb67' }); + public static readonly telescope = new Codicon('telescope', { fontCharacter: '\\eb68' }); + public static readonly textSize = new Codicon('text-size', { fontCharacter: '\\eb69' }); + public static readonly threeBars = new Codicon('three-bars', { fontCharacter: '\\eb6a' }); + public static readonly thumbsdown = new Codicon('thumbsdown', { fontCharacter: '\\eb6b' }); + public static readonly thumbsup = new Codicon('thumbsup', { fontCharacter: '\\eb6c' }); + public static readonly tools = new Codicon('tools', { fontCharacter: '\\eb6d' }); + public static readonly triangleDown = new Codicon('triangle-down', { fontCharacter: '\\eb6e' }); + public static readonly triangleLeft = new Codicon('triangle-left', { fontCharacter: '\\eb6f' }); + public static readonly triangleRight = new Codicon('triangle-right', { fontCharacter: '\\eb70' }); + public static readonly triangleUp = new Codicon('triangle-up', { fontCharacter: '\\eb71' }); + public static readonly twitter = new Codicon('twitter', { fontCharacter: '\\eb72' }); + public static readonly unfold = new Codicon('unfold', { fontCharacter: '\\eb73' }); + public static readonly unlock = new Codicon('unlock', { fontCharacter: '\\eb74' }); + public static readonly unmute = new Codicon('unmute', { fontCharacter: '\\eb75' }); + public static readonly unverified = new Codicon('unverified', { fontCharacter: '\\eb76' }); + public static readonly verified = new Codicon('verified', { fontCharacter: '\\eb77' }); + public static readonly versions = new Codicon('versions', { fontCharacter: '\\eb78' }); + public static readonly vmActive = new Codicon('vm-active', { fontCharacter: '\\eb79' }); + public static readonly vmOutline = new Codicon('vm-outline', { fontCharacter: '\\eb7a' }); + public static readonly vmRunning = new Codicon('vm-running', { fontCharacter: '\\eb7b' }); + public static readonly watch = new Codicon('watch', { fontCharacter: '\\eb7c' }); + public static readonly whitespace = new Codicon('whitespace', { fontCharacter: '\\eb7d' }); + public static readonly wholeWord = new Codicon('whole-word', { fontCharacter: '\\eb7e' }); + public static readonly window = new Codicon('window', { fontCharacter: '\\eb7f' }); + public static readonly wordWrap = new Codicon('word-wrap', { fontCharacter: '\\eb80' }); + public static readonly zoomIn = new Codicon('zoom-in', { fontCharacter: '\\eb81' }); + public static readonly zoomOut = new Codicon('zoom-out', { fontCharacter: '\\eb82' }); + public static readonly listFilter = new Codicon('list-filter', { fontCharacter: '\\eb83' }); + public static readonly listFlat = new Codicon('list-flat', { fontCharacter: '\\eb84' }); + public static readonly listSelection = new Codicon('list-selection', { fontCharacter: '\\eb85' }); + public static readonly selection = new Codicon('selection', { fontCharacter: '\\eb85' }); + public static readonly listTree = new Codicon('list-tree', { fontCharacter: '\\eb86' }); + public static readonly debugBreakpointFunctionUnverified = new Codicon('debug-breakpoint-function-unverified', { fontCharacter: '\\eb87' }); + public static readonly debugBreakpointFunction = new Codicon('debug-breakpoint-function', { fontCharacter: '\\eb88' }); + public static readonly debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { fontCharacter: '\\eb88' }); + public static readonly debugStackframeActive = new Codicon('debug-stackframe-active', { fontCharacter: '\\eb89' }); + public static readonly debugStackframeDot = new Codicon('debug-stackframe-dot', { fontCharacter: '\\eb8a' }); + public static readonly debugStackframe = new Codicon('debug-stackframe', { fontCharacter: '\\eb8b' }); + public static readonly debugStackframeFocused = new Codicon('debug-stackframe-focused', { fontCharacter: '\\eb8b' }); + public static readonly debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { fontCharacter: '\\eb8c' }); + public static readonly symbolString = new Codicon('symbol-string', { fontCharacter: '\\eb8d' }); + public static readonly debugReverseContinue = new Codicon('debug-reverse-continue', { fontCharacter: '\\eb8e' }); + public static readonly debugStepBack = new Codicon('debug-step-back', { fontCharacter: '\\eb8f' }); + public static readonly debugRestartFrame = new Codicon('debug-restart-frame', { fontCharacter: '\\eb90' }); + public static readonly callIncoming = new Codicon('call-incoming', { fontCharacter: '\\eb92' }); + public static readonly callOutgoing = new Codicon('call-outgoing', { fontCharacter: '\\eb93' }); + public static readonly menu = new Codicon('menu', { fontCharacter: '\\eb94' }); + public static readonly expandAll = new Codicon('expand-all', { fontCharacter: '\\eb95' }); + public static readonly feedback = new Codicon('feedback', { fontCharacter: '\\eb96' }); + public static readonly groupByRefType = new Codicon('group-by-ref-type', { fontCharacter: '\\eb97' }); + public static readonly ungroupByRefType = new Codicon('ungroup-by-ref-type', { fontCharacter: '\\eb98' }); + public static readonly account = new Codicon('account', { fontCharacter: '\\eb99' }); + public static readonly bellDot = new Codicon('bell-dot', { fontCharacter: '\\eb9a' }); + public static readonly debugConsole = new Codicon('debug-console', { fontCharacter: '\\eb9b' }); + public static readonly library = new Codicon('library', { fontCharacter: '\\eb9c' }); + public static readonly output = new Codicon('output', { fontCharacter: '\\eb9d' }); + public static readonly runAll = new Codicon('run-all', { fontCharacter: '\\eb9e' }); + public static readonly syncIgnored = new Codicon('sync-ignored', { fontCharacter: '\\eb9f' }); + public static readonly pinned = new Codicon('pinned', { fontCharacter: '\\eba0' }); + public static readonly githubInverted = new Codicon('github-inverted', { fontCharacter: '\\eba1' }); + public static readonly debugAlt = new Codicon('debug-alt', { fontCharacter: '\\eb91' }); + public static readonly serverProcess = new Codicon('server-process', { fontCharacter: '\\eba2' }); + public static readonly serverEnvironment = new Codicon('server-environment', { fontCharacter: '\\eba3' }); + public static readonly pass = new Codicon('pass', { fontCharacter: '\\eba4' }); + public static readonly stopCircle = new Codicon('stop-circle', { fontCharacter: '\\eba5' }); + public static readonly playCircle = new Codicon('play-circle', { fontCharacter: '\\eba6' }); + public static readonly record = new Codicon('record', { fontCharacter: '\\eba7' }); + public static readonly debugAltSmall = new Codicon('debug-alt-small', { fontCharacter: '\\eba8' }); + public static readonly vmConnect = new Codicon('vm-connect', { fontCharacter: '\\eba9' }); + public static readonly cloud = new Codicon('cloud', { fontCharacter: '\\ebaa' }); + public static readonly merge = new Codicon('merge', { fontCharacter: '\\ebab' }); + public static readonly exportIcon = new Codicon('export', { fontCharacter: '\\ebac' }); + public static readonly graphLeft = new Codicon('graph-left', { fontCharacter: '\\ebad' }); + public static readonly magnet = new Codicon('magnet', { fontCharacter: '\\ebae' }); + public static readonly notebook = new Codicon('notebook', { fontCharacter: '\\ebaf' }); + public static readonly redo = new Codicon('redo', { fontCharacter: '\\ebb0' }); + public static readonly checkAll = new Codicon('check-all', { fontCharacter: '\\ebb1' }); + public static readonly pinnedDirty = new Codicon('pinned-dirty', { fontCharacter: '\\ebb2' }); + public static readonly passFilled = new Codicon('pass-filled', { fontCharacter: '\\ebb3' }); + public static readonly circleLargeFilled = new Codicon('circle-large-filled', { fontCharacter: '\\ebb4' }); + public static readonly circleLargeOutline = new Codicon('circle-large-outline', { fontCharacter: '\\ebb5' }); + public static readonly combine = new Codicon('combine', { fontCharacter: '\\ebb6' }); + public static readonly gather = new Codicon('gather', { fontCharacter: '\\ebb6' }); + public static readonly table = new Codicon('table', { fontCharacter: '\\ebb7' }); + public static readonly variableGroup = new Codicon('variable-group', { fontCharacter: '\\ebb8' }); + public static readonly typeHierarchy = new Codicon('type-hierarchy', { fontCharacter: '\\ebb9' }); + public static readonly typeHierarchySub = new Codicon('type-hierarchy-sub', { fontCharacter: '\\ebba' }); + public static readonly typeHierarchySuper = new Codicon('type-hierarchy-super', { fontCharacter: '\\ebbb' }); + public static readonly gitPullRequestCreate = new Codicon('git-pull-request-create', { fontCharacter: '\\ebbc' }); + public static readonly runAbove = new Codicon('run-above', { fontCharacter: '\\ebbd' }); + public static readonly runBelow = new Codicon('run-below', { fontCharacter: '\\ebbe' }); + public static readonly notebookTemplate = new Codicon('notebook-template', { fontCharacter: '\\ebbf' }); + public static readonly debugRerun = new Codicon('debug-rerun', { fontCharacter: '\\ebc0' }); + public static readonly workspaceTrusted = new Codicon('workspace-trusted', { fontCharacter: '\\ebc1' }); + public static readonly workspaceUntrusted = new Codicon('workspace-untrusted', { fontCharacter: '\\ebc2' }); + public static readonly workspaceUnspecified = new Codicon('workspace-unspecified', { fontCharacter: '\\ebc3' }); + public static readonly terminalCmd = new Codicon('terminal-cmd', { fontCharacter: '\\ebc4' }); + public static readonly terminalDebian = new Codicon('terminal-debian', { fontCharacter: '\\ebc5' }); + public static readonly terminalLinux = new Codicon('terminal-linux', { fontCharacter: '\\ebc6' }); + public static readonly terminalPowershell = new Codicon('terminal-powershell', { fontCharacter: '\\ebc7' }); + public static readonly terminalTmux = new Codicon('terminal-tmux', { fontCharacter: '\\ebc8' }); + public static readonly terminalUbuntu = new Codicon('terminal-ubuntu', { fontCharacter: '\\ebc9' }); + public static readonly terminalBash = new Codicon('terminal-bash', { fontCharacter: '\\ebca' }); + public static readonly arrowSwap = new Codicon('arrow-swap', { fontCharacter: '\\ebcb' }); + public static readonly copy = new Codicon('copy', { fontCharacter: '\\ebcc' }); + public static readonly personAdd = new Codicon('person-add', { fontCharacter: '\\ebcd' }); + public static readonly filterFilled = new Codicon('filter-filled', { fontCharacter: '\\ebce' }); + public static readonly wand = new Codicon('wand', { fontCharacter: '\\ebcf' }); + public static readonly debugLineByLine = new Codicon('debug-line-by-line', { fontCharacter: '\\ebd0' }); + public static readonly inspect = new Codicon('inspect', { fontCharacter: '\\ebd1' }); + public static readonly layers = new Codicon('layers', { fontCharacter: '\\ebd2' }); + public static readonly layersDot = new Codicon('layers-dot', { fontCharacter: '\\ebd3' }); + public static readonly layersActive = new Codicon('layers-active', { fontCharacter: '\\ebd4' }); + public static readonly compass = new Codicon('compass', { fontCharacter: '\\ebd5' }); + public static readonly compassDot = new Codicon('compass-dot', { fontCharacter: '\\ebd6' }); + public static readonly compassActive = new Codicon('compass-active', { fontCharacter: '\\ebd7' }); + public static readonly azure = new Codicon('azure', { fontCharacter: '\\ebd8' }); + public static readonly issueDraft = new Codicon('issue-draft', { fontCharacter: '\\ebd9' }); + public static readonly gitPullRequestClosed = new Codicon('git-pull-request-closed', { fontCharacter: '\\ebda' }); + public static readonly gitPullRequestDraft = new Codicon('git-pull-request-draft', { fontCharacter: '\\ebdb' }); + public static readonly debugAll = new Codicon('debug-all', { fontCharacter: '\\ebdc' }); + public static readonly debugCoverage = new Codicon('debug-coverage', { fontCharacter: '\\ebdd' }); + public static readonly runErrors = new Codicon('run-errors', { fontCharacter: '\\ebde' }); + public static readonly folderLibrary = new Codicon('folder-library', { fontCharacter: '\\ebdf' }); + public static readonly debugContinueSmall = new Codicon('debug-continue-small', { fontCharacter: '\\ebe0' }); + public static readonly beakerStop = new Codicon('beaker-stop', { fontCharacter: '\\ebe1' }); + public static readonly graphLine = new Codicon('graph-line', { fontCharacter: '\\ebe2' }); + public static readonly graphScatter = new Codicon('graph-scatter', { fontCharacter: '\\ebe3' }); + public static readonly pieChart = new Codicon('pie-chart', { fontCharacter: '\\ebe4' }); + public static readonly bracket = new Codicon('bracket', Codicon.json.definition); + public static readonly bracketDot = new Codicon('bracket-dot', { fontCharacter: '\\ebe5' }); + public static readonly bracketError = new Codicon('bracket-error', { fontCharacter: '\\ebe6' }); + public static readonly lockSmall = new Codicon('lock-small', { fontCharacter: '\\ebe7' }); + public static readonly azureDevops = new Codicon('azure-devops', { fontCharacter: '\\ebe8' }); + public static readonly verifiedFilled = new Codicon('verified-filled', { fontCharacter: '\\ebe9' }); + public static readonly newLine = new Codicon('newline', { fontCharacter: '\\ebea' }); + + // derived icons, that could become separate icons + + public static readonly dialogError = new Codicon('dialog-error', Codicon.error.definition); + public static readonly dialogWarning = new Codicon('dialog-warning', Codicon.warning.definition); + public static readonly dialogInfo = new Codicon('dialog-info', Codicon.info.definition); + public static readonly dialogClose = new Codicon('dialog-close', Codicon.close.definition); + + public static readonly treeItemExpanded = new Codicon('tree-item-expanded', Codicon.chevronDown.definition); // collapsed is done with rotation + + public static readonly treeFilterOnTypeOn = new Codicon('tree-filter-on-type-on', Codicon.listFilter.definition); + public static readonly treeFilterOnTypeOff = new Codicon('tree-filter-on-type-off', Codicon.listSelection.definition); + public static readonly treeFilterClear = new Codicon('tree-filter-clear', Codicon.close.definition); + + public static readonly treeItemLoading = new Codicon('tree-item-loading', Codicon.loading.definition); + + public static readonly menuSelection = new Codicon('menu-selection', Codicon.check.definition); + public static readonly menuSubmenu = new Codicon('menu-submenu', Codicon.chevronRight.definition); + + public static readonly menuBarMore = new Codicon('menubar-more', Codicon.more.definition); + + public static readonly scrollbarButtonLeft = new Codicon('scrollbar-button-left', Codicon.triangleLeft.definition); + public static readonly scrollbarButtonRight = new Codicon('scrollbar-button-right', Codicon.triangleRight.definition); + + public static readonly scrollbarButtonUp = new Codicon('scrollbar-button-up', Codicon.triangleUp.definition); + public static readonly scrollbarButtonDown = new Codicon('scrollbar-button-down', Codicon.triangleDown.definition); + + public static readonly toolBarMore = new Codicon('toolbar-more', Codicon.more.definition); + + public static readonly quickInputBack = new Codicon('quick-input-back', Codicon.arrowLeft.definition); } export function getClassNamesArray(id: string, modifier?: string) { @@ -116,491 +630,6 @@ export namespace CSSIcon { } - interface IconDefinition { fontCharacter: string; } - -export namespace Codicon { - - // built-in icons, with image name - export const add = new Codicon('add', { fontCharacter: '\\ea60' }); - export const plus = new Codicon('plus', Codicon.add.definition); - export const gistNew = new Codicon('gist-new', Codicon.add.definition); - export const repoCreate = new Codicon('repo-create', Codicon.add.definition); - export const lightbulb = new Codicon('lightbulb', { fontCharacter: '\\ea61' }); - export const lightBulb = new Codicon('light-bulb', { fontCharacter: '\\ea61' }); - export const repo = new Codicon('repo', { fontCharacter: '\\ea62' }); - export const repoDelete = new Codicon('repo-delete', { fontCharacter: '\\ea62' }); - export const gistFork = new Codicon('gist-fork', { fontCharacter: '\\ea63' }); - export const repoForked = new Codicon('repo-forked', { fontCharacter: '\\ea63' }); - export const gitPullRequest = new Codicon('git-pull-request', { fontCharacter: '\\ea64' }); - export const gitPullRequestAbandoned = new Codicon('git-pull-request-abandoned', { fontCharacter: '\\ea64' }); - export const recordKeys = new Codicon('record-keys', { fontCharacter: '\\ea65' }); - export const keyboard = new Codicon('keyboard', { fontCharacter: '\\ea65' }); - export const tag = new Codicon('tag', { fontCharacter: '\\ea66' }); - export const tagAdd = new Codicon('tag-add', { fontCharacter: '\\ea66' }); - export const tagRemove = new Codicon('tag-remove', { fontCharacter: '\\ea66' }); - export const person = new Codicon('person', { fontCharacter: '\\ea67' }); - export const personFollow = new Codicon('person-follow', { fontCharacter: '\\ea67' }); - export const personOutline = new Codicon('person-outline', { fontCharacter: '\\ea67' }); - export const personFilled = new Codicon('person-filled', { fontCharacter: '\\ea67' }); - export const gitBranch = new Codicon('git-branch', { fontCharacter: '\\ea68' }); - export const gitBranchCreate = new Codicon('git-branch-create', { fontCharacter: '\\ea68' }); - export const gitBranchDelete = new Codicon('git-branch-delete', { fontCharacter: '\\ea68' }); - export const sourceControl = new Codicon('source-control', { fontCharacter: '\\ea68' }); - export const mirror = new Codicon('mirror', { fontCharacter: '\\ea69' }); - export const mirrorPublic = new Codicon('mirror-public', { fontCharacter: '\\ea69' }); - export const star = new Codicon('star', { fontCharacter: '\\ea6a' }); - export const starAdd = new Codicon('star-add', { fontCharacter: '\\ea6a' }); - export const starDelete = new Codicon('star-delete', { fontCharacter: '\\ea6a' }); - export const starEmpty = new Codicon('star-empty', { fontCharacter: '\\ea6a' }); - export const comment = new Codicon('comment', { fontCharacter: '\\ea6b' }); - export const commentAdd = new Codicon('comment-add', { fontCharacter: '\\ea6b' }); - export const alert = new Codicon('alert', { fontCharacter: '\\ea6c' }); - export const warning = new Codicon('warning', { fontCharacter: '\\ea6c' }); - export const search = new Codicon('search', { fontCharacter: '\\ea6d' }); - export const searchSave = new Codicon('search-save', { fontCharacter: '\\ea6d' }); - export const logOut = new Codicon('log-out', { fontCharacter: '\\ea6e' }); - export const signOut = new Codicon('sign-out', { fontCharacter: '\\ea6e' }); - export const logIn = new Codicon('log-in', { fontCharacter: '\\ea6f' }); - export const signIn = new Codicon('sign-in', { fontCharacter: '\\ea6f' }); - export const eye = new Codicon('eye', { fontCharacter: '\\ea70' }); - export const eyeUnwatch = new Codicon('eye-unwatch', { fontCharacter: '\\ea70' }); - export const eyeWatch = new Codicon('eye-watch', { fontCharacter: '\\ea70' }); - export const circleFilled = new Codicon('circle-filled', { fontCharacter: '\\ea71' }); - export const primitiveDot = new Codicon('primitive-dot', { fontCharacter: '\\ea71' }); - export const closeDirty = new Codicon('close-dirty', { fontCharacter: '\\ea71' }); - export const debugBreakpoint = new Codicon('debug-breakpoint', { fontCharacter: '\\ea71' }); - export const debugBreakpointDisabled = new Codicon('debug-breakpoint-disabled', { fontCharacter: '\\ea71' }); - export const debugHint = new Codicon('debug-hint', { fontCharacter: '\\ea71' }); - export const primitiveSquare = new Codicon('primitive-square', { fontCharacter: '\\ea72' }); - export const edit = new Codicon('edit', { fontCharacter: '\\ea73' }); - export const pencil = new Codicon('pencil', { fontCharacter: '\\ea73' }); - export const info = new Codicon('info', { fontCharacter: '\\ea74' }); - export const issueOpened = new Codicon('issue-opened', { fontCharacter: '\\ea74' }); - export const gistPrivate = new Codicon('gist-private', { fontCharacter: '\\ea75' }); - export const gitForkPrivate = new Codicon('git-fork-private', { fontCharacter: '\\ea75' }); - export const lock = new Codicon('lock', { fontCharacter: '\\ea75' }); - export const mirrorPrivate = new Codicon('mirror-private', { fontCharacter: '\\ea75' }); - export const close = new Codicon('close', { fontCharacter: '\\ea76' }); - export const removeClose = new Codicon('remove-close', { fontCharacter: '\\ea76' }); - export const x = new Codicon('x', { fontCharacter: '\\ea76' }); - export const repoSync = new Codicon('repo-sync', { fontCharacter: '\\ea77' }); - export const sync = new Codicon('sync', { fontCharacter: '\\ea77' }); - export const clone = new Codicon('clone', { fontCharacter: '\\ea78' }); - export const desktopDownload = new Codicon('desktop-download', { fontCharacter: '\\ea78' }); - export const beaker = new Codicon('beaker', { fontCharacter: '\\ea79' }); - export const microscope = new Codicon('microscope', { fontCharacter: '\\ea79' }); - export const vm = new Codicon('vm', { fontCharacter: '\\ea7a' }); - export const deviceDesktop = new Codicon('device-desktop', { fontCharacter: '\\ea7a' }); - export const file = new Codicon('file', { fontCharacter: '\\ea7b' }); - export const fileText = new Codicon('file-text', { fontCharacter: '\\ea7b' }); - export const more = new Codicon('more', { fontCharacter: '\\ea7c' }); - export const ellipsis = new Codicon('ellipsis', { fontCharacter: '\\ea7c' }); - export const kebabHorizontal = new Codicon('kebab-horizontal', { fontCharacter: '\\ea7c' }); - export const mailReply = new Codicon('mail-reply', { fontCharacter: '\\ea7d' }); - export const reply = new Codicon('reply', { fontCharacter: '\\ea7d' }); - export const organization = new Codicon('organization', { fontCharacter: '\\ea7e' }); - export const organizationFilled = new Codicon('organization-filled', { fontCharacter: '\\ea7e' }); - export const organizationOutline = new Codicon('organization-outline', { fontCharacter: '\\ea7e' }); - export const newFile = new Codicon('new-file', { fontCharacter: '\\ea7f' }); - export const fileAdd = new Codicon('file-add', { fontCharacter: '\\ea7f' }); - export const newFolder = new Codicon('new-folder', { fontCharacter: '\\ea80' }); - export const fileDirectoryCreate = new Codicon('file-directory-create', { fontCharacter: '\\ea80' }); - export const trash = new Codicon('trash', { fontCharacter: '\\ea81' }); - export const trashcan = new Codicon('trashcan', { fontCharacter: '\\ea81' }); - export const history = new Codicon('history', { fontCharacter: '\\ea82' }); - export const clock = new Codicon('clock', { fontCharacter: '\\ea82' }); - export const folder = new Codicon('folder', { fontCharacter: '\\ea83' }); - export const fileDirectory = new Codicon('file-directory', { fontCharacter: '\\ea83' }); - export const symbolFolder = new Codicon('symbol-folder', { fontCharacter: '\\ea83' }); - export const logoGithub = new Codicon('logo-github', { fontCharacter: '\\ea84' }); - export const markGithub = new Codicon('mark-github', { fontCharacter: '\\ea84' }); - export const github = new Codicon('github', { fontCharacter: '\\ea84' }); - export const terminal = new Codicon('terminal', { fontCharacter: '\\ea85' }); - export const console = new Codicon('console', { fontCharacter: '\\ea85' }); - export const repl = new Codicon('repl', { fontCharacter: '\\ea85' }); - export const zap = new Codicon('zap', { fontCharacter: '\\ea86' }); - export const symbolEvent = new Codicon('symbol-event', { fontCharacter: '\\ea86' }); - export const error = new Codicon('error', { fontCharacter: '\\ea87' }); - export const stop = new Codicon('stop', { fontCharacter: '\\ea87' }); - export const variable = new Codicon('variable', { fontCharacter: '\\ea88' }); - export const symbolVariable = new Codicon('symbol-variable', { fontCharacter: '\\ea88' }); - export const array = new Codicon('array', { fontCharacter: '\\ea8a' }); - export const symbolArray = new Codicon('symbol-array', { fontCharacter: '\\ea8a' }); - export const symbolModule = new Codicon('symbol-module', { fontCharacter: '\\ea8b' }); - export const symbolPackage = new Codicon('symbol-package', { fontCharacter: '\\ea8b' }); - export const symbolNamespace = new Codicon('symbol-namespace', { fontCharacter: '\\ea8b' }); - export const symbolObject = new Codicon('symbol-object', { fontCharacter: '\\ea8b' }); - export const symbolMethod = new Codicon('symbol-method', { fontCharacter: '\\ea8c' }); - export const symbolFunction = new Codicon('symbol-function', { fontCharacter: '\\ea8c' }); - export const symbolConstructor = new Codicon('symbol-constructor', { fontCharacter: '\\ea8c' }); - export const symbolBoolean = new Codicon('symbol-boolean', { fontCharacter: '\\ea8f' }); - export const symbolNull = new Codicon('symbol-null', { fontCharacter: '\\ea8f' }); - export const symbolNumeric = new Codicon('symbol-numeric', { fontCharacter: '\\ea90' }); - export const symbolNumber = new Codicon('symbol-number', { fontCharacter: '\\ea90' }); - export const symbolStructure = new Codicon('symbol-structure', { fontCharacter: '\\ea91' }); - export const symbolStruct = new Codicon('symbol-struct', { fontCharacter: '\\ea91' }); - export const symbolParameter = new Codicon('symbol-parameter', { fontCharacter: '\\ea92' }); - export const symbolTypeParameter = new Codicon('symbol-type-parameter', { fontCharacter: '\\ea92' }); - export const symbolKey = new Codicon('symbol-key', { fontCharacter: '\\ea93' }); - export const symbolText = new Codicon('symbol-text', { fontCharacter: '\\ea93' }); - export const symbolReference = new Codicon('symbol-reference', { fontCharacter: '\\ea94' }); - export const goToFile = new Codicon('go-to-file', { fontCharacter: '\\ea94' }); - export const symbolEnum = new Codicon('symbol-enum', { fontCharacter: '\\ea95' }); - export const symbolValue = new Codicon('symbol-value', { fontCharacter: '\\ea95' }); - export const symbolRuler = new Codicon('symbol-ruler', { fontCharacter: '\\ea96' }); - export const symbolUnit = new Codicon('symbol-unit', { fontCharacter: '\\ea96' }); - export const activateBreakpoints = new Codicon('activate-breakpoints', { fontCharacter: '\\ea97' }); - export const archive = new Codicon('archive', { fontCharacter: '\\ea98' }); - export const arrowBoth = new Codicon('arrow-both', { fontCharacter: '\\ea99' }); - export const arrowDown = new Codicon('arrow-down', { fontCharacter: '\\ea9a' }); - export const arrowLeft = new Codicon('arrow-left', { fontCharacter: '\\ea9b' }); - export const arrowRight = new Codicon('arrow-right', { fontCharacter: '\\ea9c' }); - export const arrowSmallDown = new Codicon('arrow-small-down', { fontCharacter: '\\ea9d' }); - export const arrowSmallLeft = new Codicon('arrow-small-left', { fontCharacter: '\\ea9e' }); - export const arrowSmallRight = new Codicon('arrow-small-right', { fontCharacter: '\\ea9f' }); - export const arrowSmallUp = new Codicon('arrow-small-up', { fontCharacter: '\\eaa0' }); - export const arrowUp = new Codicon('arrow-up', { fontCharacter: '\\eaa1' }); - export const bell = new Codicon('bell', { fontCharacter: '\\eaa2' }); - export const bold = new Codicon('bold', { fontCharacter: '\\eaa3' }); - export const book = new Codicon('book', { fontCharacter: '\\eaa4' }); - export const bookmark = new Codicon('bookmark', { fontCharacter: '\\eaa5' }); - export const debugBreakpointConditionalUnverified = new Codicon('debug-breakpoint-conditional-unverified', { fontCharacter: '\\eaa6' }); - export const debugBreakpointConditional = new Codicon('debug-breakpoint-conditional', { fontCharacter: '\\eaa7' }); - export const debugBreakpointConditionalDisabled = new Codicon('debug-breakpoint-conditional-disabled', { fontCharacter: '\\eaa7' }); - export const debugBreakpointDataUnverified = new Codicon('debug-breakpoint-data-unverified', { fontCharacter: '\\eaa8' }); - export const debugBreakpointData = new Codicon('debug-breakpoint-data', { fontCharacter: '\\eaa9' }); - export const debugBreakpointDataDisabled = new Codicon('debug-breakpoint-data-disabled', { fontCharacter: '\\eaa9' }); - export const debugBreakpointLogUnverified = new Codicon('debug-breakpoint-log-unverified', { fontCharacter: '\\eaaa' }); - export const debugBreakpointLog = new Codicon('debug-breakpoint-log', { fontCharacter: '\\eaab' }); - export const debugBreakpointLogDisabled = new Codicon('debug-breakpoint-log-disabled', { fontCharacter: '\\eaab' }); - export const briefcase = new Codicon('briefcase', { fontCharacter: '\\eaac' }); - export const broadcast = new Codicon('broadcast', { fontCharacter: '\\eaad' }); - export const browser = new Codicon('browser', { fontCharacter: '\\eaae' }); - export const bug = new Codicon('bug', { fontCharacter: '\\eaaf' }); - export const calendar = new Codicon('calendar', { fontCharacter: '\\eab0' }); - export const caseSensitive = new Codicon('case-sensitive', { fontCharacter: '\\eab1' }); - export const check = new Codicon('check', { fontCharacter: '\\eab2' }); - export const checklist = new Codicon('checklist', { fontCharacter: '\\eab3' }); - export const chevronDown = new Codicon('chevron-down', { fontCharacter: '\\eab4' }); - export const dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition); - export const chevronLeft = new Codicon('chevron-left', { fontCharacter: '\\eab5' }); - export const chevronRight = new Codicon('chevron-right', { fontCharacter: '\\eab6' }); - export const chevronUp = new Codicon('chevron-up', { fontCharacter: '\\eab7' }); - export const chromeClose = new Codicon('chrome-close', { fontCharacter: '\\eab8' }); - export const chromeMaximize = new Codicon('chrome-maximize', { fontCharacter: '\\eab9' }); - export const chromeMinimize = new Codicon('chrome-minimize', { fontCharacter: '\\eaba' }); - export const chromeRestore = new Codicon('chrome-restore', { fontCharacter: '\\eabb' }); - export const circleOutline = new Codicon('circle-outline', { fontCharacter: '\\eabc' }); - export const debugBreakpointUnverified = new Codicon('debug-breakpoint-unverified', { fontCharacter: '\\eabc' }); - export const circleSlash = new Codicon('circle-slash', { fontCharacter: '\\eabd' }); - export const circuitBoard = new Codicon('circuit-board', { fontCharacter: '\\eabe' }); - export const clearAll = new Codicon('clear-all', { fontCharacter: '\\eabf' }); - export const clippy = new Codicon('clippy', { fontCharacter: '\\eac0' }); - export const closeAll = new Codicon('close-all', { fontCharacter: '\\eac1' }); - export const cloudDownload = new Codicon('cloud-download', { fontCharacter: '\\eac2' }); - export const cloudUpload = new Codicon('cloud-upload', { fontCharacter: '\\eac3' }); - export const code = new Codicon('code', { fontCharacter: '\\eac4' }); - export const collapseAll = new Codicon('collapse-all', { fontCharacter: '\\eac5' }); - export const colorMode = new Codicon('color-mode', { fontCharacter: '\\eac6' }); - export const commentDiscussion = new Codicon('comment-discussion', { fontCharacter: '\\eac7' }); - export const compareChanges = new Codicon('compare-changes', { fontCharacter: '\\eafd' }); - export const creditCard = new Codicon('credit-card', { fontCharacter: '\\eac9' }); - export const dash = new Codicon('dash', { fontCharacter: '\\eacc' }); - export const dashboard = new Codicon('dashboard', { fontCharacter: '\\eacd' }); - export const database = new Codicon('database', { fontCharacter: '\\eace' }); - export const debugContinue = new Codicon('debug-continue', { fontCharacter: '\\eacf' }); - export const debugDisconnect = new Codicon('debug-disconnect', { fontCharacter: '\\ead0' }); - export const debugPause = new Codicon('debug-pause', { fontCharacter: '\\ead1' }); - export const debugRestart = new Codicon('debug-restart', { fontCharacter: '\\ead2' }); - export const debugStart = new Codicon('debug-start', { fontCharacter: '\\ead3' }); - export const debugStepInto = new Codicon('debug-step-into', { fontCharacter: '\\ead4' }); - export const debugStepOut = new Codicon('debug-step-out', { fontCharacter: '\\ead5' }); - export const debugStepOver = new Codicon('debug-step-over', { fontCharacter: '\\ead6' }); - export const debugStop = new Codicon('debug-stop', { fontCharacter: '\\ead7' }); - export const debug = new Codicon('debug', { fontCharacter: '\\ead8' }); - export const deviceCameraVideo = new Codicon('device-camera-video', { fontCharacter: '\\ead9' }); - export const deviceCamera = new Codicon('device-camera', { fontCharacter: '\\eada' }); - export const deviceMobile = new Codicon('device-mobile', { fontCharacter: '\\eadb' }); - export const diffAdded = new Codicon('diff-added', { fontCharacter: '\\eadc' }); - export const diffIgnored = new Codicon('diff-ignored', { fontCharacter: '\\eadd' }); - export const diffModified = new Codicon('diff-modified', { fontCharacter: '\\eade' }); - export const diffRemoved = new Codicon('diff-removed', { fontCharacter: '\\eadf' }); - export const diffRenamed = new Codicon('diff-renamed', { fontCharacter: '\\eae0' }); - export const diff = new Codicon('diff', { fontCharacter: '\\eae1' }); - export const discard = new Codicon('discard', { fontCharacter: '\\eae2' }); - export const editorLayout = new Codicon('editor-layout', { fontCharacter: '\\eae3' }); - export const emptyWindow = new Codicon('empty-window', { fontCharacter: '\\eae4' }); - export const exclude = new Codicon('exclude', { fontCharacter: '\\eae5' }); - export const extensions = new Codicon('extensions', { fontCharacter: '\\eae6' }); - export const eyeClosed = new Codicon('eye-closed', { fontCharacter: '\\eae7' }); - export const fileBinary = new Codicon('file-binary', { fontCharacter: '\\eae8' }); - export const fileCode = new Codicon('file-code', { fontCharacter: '\\eae9' }); - export const fileMedia = new Codicon('file-media', { fontCharacter: '\\eaea' }); - export const filePdf = new Codicon('file-pdf', { fontCharacter: '\\eaeb' }); - export const fileSubmodule = new Codicon('file-submodule', { fontCharacter: '\\eaec' }); - export const fileSymlinkDirectory = new Codicon('file-symlink-directory', { fontCharacter: '\\eaed' }); - export const fileSymlinkFile = new Codicon('file-symlink-file', { fontCharacter: '\\eaee' }); - export const fileZip = new Codicon('file-zip', { fontCharacter: '\\eaef' }); - export const files = new Codicon('files', { fontCharacter: '\\eaf0' }); - export const filter = new Codicon('filter', { fontCharacter: '\\eaf1' }); - export const flame = new Codicon('flame', { fontCharacter: '\\eaf2' }); - export const foldDown = new Codicon('fold-down', { fontCharacter: '\\eaf3' }); - export const foldUp = new Codicon('fold-up', { fontCharacter: '\\eaf4' }); - export const fold = new Codicon('fold', { fontCharacter: '\\eaf5' }); - export const folderActive = new Codicon('folder-active', { fontCharacter: '\\eaf6' }); - export const folderOpened = new Codicon('folder-opened', { fontCharacter: '\\eaf7' }); - export const gear = new Codicon('gear', { fontCharacter: '\\eaf8' }); - export const gift = new Codicon('gift', { fontCharacter: '\\eaf9' }); - export const gistSecret = new Codicon('gist-secret', { fontCharacter: '\\eafa' }); - export const gist = new Codicon('gist', { fontCharacter: '\\eafb' }); - export const gitCommit = new Codicon('git-commit', { fontCharacter: '\\eafc' }); - export const gitCompare = new Codicon('git-compare', { fontCharacter: '\\eafd' }); - export const gitMerge = new Codicon('git-merge', { fontCharacter: '\\eafe' }); - export const githubAction = new Codicon('github-action', { fontCharacter: '\\eaff' }); - export const githubAlt = new Codicon('github-alt', { fontCharacter: '\\eb00' }); - export const globe = new Codicon('globe', { fontCharacter: '\\eb01' }); - export const grabber = new Codicon('grabber', { fontCharacter: '\\eb02' }); - export const graph = new Codicon('graph', { fontCharacter: '\\eb03' }); - export const gripper = new Codicon('gripper', { fontCharacter: '\\eb04' }); - export const heart = new Codicon('heart', { fontCharacter: '\\eb05' }); - export const home = new Codicon('home', { fontCharacter: '\\eb06' }); - export const horizontalRule = new Codicon('horizontal-rule', { fontCharacter: '\\eb07' }); - export const hubot = new Codicon('hubot', { fontCharacter: '\\eb08' }); - export const inbox = new Codicon('inbox', { fontCharacter: '\\eb09' }); - export const issueClosed = new Codicon('issue-closed', { fontCharacter: '\\eba4' }); - export const issueReopened = new Codicon('issue-reopened', { fontCharacter: '\\eb0b' }); - export const issues = new Codicon('issues', { fontCharacter: '\\eb0c' }); - export const italic = new Codicon('italic', { fontCharacter: '\\eb0d' }); - export const jersey = new Codicon('jersey', { fontCharacter: '\\eb0e' }); - export const json = new Codicon('json', { fontCharacter: '\\eb0f' }); - export const kebabVertical = new Codicon('kebab-vertical', { fontCharacter: '\\eb10' }); - export const key = new Codicon('key', { fontCharacter: '\\eb11' }); - export const law = new Codicon('law', { fontCharacter: '\\eb12' }); - export const lightbulbAutofix = new Codicon('lightbulb-autofix', { fontCharacter: '\\eb13' }); - export const linkExternal = new Codicon('link-external', { fontCharacter: '\\eb14' }); - export const link = new Codicon('link', { fontCharacter: '\\eb15' }); - export const listOrdered = new Codicon('list-ordered', { fontCharacter: '\\eb16' }); - export const listUnordered = new Codicon('list-unordered', { fontCharacter: '\\eb17' }); - export const liveShare = new Codicon('live-share', { fontCharacter: '\\eb18' }); - export const loading = new Codicon('loading', { fontCharacter: '\\eb19' }); - export const location = new Codicon('location', { fontCharacter: '\\eb1a' }); - export const mailRead = new Codicon('mail-read', { fontCharacter: '\\eb1b' }); - export const mail = new Codicon('mail', { fontCharacter: '\\eb1c' }); - export const markdown = new Codicon('markdown', { fontCharacter: '\\eb1d' }); - export const megaphone = new Codicon('megaphone', { fontCharacter: '\\eb1e' }); - export const mention = new Codicon('mention', { fontCharacter: '\\eb1f' }); - export const milestone = new Codicon('milestone', { fontCharacter: '\\eb20' }); - export const mortarBoard = new Codicon('mortar-board', { fontCharacter: '\\eb21' }); - export const move = new Codicon('move', { fontCharacter: '\\eb22' }); - export const multipleWindows = new Codicon('multiple-windows', { fontCharacter: '\\eb23' }); - export const mute = new Codicon('mute', { fontCharacter: '\\eb24' }); - export const noNewline = new Codicon('no-newline', { fontCharacter: '\\eb25' }); - export const note = new Codicon('note', { fontCharacter: '\\eb26' }); - export const octoface = new Codicon('octoface', { fontCharacter: '\\eb27' }); - export const openPreview = new Codicon('open-preview', { fontCharacter: '\\eb28' }); - export const package_ = new Codicon('package', { fontCharacter: '\\eb29' }); - export const paintcan = new Codicon('paintcan', { fontCharacter: '\\eb2a' }); - export const pin = new Codicon('pin', { fontCharacter: '\\eb2b' }); - export const play = new Codicon('play', { fontCharacter: '\\eb2c' }); - export const run = new Codicon('run', { fontCharacter: '\\eb2c' }); - export const plug = new Codicon('plug', { fontCharacter: '\\eb2d' }); - export const preserveCase = new Codicon('preserve-case', { fontCharacter: '\\eb2e' }); - export const preview = new Codicon('preview', { fontCharacter: '\\eb2f' }); - export const project = new Codicon('project', { fontCharacter: '\\eb30' }); - export const pulse = new Codicon('pulse', { fontCharacter: '\\eb31' }); - export const question = new Codicon('question', { fontCharacter: '\\eb32' }); - export const quote = new Codicon('quote', { fontCharacter: '\\eb33' }); - export const radioTower = new Codicon('radio-tower', { fontCharacter: '\\eb34' }); - export const reactions = new Codicon('reactions', { fontCharacter: '\\eb35' }); - export const references = new Codicon('references', { fontCharacter: '\\eb36' }); - export const refresh = new Codicon('refresh', { fontCharacter: '\\eb37' }); - export const regex = new Codicon('regex', { fontCharacter: '\\eb38' }); - export const remoteExplorer = new Codicon('remote-explorer', { fontCharacter: '\\eb39' }); - export const remote = new Codicon('remote', { fontCharacter: '\\eb3a' }); - export const remove = new Codicon('remove', { fontCharacter: '\\eb3b' }); - export const replaceAll = new Codicon('replace-all', { fontCharacter: '\\eb3c' }); - export const replace = new Codicon('replace', { fontCharacter: '\\eb3d' }); - export const repoClone = new Codicon('repo-clone', { fontCharacter: '\\eb3e' }); - export const repoForcePush = new Codicon('repo-force-push', { fontCharacter: '\\eb3f' }); - export const repoPull = new Codicon('repo-pull', { fontCharacter: '\\eb40' }); - export const repoPush = new Codicon('repo-push', { fontCharacter: '\\eb41' }); - export const report = new Codicon('report', { fontCharacter: '\\eb42' }); - export const requestChanges = new Codicon('request-changes', { fontCharacter: '\\eb43' }); - export const rocket = new Codicon('rocket', { fontCharacter: '\\eb44' }); - export const rootFolderOpened = new Codicon('root-folder-opened', { fontCharacter: '\\eb45' }); - export const rootFolder = new Codicon('root-folder', { fontCharacter: '\\eb46' }); - export const rss = new Codicon('rss', { fontCharacter: '\\eb47' }); - export const ruby = new Codicon('ruby', { fontCharacter: '\\eb48' }); - export const saveAll = new Codicon('save-all', { fontCharacter: '\\eb49' }); - export const saveAs = new Codicon('save-as', { fontCharacter: '\\eb4a' }); - export const save = new Codicon('save', { fontCharacter: '\\eb4b' }); - export const screenFull = new Codicon('screen-full', { fontCharacter: '\\eb4c' }); - export const screenNormal = new Codicon('screen-normal', { fontCharacter: '\\eb4d' }); - export const searchStop = new Codicon('search-stop', { fontCharacter: '\\eb4e' }); - export const server = new Codicon('server', { fontCharacter: '\\eb50' }); - export const settingsGear = new Codicon('settings-gear', { fontCharacter: '\\eb51' }); - export const settings = new Codicon('settings', { fontCharacter: '\\eb52' }); - export const shield = new Codicon('shield', { fontCharacter: '\\eb53' }); - export const smiley = new Codicon('smiley', { fontCharacter: '\\eb54' }); - export const sortPrecedence = new Codicon('sort-precedence', { fontCharacter: '\\eb55' }); - export const splitHorizontal = new Codicon('split-horizontal', { fontCharacter: '\\eb56' }); - export const splitVertical = new Codicon('split-vertical', { fontCharacter: '\\eb57' }); - export const squirrel = new Codicon('squirrel', { fontCharacter: '\\eb58' }); - export const starFull = new Codicon('star-full', { fontCharacter: '\\eb59' }); - export const starHalf = new Codicon('star-half', { fontCharacter: '\\eb5a' }); - export const symbolClass = new Codicon('symbol-class', { fontCharacter: '\\eb5b' }); - export const symbolColor = new Codicon('symbol-color', { fontCharacter: '\\eb5c' }); - export const symbolConstant = new Codicon('symbol-constant', { fontCharacter: '\\eb5d' }); - export const symbolEnumMember = new Codicon('symbol-enum-member', { fontCharacter: '\\eb5e' }); - export const symbolField = new Codicon('symbol-field', { fontCharacter: '\\eb5f' }); - export const symbolFile = new Codicon('symbol-file', { fontCharacter: '\\eb60' }); - export const symbolInterface = new Codicon('symbol-interface', { fontCharacter: '\\eb61' }); - export const symbolKeyword = new Codicon('symbol-keyword', { fontCharacter: '\\eb62' }); - export const symbolMisc = new Codicon('symbol-misc', { fontCharacter: '\\eb63' }); - export const symbolOperator = new Codicon('symbol-operator', { fontCharacter: '\\eb64' }); - export const symbolProperty = new Codicon('symbol-property', { fontCharacter: '\\eb65' }); - export const wrench = new Codicon('wrench', { fontCharacter: '\\eb65' }); - export const wrenchSubaction = new Codicon('wrench-subaction', { fontCharacter: '\\eb65' }); - export const symbolSnippet = new Codicon('symbol-snippet', { fontCharacter: '\\eb66' }); - export const tasklist = new Codicon('tasklist', { fontCharacter: '\\eb67' }); - export const telescope = new Codicon('telescope', { fontCharacter: '\\eb68' }); - export const textSize = new Codicon('text-size', { fontCharacter: '\\eb69' }); - export const threeBars = new Codicon('three-bars', { fontCharacter: '\\eb6a' }); - export const thumbsdown = new Codicon('thumbsdown', { fontCharacter: '\\eb6b' }); - export const thumbsup = new Codicon('thumbsup', { fontCharacter: '\\eb6c' }); - export const tools = new Codicon('tools', { fontCharacter: '\\eb6d' }); - export const triangleDown = new Codicon('triangle-down', { fontCharacter: '\\eb6e' }); - export const triangleLeft = new Codicon('triangle-left', { fontCharacter: '\\eb6f' }); - export const triangleRight = new Codicon('triangle-right', { fontCharacter: '\\eb70' }); - export const triangleUp = new Codicon('triangle-up', { fontCharacter: '\\eb71' }); - export const twitter = new Codicon('twitter', { fontCharacter: '\\eb72' }); - export const unfold = new Codicon('unfold', { fontCharacter: '\\eb73' }); - export const unlock = new Codicon('unlock', { fontCharacter: '\\eb74' }); - export const unmute = new Codicon('unmute', { fontCharacter: '\\eb75' }); - export const unverified = new Codicon('unverified', { fontCharacter: '\\eb76' }); - export const verified = new Codicon('verified', { fontCharacter: '\\eb77' }); - export const versions = new Codicon('versions', { fontCharacter: '\\eb78' }); - export const vmActive = new Codicon('vm-active', { fontCharacter: '\\eb79' }); - export const vmOutline = new Codicon('vm-outline', { fontCharacter: '\\eb7a' }); - export const vmRunning = new Codicon('vm-running', { fontCharacter: '\\eb7b' }); - export const watch = new Codicon('watch', { fontCharacter: '\\eb7c' }); - export const whitespace = new Codicon('whitespace', { fontCharacter: '\\eb7d' }); - export const wholeWord = new Codicon('whole-word', { fontCharacter: '\\eb7e' }); - export const window = new Codicon('window', { fontCharacter: '\\eb7f' }); - export const wordWrap = new Codicon('word-wrap', { fontCharacter: '\\eb80' }); - export const zoomIn = new Codicon('zoom-in', { fontCharacter: '\\eb81' }); - export const zoomOut = new Codicon('zoom-out', { fontCharacter: '\\eb82' }); - export const listFilter = new Codicon('list-filter', { fontCharacter: '\\eb83' }); - export const listFlat = new Codicon('list-flat', { fontCharacter: '\\eb84' }); - export const listSelection = new Codicon('list-selection', { fontCharacter: '\\eb85' }); - export const selection = new Codicon('selection', { fontCharacter: '\\eb85' }); - export const listTree = new Codicon('list-tree', { fontCharacter: '\\eb86' }); - export const debugBreakpointFunctionUnverified = new Codicon('debug-breakpoint-function-unverified', { fontCharacter: '\\eb87' }); - export const debugBreakpointFunction = new Codicon('debug-breakpoint-function', { fontCharacter: '\\eb88' }); - export const debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { fontCharacter: '\\eb88' }); - export const debugStackframeActive = new Codicon('debug-stackframe-active', { fontCharacter: '\\eb89' }); - export const debugStackframeDot = new Codicon('debug-stackframe-dot', { fontCharacter: '\\eb8a' }); - export const debugStackframe = new Codicon('debug-stackframe', { fontCharacter: '\\eb8b' }); - export const debugStackframeFocused = new Codicon('debug-stackframe-focused', { fontCharacter: '\\eb8b' }); - export const debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { fontCharacter: '\\eb8c' }); - export const symbolString = new Codicon('symbol-string', { fontCharacter: '\\eb8d' }); - export const debugReverseContinue = new Codicon('debug-reverse-continue', { fontCharacter: '\\eb8e' }); - export const debugStepBack = new Codicon('debug-step-back', { fontCharacter: '\\eb8f' }); - export const debugRestartFrame = new Codicon('debug-restart-frame', { fontCharacter: '\\eb90' }); - export const callIncoming = new Codicon('call-incoming', { fontCharacter: '\\eb92' }); - export const callOutgoing = new Codicon('call-outgoing', { fontCharacter: '\\eb93' }); - export const menu = new Codicon('menu', { fontCharacter: '\\eb94' }); - export const expandAll = new Codicon('expand-all', { fontCharacter: '\\eb95' }); - export const feedback = new Codicon('feedback', { fontCharacter: '\\eb96' }); - export const groupByRefType = new Codicon('group-by-ref-type', { fontCharacter: '\\eb97' }); - export const ungroupByRefType = new Codicon('ungroup-by-ref-type', { fontCharacter: '\\eb98' }); - export const account = new Codicon('account', { fontCharacter: '\\eb99' }); - export const bellDot = new Codicon('bell-dot', { fontCharacter: '\\eb9a' }); - export const debugConsole = new Codicon('debug-console', { fontCharacter: '\\eb9b' }); - export const library = new Codicon('library', { fontCharacter: '\\eb9c' }); - export const output = new Codicon('output', { fontCharacter: '\\eb9d' }); - export const runAll = new Codicon('run-all', { fontCharacter: '\\eb9e' }); - export const syncIgnored = new Codicon('sync-ignored', { fontCharacter: '\\eb9f' }); - export const pinned = new Codicon('pinned', { fontCharacter: '\\eba0' }); - export const githubInverted = new Codicon('github-inverted', { fontCharacter: '\\eba1' }); - export const debugAlt = new Codicon('debug-alt', { fontCharacter: '\\eb91' }); - export const serverProcess = new Codicon('server-process', { fontCharacter: '\\eba2' }); - export const serverEnvironment = new Codicon('server-environment', { fontCharacter: '\\eba3' }); - export const pass = new Codicon('pass', { fontCharacter: '\\eba4' }); - export const stopCircle = new Codicon('stop-circle', { fontCharacter: '\\eba5' }); - export const playCircle = new Codicon('play-circle', { fontCharacter: '\\eba6' }); - export const record = new Codicon('record', { fontCharacter: '\\eba7' }); - export const debugAltSmall = new Codicon('debug-alt-small', { fontCharacter: '\\eba8' }); - export const vmConnect = new Codicon('vm-connect', { fontCharacter: '\\eba9' }); - export const cloud = new Codicon('cloud', { fontCharacter: '\\ebaa' }); - export const merge = new Codicon('merge', { fontCharacter: '\\ebab' }); - export const exportIcon = new Codicon('export', { fontCharacter: '\\ebac' }); - export const graphLeft = new Codicon('graph-left', { fontCharacter: '\\ebad' }); - export const magnet = new Codicon('magnet', { fontCharacter: '\\ebae' }); - export const notebook = new Codicon('notebook', { fontCharacter: '\\ebaf' }); - export const redo = new Codicon('redo', { fontCharacter: '\\ebb0' }); - export const checkAll = new Codicon('check-all', { fontCharacter: '\\ebb1' }); - export const pinnedDirty = new Codicon('pinned-dirty', { fontCharacter: '\\ebb2' }); - export const passFilled = new Codicon('pass-filled', { fontCharacter: '\\ebb3' }); - export const circleLargeFilled = new Codicon('circle-large-filled', { fontCharacter: '\\ebb4' }); - export const circleLargeOutline = new Codicon('circle-large-outline', { fontCharacter: '\\ebb5' }); - export const combine = new Codicon('combine', { fontCharacter: '\\ebb6' }); - export const gather = new Codicon('gather', { fontCharacter: '\\ebb6' }); - export const table = new Codicon('table', { fontCharacter: '\\ebb7' }); - export const variableGroup = new Codicon('variable-group', { fontCharacter: '\\ebb8' }); - export const typeHierarchy = new Codicon('type-hierarchy', { fontCharacter: '\\ebb9' }); - export const typeHierarchySub = new Codicon('type-hierarchy-sub', { fontCharacter: '\\ebba' }); - export const typeHierarchySuper = new Codicon('type-hierarchy-super', { fontCharacter: '\\ebbb' }); - export const gitPullRequestCreate = new Codicon('git-pull-request-create', { fontCharacter: '\\ebbc' }); - export const runAbove = new Codicon('run-above', { fontCharacter: '\\ebbd' }); - export const runBelow = new Codicon('run-below', { fontCharacter: '\\ebbe' }); - export const notebookTemplate = new Codicon('notebook-template', { fontCharacter: '\\ebbf' }); - export const debugRerun = new Codicon('debug-rerun', { fontCharacter: '\\ebc0' }); - export const workspaceTrusted = new Codicon('workspace-trusted', { fontCharacter: '\\ebc1' }); - export const workspaceUntrusted = new Codicon('workspace-untrusted', { fontCharacter: '\\ebc2' }); - export const workspaceUnspecified = new Codicon('workspace-unspecified', { fontCharacter: '\\ebc3' }); - export const terminalCmd = new Codicon('terminal-cmd', { fontCharacter: '\\ebc4' }); - export const terminalDebian = new Codicon('terminal-debian', { fontCharacter: '\\ebc5' }); - export const terminalLinux = new Codicon('terminal-linux', { fontCharacter: '\\ebc6' }); - export const terminalPowershell = new Codicon('terminal-powershell', { fontCharacter: '\\ebc7' }); - export const terminalTmux = new Codicon('terminal-tmux', { fontCharacter: '\\ebc8' }); - export const terminalUbuntu = new Codicon('terminal-ubuntu', { fontCharacter: '\\ebc9' }); - export const terminalBash = new Codicon('terminal-bash', { fontCharacter: '\\ebca' }); - export const arrowSwap = new Codicon('arrow-swap', { fontCharacter: '\\ebcb' }); - export const copy = new Codicon('copy', { fontCharacter: '\\ebcc' }); - export const personAdd = new Codicon('person-add', { fontCharacter: '\\ebcd' }); - export const filterFilled = new Codicon('filter-filled', { fontCharacter: '\\ebce' }); - export const wand = new Codicon('wand', { fontCharacter: '\\ebcf' }); - export const debugLineByLine = new Codicon('debug-line-by-line', { fontCharacter: '\\ebd0' }); - export const inspect = new Codicon('inspect', { fontCharacter: '\\ebd1' }); - export const layers = new Codicon('layers', { fontCharacter: '\\ebd2' }); - export const layersDot = new Codicon('layers-dot', { fontCharacter: '\\ebd3' }); - export const layersActive = new Codicon('layers-active', { fontCharacter: '\\ebd4' }); - export const compass = new Codicon('compass', { fontCharacter: '\\ebd5' }); - export const compassDot = new Codicon('compass-dot', { fontCharacter: '\\ebd6' }); - export const compassActive = new Codicon('compass-active', { fontCharacter: '\\ebd7' }); - export const azure = new Codicon('azure', { fontCharacter: '\\ebd8' }); - export const issueDraft = new Codicon('issue-draft', { fontCharacter: '\\ebd9' }); - export const gitPullRequestClosed = new Codicon('git-pull-request-closed', { fontCharacter: '\\ebda' }); - export const gitPullRequestDraft = new Codicon('git-pull-request-draft', { fontCharacter: '\\ebdb' }); - export const debugAll = new Codicon('debug-all', { fontCharacter: '\\ebdc' }); - export const debugCoverage = new Codicon('debug-coverage', { fontCharacter: '\\ebdd' }); - export const runErrors = new Codicon('run-errors', { fontCharacter: '\\ebde' }); - export const folderLibrary = new Codicon('folder-library', { fontCharacter: '\\ebdf' }); - export const debugContinueSmall = new Codicon('debug-continue-small', { fontCharacter: '\\ebe0' }); - export const beakerStop = new Codicon('beaker-stop', { fontCharacter: '\\ebe1' }); - export const graphLine = new Codicon('graph-line', { fontCharacter: '\\ebe2' }); - export const graphScatter = new Codicon('graph-scatter', { fontCharacter: '\\ebe3' }); - export const pieChart = new Codicon('pie-chart', { fontCharacter: '\\ebe4' }); - export const bracket = new Codicon('bracket', Codicon.json.definition); - export const bracketDot = new Codicon('bracket-dot', { fontCharacter: '\\ebe5' }); - export const bracketError = new Codicon('bracket-error', { fontCharacter: '\\ebe6' }); - export const lockSmall = new Codicon('lock-small', { fontCharacter: '\\ebe7' }); - export const azureDevops = new Codicon('azure-devops', { fontCharacter: '\\ebe8' }); - export const verifiedFilled = new Codicon('verified-filled', { fontCharacter: '\\ebe9' }); - export const newLine = new Codicon('newline', { fontCharacter: '\\ebea' }); -} - diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index b9db31257f9..2e3e020856c 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -229,9 +229,9 @@ export function getCaseInsensitive(target: obj, key: string): any { export function filter(obj: obj, predicate: (key: string, value: any) => boolean): obj { const result = Object.create(null); - for (const key of Object.keys(obj)) { - if (predicate(key, obj[key])) { - result[key] = obj[key]; + for (const [key, value] of Object.entries(obj)) { + if (predicate(key, value)) { + result[key] = value; } } return result; diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 657b9c9dbaa..9e917c9e7ac 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -135,7 +135,9 @@ export interface IProductConfiguration { readonly extensionKind?: { readonly [extensionId: string]: ('ui' | 'workspace' | 'web')[]; }; readonly extensionPointExtensionKind?: { readonly [extensionPointId: string]: ('ui' | 'workspace' | 'web')[]; }; readonly extensionSyncedKeys?: { readonly [extensionId: string]: string[]; }; + /** @deprecated */ readonly extensionAllowedProposedApi?: readonly string[]; + readonly extensionEnabledApiProposals?: { readonly [extensionId: string]: string[] } readonly extensionUntrustedWorkspaceSupport?: { readonly [extensionId: string]: ExtensionUntrustedWorkspaceSupport }; readonly extensionVirtualWorkspacesSupport?: { readonly [extensionId: string]: ExtensionVirtualWorkspaceSupport }; diff --git a/src/vs/base/node/ports.ts b/src/vs/base/node/ports.ts index 6ecdc71019c..9d3a8bdcfe1 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -63,6 +63,90 @@ function doFindFreePort(startPort: number, giveUpAfter: number, stride: number, client.connect(startPort, '127.0.0.1'); } +// Reference: https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc#56 +export const BROWSER_RESTRICTED_PORTS: any = { + 1: true, // tcpmux + 7: true, // echo + 9: true, // discard + 11: true, // systat + 13: true, // daytime + 15: true, // netstat + 17: true, // qotd + 19: true, // chargen + 20: true, // ftp data + 21: true, // ftp access + 22: true, // ssh + 23: true, // telnet + 25: true, // smtp + 37: true, // time + 42: true, // name + 43: true, // nicname + 53: true, // domain + 69: true, // tftp + 77: true, // priv-rjs + 79: true, // finger + 87: true, // ttylink + 95: true, // supdup + 101: true, // hostriame + 102: true, // iso-tsap + 103: true, // gppitnp + 104: true, // acr-nema + 109: true, // pop2 + 110: true, // pop3 + 111: true, // sunrpc + 113: true, // auth + 115: true, // sftp + 117: true, // uucp-path + 119: true, // nntp + 123: true, // NTP + 135: true, // loc-srv /epmap + 137: true, // netbios + 139: true, // netbios + 143: true, // imap2 + 161: true, // snmp + 179: true, // BGP + 389: true, // ldap + 427: true, // SLP (Also used by Apple Filing Protocol) + 465: true, // smtp+ssl + 512: true, // print / exec + 513: true, // login + 514: true, // shell + 515: true, // printer + 526: true, // tempo + 530: true, // courier + 531: true, // chat + 532: true, // netnews + 540: true, // uucp + 548: true, // AFP (Apple Filing Protocol) + 554: true, // rtsp + 556: true, // remotefs + 563: true, // nntp+ssl + 587: true, // smtp (rfc6409) + 601: true, // syslog-conn (rfc3195) + 636: true, // ldap+ssl + 989: true, // ftps-data + 990: true, // ftps + 993: true, // ldap+ssl + 995: true, // pop3+ssl + 1719: true, // h323gatestat + 1720: true, // h323hostcall + 1723: true, // pptp + 2049: true, // nfs + 3659: true, // apple-sasl / PasswordServer + 4045: true, // lockd + 5060: true, // sip + 5061: true, // sips + 6000: true, // X11 + 6566: true, // sane-port + 6665: true, // Alternate IRC [Apple addition] + 6666: true, // Alternate IRC [Apple addition] + 6667: true, // Standard IRC [Apple addition] + 6668: true, // Alternate IRC [Apple addition] + 6669: true, // Alternate IRC [Apple addition] + 6697: true, // IRC + TLS + 10080: true // Amanda +}; + /** * Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener. */ diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index ed8ff0f3da1..6b84d0aa6c3 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -19,7 +19,7 @@ import { Action } from 'vs/base/common/actions'; import { equals } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -74,11 +74,8 @@ const $ = dom.$; type Writeable = { -readonly [P in keyof T]: T[P] }; - -const backButtonIcon = registerCodicon('quick-input-back', Codicon.arrowLeft); - const backButton = { - iconClass: backButtonIcon.classNames, + iconClass: Codicon.quickInputBack.classNames, tooltip: localize('quickInput.back', "Back"), handle: -1 // TODO }; diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index b29c1b46922..6de166b8234 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -10,7 +10,7 @@ suite('HighlightedLabel', () => { let label: HighlightedLabel; setup(() => { - label = new HighlightedLabel(document.createElement('div'), true); + label = new HighlightedLabel(document.createElement('div'), { supportIcons: true }); }); test('empty label', function () { diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 2e9cf651117..be85ad23c15 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -297,12 +297,12 @@ suite('Splitview', () => { assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled'); view1.maximumSize = 300; - assert.strictEqual(sashes[0].state, SashState.Minimum, 'first sash is enabled'); - assert.strictEqual(sashes[1].state, SashState.Minimum, 'second sash is enabled'); + assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled'); + assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled'); view2.maximumSize = 200; - assert.strictEqual(sashes[0].state, SashState.Minimum, 'first sash is enabled'); - assert.strictEqual(sashes[1].state, SashState.Minimum, 'second sash is enabled'); + assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled'); + assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled'); splitview.resizeView(0, 40); assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled'); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 0d8614131be..4baf8158a59 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -44,6 +44,8 @@ import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; import { resolveShellEnv } from 'vs/platform/environment/node/shellEnv'; import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService'; +import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter'; +import { WorkerMainProcessExtensionHostStarter } from 'vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter'; import { IExternalTerminalMainService } from 'vs/platform/externalTerminal/common/externalTerminal'; import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService'; import { IFileService } from 'vs/platform/files/common/files'; @@ -514,6 +516,9 @@ export class CodeApplication extends Disposable { // Extension URL Trust services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService)); + // Extension Host Starter + services.set(IExtensionHostStarter, new SyncDescriptor(WorkerMainProcessExtensionHostStarter)); + // Storage services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); @@ -640,6 +645,10 @@ export class CodeApplication extends Disposable { // Extension Host Debug Broadcasting const electronExtensionHostDebugBroadcastChannel = new ElectronExtensionHostDebugBroadcastChannel(accessor.get(IWindowsMainService)); mainProcessElectronServer.registerChannel('extensionhostdebugservice', electronExtensionHostDebugBroadcastChannel); + + // Extension Host Starter + const extensionHostStarterChannel = ProxyChannel.fromService(accessor.get(IExtensionHostStarter)); + mainProcessElectronServer.registerChannel(ipcExtensionHostStarterChannelName, extensionHostStarterChannel); } private openFirstWindow(accessor: ServicesAccessor, mainProcessElectronServer: ElectronIPCServer): ICodeWindow[] { diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 46e5dbab5c1..6340ed52eab 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -118,7 +118,7 @@ class CodeMain { }); // Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906) - bufferLogService.logger = new SpdLogLogger('main', join(environmentMainService.logsPath, 'main.log'), true, bufferLogService.getLevel()); + bufferLogService.logger = new SpdLogLogger('main', join(environmentMainService.logsPath, 'main.log'), true, false, bufferLogService.getLevel()); // Lifecycle once(lifecycleMainService.onWillShutdown)(evt => { diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 379072cae9f..fdee012d7ea 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -110,7 +110,7 @@ class CliMain extends Disposable { // Log const logLevel = getLogLevel(environmentService); const loggers: ILogger[] = []; - loggers.push(new SpdLogLogger('cli', join(environmentService.logsPath, 'cli.log'), true, logLevel)); + loggers.push(new SpdLogLogger('cli', join(environmentService.logsPath, 'cli.log'), true, false, logLevel)); if (logLevel === LogLevel.Trace) { loggers.push(new ConsoleLogger(logLevel)); } diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.ts b/src/vs/editor/browser/viewParts/decorations/decorations.ts index 9b9c9fc7e31..f4940ea54f9 100644 --- a/src/vs/editor/browser/viewParts/decorations/decorations.ts +++ b/src/vs/editor/browser/viewParts/decorations/decorations.ts @@ -202,9 +202,12 @@ export class DecorationsOverlay extends DynamicViewOverlay { if (showIfCollapsed && lineVisibleRanges.ranges.length === 1) { const singleVisibleRange = lineVisibleRanges.ranges[0]; - if (singleVisibleRange.width === 0) { - // collapsed range case => make the decoration visible by faking its width - lineVisibleRanges.ranges[0] = new HorizontalRange(singleVisibleRange.left, this._typicalHalfwidthCharacterWidth); + if (singleVisibleRange.width < this._typicalHalfwidthCharacterWidth) { + // collapsed/very small range case => make the decoration visible by expanding its width + // expand its size on both sides (both to the left and to the right, keeping it centered) + const center = Math.round(singleVisibleRange.left + singleVisibleRange.width / 2); + const left = Math.max(0, Math.round(center - this._typicalHalfwidthCharacterWidth / 2)); + lineVisibleRanges.ranges[0] = new HorizontalRange(left, this._typicalHalfwidthCharacterWidth); } } diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index b7025539667..3736489d8fc 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -13,8 +13,9 @@ import { INewScrollPosition, ScrollType } from 'vs/editor/common/editorCommon'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; export class EditorScrollbar extends ViewPart { @@ -180,3 +181,51 @@ export class EditorScrollbar extends ViewPart { this.scrollbar.renderNow(); } } + +registerThemingParticipant((theme, collector) => { + + // Scrollbars + const scrollbarShadowColor = theme.getColor(scrollbarShadow); + if (scrollbarShadowColor) { + collector.addRule(` + .monaco-scrollable-element > .shadow.top { + box-shadow: ${scrollbarShadowColor} 0 6px 6px -6px inset; + } + + .monaco-scrollable-element > .shadow.left { + box-shadow: ${scrollbarShadowColor} 6px 0 6px -6px inset; + } + + .monaco-scrollable-element > .shadow.top.left { + box-shadow: ${scrollbarShadowColor} 6px 6px 6px -6px inset; + } + `); + } + + const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground); + if (scrollbarSliderBackgroundColor) { + collector.addRule(` + .monaco-scrollable-element > .scrollbar > .slider { + background: ${scrollbarSliderBackgroundColor}; + } + `); + } + + const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); + if (scrollbarSliderHoverBackgroundColor) { + collector.addRule(` + .monaco-scrollable-element > .scrollbar > .slider:hover { + background: ${scrollbarSliderHoverBackgroundColor}; + } + `); + } + + const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); + if (scrollbarSliderActiveBackgroundColor) { + collector.addRule(` + .monaco-scrollable-element > .scrollbar > .slider.active { + background: ${scrollbarSliderActiveBackgroundColor}; + } + `); + } +}); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index fae61d04453..6eda6513b9f 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4373,7 +4373,7 @@ export const EditorOptions = { default: 0, minimum: 0, maximum: 100, - markdownDescription: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, the 90% of `#editor.fontSize#` is used.") + markdownDescription: nls.localize('codeLensFontSize', "Controls the font size in pixels for CodeLens. When set to `0`, 90% of `#editor.fontSize#` is used.") })), colorDecorators: register(new EditorBooleanOption( EditorOption.colorDecorators, 'colorDecorators', true, diff --git a/src/vs/editor/common/services/getSemanticTokens.ts b/src/vs/editor/common/services/getSemanticTokens.ts index 71c05f7252d..5d5026ff4c9 100644 --- a/src/vs/editor/common/services/getSemanticTokens.ts +++ b/src/vs/editor/common/services/getSemanticTokens.ts @@ -27,6 +27,7 @@ export class DocumentSemanticTokensResult { constructor( public readonly provider: DocumentSemanticTokensProvider, public readonly tokens: SemanticTokens | SemanticTokensEdits | null, + public readonly error: any ) { } } @@ -45,10 +46,11 @@ export async function getDocumentSemanticTokens(model: ITextModel, lastProvider: // Get tokens from all providers at the same time. const results = await Promise.all(providers.map(async (provider) => { let result: SemanticTokens | SemanticTokensEdits | null | undefined; + let error: any = null; try { result = await provider.provideDocumentSemanticTokens(model, (provider === lastProvider ? lastResultId : null), token); } catch (err) { - onUnexpectedExternalError(err); + error = err; result = null; } @@ -56,11 +58,15 @@ export async function getDocumentSemanticTokens(model: ITextModel, lastProvider: result = null; } - return new DocumentSemanticTokensResult(provider, result); + return new DocumentSemanticTokensResult(provider, result, error); })); - // Try to return the first result with actual tokens + // Try to return the first result with actual tokens or + // the first result which threw an error (!!) for (const result of results) { + if (result.error) { + throw result.error; + } if (result.tokens) { return result; } diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index d8615a93da0..3fba8e439f0 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -474,6 +474,11 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput } let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len); + if (input.renderControlCharacters && !input.isBasicASCII) { + // Calling `extractControlCharacters` before adding (possibly empty) line parts + // for inline decorations. `extractControlCharacters` removes empty line parts. + tokens = extractControlCharacters(lineContent, tokens); + } if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine) || @@ -500,9 +505,6 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput // We can never split RTL text, as it ruins the rendering tokens = splitLargeTokens(lineContent, tokens, !input.isBasicASCII || input.fontLigatures); } - if (input.renderControlCharacters && !input.isBasicASCII) { - tokens = extractControlCharacters(lineContent, tokens); - } return new ResolvedRenderLineInput( input.useMonospaceOptimizations, diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 7a46ce01ff1..b9d811d55f5 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -21,6 +21,7 @@ import { IRange } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; +import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { FoldingRangeKind, FoldingRangeProviderRegistry } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { CollapseMemento, FoldingModel, getNextFoldLine, getParentFoldLine as getParentFoldLine, getPreviousFoldLine, setCollapseStateAtLevel, setCollapseStateForMatchingLines, setCollapseStateForRest, setCollapseStateForType, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateUp, toggleCollapseState } from 'vs/editor/contrib/folding/foldingModel'; @@ -130,7 +131,7 @@ export class FoldingController extends Disposable implements IEditorContribution const options = this.editor.getOptions(); this.foldingDecorationProvider.autoHideFoldingControls = options.get(EditorOption.showFoldingControls) === 'mouseover'; this.foldingDecorationProvider.showFoldingHighlights = options.get(EditorOption.foldingHighlight); - this.onModelContentChanged(); + this.triggerFoldingModelChanged(); } if (e.hasChanged(EditorOption.foldingStrategy)) { this._useFoldingProviders = this.editor.getOptions().get(EditorOption.foldingStrategy) !== 'indentation'; @@ -225,7 +226,7 @@ export class FoldingController extends Disposable implements IEditorContribution this.localToDispose.add(this.cursorChangedScheduler); this.localToDispose.add(FoldingRangeProviderRegistry.onDidChange(() => this.onFoldingStrategyChanged())); this.localToDispose.add(this.editor.onDidChangeModelLanguageConfiguration(() => this.onFoldingStrategyChanged())); // covers model language changes as well - this.localToDispose.add(this.editor.onDidChangeModelContent(() => this.onModelContentChanged())); + this.localToDispose.add(this.editor.onDidChangeModelContent(e => this.onDidChangeModelContent(e))); this.localToDispose.add(this.editor.onDidChangeCursorPosition(() => this.onCursorPositionChanged())); this.localToDispose.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); this.localToDispose.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); @@ -250,7 +251,7 @@ export class FoldingController extends Disposable implements IEditorContribution this.rangeProvider = null; } }); - this.onModelContentChanged(); + this.triggerFoldingModelChanged(); } private onFoldingStrategyChanged() { @@ -258,7 +259,7 @@ export class FoldingController extends Disposable implements IEditorContribution this.rangeProvider.dispose(); } this.rangeProvider = null; - this.onModelContentChanged(); + this.triggerFoldingModelChanged(); } private getRangeProvider(editorModel: ITextModel): RangeProvider { @@ -278,7 +279,7 @@ export class FoldingController extends Disposable implements IEditorContribution }, 30000); return rangeProvider; // keep memento in case there are still no foldingProviders on the next request. } else if (foldingProviders.length > 0) { - this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.onModelContentChanged()); + this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.triggerFoldingModelChanged()); } } this.foldingStateMemento = null; @@ -289,7 +290,12 @@ export class FoldingController extends Disposable implements IEditorContribution return this.foldingModelPromise; } - private onModelContentChanged() { + private onDidChangeModelContent(e: IModelContentChangedEvent) { + this.hiddenRangeModel?.notifyChangeModelContent(e); + this.triggerFoldingModelChanged(); + } + + private triggerFoldingModelChanged() { if (this.updateScheduler) { if (this.foldingRegionPromise) { this.foldingRegionPromise.cancel(); diff --git a/src/vs/editor/contrib/folding/hiddenRangeModel.ts b/src/vs/editor/contrib/folding/hiddenRangeModel.ts index 6faaf3709a2..5d5d0698725 100644 --- a/src/vs/editor/contrib/folding/hiddenRangeModel.ts +++ b/src/vs/editor/contrib/folding/hiddenRangeModel.ts @@ -4,17 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { findFirstInSorted } from 'vs/base/common/arrays'; + import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { countEOL } from 'vs/editor/common/model/tokensStore'; import { CollapseMemento, FoldingModel } from 'vs/editor/contrib/folding/foldingModel'; export class HiddenRangeModel { + private readonly _foldingModel: FoldingModel; private _hiddenRanges: IRange[]; private _foldingModelListener: IDisposable | null; private readonly _updateEventEmitter = new Emitter(); + private _hasLineChanges: boolean = false; public get onDidChange(): Event { return this._updateEventEmitter.event; } public get hiddenRanges() { return this._hiddenRanges; } @@ -28,6 +33,14 @@ export class HiddenRangeModel { } } + public notifyChangeModelContent(e: IModelContentChangedEvent) { + if (this._hiddenRanges.length && !this._hasLineChanges) { + this._hasLineChanges = e.changes.some(change => { + return change.range.endLineNumber !== change.range.startLineNumber || countEOL(change.text)[0] !== 0; + }); + } + } + private updateHiddenRanges(): void { let updateHiddenAreas = false; let newHiddenAreas: IRange[] = []; @@ -61,7 +74,7 @@ export class HiddenRangeModel { lastCollapsedStart = startLineNumber; lastCollapsedEnd = endLineNumber; } - if (updateHiddenAreas || k < this._hiddenRanges.length) { + if (this._hasLineChanges || updateHiddenAreas || k < this._hiddenRanges.length) { this.applyHiddenRanges(newHiddenAreas); } } @@ -90,6 +103,7 @@ export class HiddenRangeModel { private applyHiddenRanges(newHiddenAreas: IRange[]) { this._hiddenRanges = newHiddenAreas; + this._hasLineChanges = false; this._updateEventEmitter.fire(newHiddenAreas); } diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts index a97fccc0d3c..5ded91884f0 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts @@ -167,7 +167,7 @@ class OneReferenceTemplate { readonly label: HighlightedLabel; constructor(container: HTMLElement) { - this.label = new HighlightedLabel(container, false); + this.label = new HighlightedLabel(container); } set(element: OneReference, score?: FuzzyScore): void { diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index a2eca47d536..8ed68d21cf9 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -105,9 +105,17 @@ class ModesContentComputer implements IHoverComputer { const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1; const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn; - if (startColumn > anchor.range.startColumn || anchor.range.endColumn > endColumn) { - return false; + if (d.options.showIfCollapsed) { + // Relax check around `showIfCollapsed` decorations to also include +/- 1 character + if (startColumn > anchor.range.startColumn + 1 || anchor.range.endColumn - 1 > endColumn) { + return false; + } + } else { + if (startColumn > anchor.range.startColumn || anchor.range.endColumn > endColumn) { + return false; + } } + return true; }); } diff --git a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts index 279c419a56c..129a4a9f58f 100644 --- a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsModel.ts @@ -11,7 +11,6 @@ import { Disposable, IDisposable, MutableDisposable, toDisposable } from 'vs/bas import { commonPrefixLength, commonSuffixLength } from 'vs/base/common/strings'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; @@ -43,8 +42,6 @@ export class InlineCompletionsModel extends Disposable implements GhostTextWidge this._register(commandService.onDidExecuteCommand(e => { // These commands don't trigger onDidType. const commands = new Set([ - UndoCommand.id, - RedoCommand.id, CoreEditingCommands.Tab.id, CoreEditingCommands.DeleteLeft.id, CoreEditingCommands.DeleteRight.id, diff --git a/src/vs/editor/contrib/inlineCompletions/suggestWidgetInlineCompletionProvider.ts b/src/vs/editor/contrib/inlineCompletions/suggestWidgetInlineCompletionProvider.ts index b55501d6f62..ceb8b3e7b03 100644 --- a/src/vs/editor/contrib/inlineCompletions/suggestWidgetInlineCompletionProvider.ts +++ b/src/vs/editor/contrib/inlineCompletions/suggestWidgetInlineCompletionProvider.ts @@ -190,13 +190,13 @@ export class SuggestWidgetInlineCompletionProvider extends Disposable { } } -function rangeStartsWith(rangeToTest: Range, prefix: Range): boolean { +export function rangeStartsWith(rangeToTest: Range, prefix: Range): boolean { return ( - rangeToTest.startLineNumber === prefix.startLineNumber && - rangeToTest.startColumn === prefix.startColumn && - (rangeToTest.endLineNumber < prefix.endLineNumber || - (rangeToTest.endLineNumber === prefix.endLineNumber && - rangeToTest.endColumn <= prefix.endColumn)) + prefix.startLineNumber === rangeToTest.startLineNumber && + prefix.startColumn === rangeToTest.startColumn && + (prefix.endLineNumber < rangeToTest.endLineNumber || + (prefix.endLineNumber === rangeToTest.endLineNumber && + prefix.endColumn <= rangeToTest.endColumn)) ); } diff --git a/src/vs/editor/contrib/inlineCompletions/test/suggestWidgetModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/suggestWidgetModel.test.ts index 16f869930ac..b45349df0be 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/suggestWidgetModel.test.ts @@ -32,8 +32,20 @@ import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { minimizeInlineCompletion } from 'vs/editor/contrib/inlineCompletions/inlineCompletionsModel'; +import { rangeStartsWith } from 'vs/editor/contrib/inlineCompletions/suggestWidgetInlineCompletionProvider'; suite('Suggest Widget Model', () => { + test('rangeStartsWith', () => { + assert.strictEqual(rangeStartsWith(new Range(1, 1, 10, 5), new Range(1, 1, 1, 1)), true); + assert.strictEqual(rangeStartsWith(new Range(1, 1, 10, 5), new Range(1, 1, 10, 5)), true); + assert.strictEqual(rangeStartsWith(new Range(1, 1, 10, 5), new Range(1, 1, 10, 4)), true); + assert.strictEqual(rangeStartsWith(new Range(1, 1, 10, 5), new Range(1, 1, 9, 6)), true); + + assert.strictEqual(rangeStartsWith(new Range(2, 1, 10, 5), new Range(1, 1, 10, 5)), false); + assert.strictEqual(rangeStartsWith(new Range(1, 1, 10, 5), new Range(1, 1, 10, 6)), false); + assert.strictEqual(rangeStartsWith(new Range(1, 1, 10, 5), new Range(1, 1, 11, 4)), false); + }); + test('Active', async () => { await withAsyncTestCodeEditorAndInlineCompletionsModel('', { fakeClock: true, provider, }, diff --git a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts index b5e9cad6a46..e12163c1035 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts @@ -363,7 +363,7 @@ export class SuggestDetailsOverlay implements IOverlayWidget { } placeAtAnchor(anchor: HTMLElement, preferAlignAtTop: boolean) { - const anchorBox = dom.getDomNodePagePosition(anchor); + const anchorBox = anchor.getBoundingClientRect(); this._anchorBox = anchorBox; this._preferAlignAtTop = preferAlignAtTop; this._placeAtAnchor(this._anchorBox, this._userSize ?? this.widget.size, preferAlignAtTop); diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 1d8403c185b..14b5bbb886a 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -49,8 +49,8 @@ export function getEncodedLanguageId(languageId: string): number { * @event */ export function onLanguage(languageId: string, callback: () => void): IDisposable { - let disposable = StaticServices.modeService.get().onDidEncounterLanguage((languageId) => { - if (languageId === languageId) { + let disposable = StaticServices.modeService.get().onDidEncounterLanguage((encounteredLanguageId) => { + if (encounteredLanguageId === languageId) { // stop listening disposable.dispose(); // invoke actual listener diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 779f91ebf31..9feb2e1bcb3 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -132,6 +132,19 @@ class StandaloneTheme implements IStandaloneTheme { encodedTokensColors = baseData.encodedTokensColors; } } + // Pick up default colors from `editor.foreground` and `editor.background` if available + const editorForeground = this.themeData.colors['editor.foreground']; + const editorBackground = this.themeData.colors['editor.background']; + if (editorForeground || editorBackground) { + const rule: ITokenThemeRule = { token: '' }; + if (editorForeground) { + rule.foreground = editorForeground; + } + if (editorBackground) { + rule.background = editorBackground; + } + rules.push(rule); + } rules = rules.concat(this.themeData.rules); if (this.themeData.encodedTokensColors) { encodedTokensColors = this.themeData.encodedTokensColors; diff --git a/src/vs/editor/test/common/services/getSemanticTokens.test.ts b/src/vs/editor/test/common/services/getSemanticTokens.test.ts new file mode 100644 index 00000000000..eaaf02019bf --- /dev/null +++ b/src/vs/editor/test/common/services/getSemanticTokens.test.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { CancellationToken } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ITextModel } from 'vs/editor/common/model'; +import { DocumentSemanticTokensProvider, DocumentSemanticTokensProviderRegistry, ProviderResult, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from 'vs/editor/common/modes'; +import { getDocumentSemanticTokens } from 'vs/editor/common/services/getSemanticTokens'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; + +suite('getSemanticTokens', () => { + + test('issue #136540: semantic highlighting flickers', async () => { + const disposables = new DisposableStore(); + + const provider = new class implements DocumentSemanticTokensProvider { + getLegend(): SemanticTokensLegend { + return { tokenTypes: ['test'], tokenModifiers: [] }; + } + provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): ProviderResult { + throw canceled(); + } + releaseDocumentSemanticTokens(resultId: string | undefined): void { + } + }; + + disposables.add(DocumentSemanticTokensProviderRegistry.register('testLang', provider)); + + const textModel = disposables.add(createTextModel('example', undefined, 'testLang')); + + await getDocumentSemanticTokens(textModel, null, null, CancellationToken.None).then((res) => { + assert.fail(); + }, (err) => { + assert.ok(!!err); + }); + + disposables.dispose(); + }); + +}); diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 371db99fa20..df65a7573f6 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -1722,6 +1722,46 @@ suite('viewLineRenderer.renderLine 2', () => { assert.deepStrictEqual(actual.html, expected); }); + test('issue #136622: Inline decorations are not rendering on non-ASCII lines when renderControlCharacters is on', () => { + + let actual = renderViewLine(new RenderLineInput( + true, + true, + 'some text £', + false, + false, + false, + 0, + createViewLineTokens([createPart(11, 3)]), + [ + new LineDecoration(5, 5, 'inlineDec1', InlineDecorationType.After), + new LineDecoration(6, 6, 'inlineDec2', InlineDecorationType.Before), + ], + 4, + 0, + 10, + 10, + 10, + 10000, + 'none', + true, + false, + null + )); + + let expected = [ + '', + 'some', + '', + '\u00a0', + '', + 'text\u00a0£', + '' + ].join(''); + + assert.deepStrictEqual(actual.html, expected); + }); + test('issue #22832: Consider fullwidth characters when rendering tabs', () => { let actual = renderViewLine(new RenderLineInput( diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 7e6b6e1dbf7..7097638b105 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -101,6 +101,7 @@ export class MenuId { static readonly ExplorerContext = new MenuId('ExplorerContext'); static readonly ExtensionContext = new MenuId('ExtensionContext'); static readonly GlobalActivity = new MenuId('GlobalActivity'); + static readonly LayoutControlMenu = new MenuId('LayoutControlMenu'); static readonly MenubarMainMenu = new MenuId('MenubarMainMenu'); static readonly MenubarAppearanceMenu = new MenuId('MenubarAppearanceMenu'); static readonly MenubarDebugMenu = new MenuId('MenubarDebugMenu'); diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index a5fed8d84cb..2b84d12e975 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -10,40 +10,6 @@ import { IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver } from 'vs/plat import localizedStrings from 'vs/platform/localizations/common/localizedStrings'; import type { Terminal } from 'xterm'; // eslint-disable-line code-import-patterns -function serializeElement(element: Element, recursive: boolean): IElement { - const attributes = Object.create(null); - - for (let j = 0; j < element.attributes.length; j++) { - const attr = element.attributes.item(j); - if (attr) { - attributes[attr.name] = attr.value; - } - } - - const children: IElement[] = []; - - if (recursive) { - for (let i = 0; i < element.children.length; i++) { - const child = element.children.item(i); - if (child) { - children.push(serializeElement(child, true)); - } - } - } - - const { left, top } = getTopLeftOffset(element as HTMLElement); - - return { - tagName: element.tagName, - className: element.className, - textContent: element.textContent || '', - attributes, - children, - left, - top - }; -} - export abstract class BaseWindowDriver implements IWindowDriver { abstract click(selector: string, xoffset?: number, yoffset?: number): Promise; @@ -95,12 +61,46 @@ export abstract class BaseWindowDriver implements IWindowDriver { for (let i = 0; i < query.length; i++) { const element = query.item(i); - result.push(serializeElement(element, recursive)); + result.push(this.serializeElement(element, recursive)); } return result; } + private serializeElement(element: Element, recursive: boolean): IElement { + const attributes = Object.create(null); + + for (let j = 0; j < element.attributes.length; j++) { + const attr = element.attributes.item(j); + if (attr) { + attributes[attr.name] = attr.value; + } + } + + const children: IElement[] = []; + + if (recursive) { + for (let i = 0; i < element.children.length; i++) { + const child = element.children.item(i); + if (child) { + children.push(this.serializeElement(child, true)); + } + } + } + + const { left, top } = getTopLeftOffset(element as HTMLElement); + + return { + tagName: element.tagName, + className: element.className, + textContent: element.textContent || '', + attributes, + children, + left, + top + }; + } + async getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }> { const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined; return this._getElementXY(selector, offset); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index a67a1dc44ea..326a4430832 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -106,4 +106,7 @@ export interface NativeParsedArgs { 'allow-insecure-localhost'?: boolean; 'log-net-log'?: string; 'vmodule'?: string; + + // MS Build command line arg + 'ms-enable-electron-run-as-node'?: boolean; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 7260f87c2c5..821cbf257f6 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -75,6 +75,7 @@ export const OPTIONS: OptionDescriptions> = { 'inspect-extensions': { type: 'string', deprecates: 'debugPluginHost', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, 'inspect-brk-extensions': { type: 'string', deprecates: 'debugBrkPluginHost', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") }, 'disable-gpu': { type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") }, + 'ms-enable-electron-run-as-node': { type: 'boolean' }, 'max-memory': { type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes)."), args: 'memory' }, 'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, diff --git a/src/vs/platform/environment/node/shellEnv.ts b/src/vs/platform/environment/node/shellEnv.ts index de217e36abe..a80d1f69863 100644 --- a/src/vs/platform/environment/node/shellEnv.ts +++ b/src/vs/platform/environment/node/shellEnv.ts @@ -127,13 +127,14 @@ async function doResolveUnixShellEnv(logService: ILogService, token: Cancellatio // handle popular non-POSIX shells const name = basename(systemShellUnix); let command: string, shellArgs: Array; + const extraArgs = (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : ''; if (/^pwsh(-preview)?$/.test(name)) { // Older versions of PowerShell removes double quotes sometimes so we use "double single quotes" which is how // you escape single quotes inside of a single quoted string. - command = `& '${process.execPath}' -p '''${mark}'' + JSON.stringify(process.env) + ''${mark}'''`; + command = `& '${process.execPath}' ${extraArgs} -p '''${mark}'' + JSON.stringify(process.env) + ''${mark}'''`; shellArgs = ['-Login', '-Command']; } else { - command = `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`; + command = `'${process.execPath}' ${extraArgs} -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`; if (name === 'tcsh') { shellArgs = ['-ic']; diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index cad22f2257f..473f7794e15 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -84,42 +84,13 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } async installFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise { - if (!this.galleryService.isEnabled()) { - throw new ExtensionManagementError(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal); - } - - if (!await this.canInstall(extension)) { - const targetPlatform = await this.getTargetPlatform(); - const error = new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.Incompatible); - this.logService.error(`Cannot install extension.`, extension.identifier.id, error.message); - reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); - throw error; - } - try { - extension = await this.checkAndGetCompatibleVersion(extension, !options.installGivenVersion); + return await this.doInstallFromGallery(extension, options); } catch (error) { - this.logService.error(getErrorMessage(error)); - reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); - throw error; - } - - const manifest = await this.galleryService.getManifest(extension, CancellationToken.None); - if (manifest === null) { - const error = new ExtensionManagementError(`Missing manifest for extension ${extension.identifier.id}`, ExtensionManagementErrorCode.Invalid); - this.logService.error(`Failed to install extension:`, extension.identifier.id, error.message); - reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); - throw error; + this.logService.error(`Failed to install extension.`, extension.identifier.id); + this.logService.error(error); + throw toExtensionManagementError(error); } - - if (manifest.version !== extension.version) { - const error = new ExtensionManagementError(`Cannot install '${extension.identifier.id}' extension because of version mismatch in Marketplace`, ExtensionManagementErrorCode.Invalid); - this.logService.error(error.message); - reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); - throw error; - } - - return this.installExtension(manifest, extension, options); } async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise { @@ -157,6 +128,41 @@ export abstract class AbstractExtensionManagementService extends Disposable impl this.participants.push(participant); } + private async doInstallFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise { + if (!this.galleryService.isEnabled()) { + throw new ExtensionManagementError(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal); + } + + if (!await this.canInstall(extension)) { + const targetPlatform = await this.getTargetPlatform(); + const error = new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.Incompatible); + reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); + throw error; + } + + try { + extension = await this.checkAndGetCompatibleVersion(extension, !options.installGivenVersion); + } catch (error) { + reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); + throw error; + } + + const manifest = await this.galleryService.getManifest(extension, CancellationToken.None); + if (manifest === null) { + const error = new ExtensionManagementError(`Missing manifest for extension ${extension.identifier.id}`, ExtensionManagementErrorCode.Invalid); + reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); + throw error; + } + + if (manifest.version !== extension.version) { + const error = new ExtensionManagementError(`Cannot install '${extension.identifier.id}' extension because of version mismatch in Marketplace`, ExtensionManagementErrorCode.Invalid); + reportTelemetry(this.telemetryService, 'extensionGallery:install', getGalleryExtensionTelemetryData(extension), undefined, error); + throw error; + } + + return this.installExtension(manifest, extension, options); + } + protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): Promise { // only cache gallery extensions tasks if (!URI.isUri(extension)) { @@ -209,7 +215,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } } else { this.logService.error('Error while preparing to install dependencies and extension packs of the extension:', installExtensionTask.identifier.id); - this.logService.error(error); throw error; } } @@ -253,7 +258,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl reportTelemetry(this.telemetryService, task.operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', getGalleryExtensionTelemetryData(task.source), new Date().getTime() - startTime, error); } this.logService.error('Error while installing the extension:', task.identifier.id); - this.logService.error(error); throw error; } finally { extensionsToInstallMap.delete(task.identifier.id.toLowerCase()); } })); @@ -287,12 +291,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } } - this.logService.error(`Failed to install extension:`, installExtensionTask.identifier.id, getErrorMessage(error)); this._onDidInstallExtensions.fire(allInstallExtensionTasks.map(({ task }) => ({ identifier: task.identifier, operation: InstallOperation.Install, source: task.source }))); - - if (error instanceof Error) { - error.name = error && (error).code ? (error).code : ExtensionManagementErrorCode.Internal; - } throw error; } finally { /* Remove the gallery tasks from the cache */ @@ -619,6 +618,15 @@ export function joinErrors(errorOrErrors: (Error | string) | (Array { + // this.killAllNow(); + // }); + + // Normal shutdown: gracefully await extension host shutdowns + lifecycleMainService.onWillShutdown((e) => { + e.join(this.waitForAllExit(6000)); + }); + } + +} diff --git a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts new file mode 100644 index 00000000000..f98915bfdb6 --- /dev/null +++ b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { canceled, SerializedError } from 'vs/base/common/errors'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; +import { Event } from 'vs/base/common/event'; +import { FileAccess } from 'vs/base/common/network'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Worker } from 'worker_threads'; +import { IWorker, IWorkerCallback, IWorkerFactory, SimpleWorkerClient } from 'vs/base/common/worker/simpleWorker'; +import { IExtensionHostStarterWorkerHost } from 'vs/platform/extensions/node/extensionHostStarterWorker'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { ExtensionHostStarter } from 'vs/platform/extensions/node/extensionHostStarter'; + +class NodeWorker implements IWorker { + + private readonly _worker: Worker; + + constructor(callback: IWorkerCallback, onErrorCallback: (err: any) => void) { + this._worker = new Worker( + FileAccess.asFileUri('vs/platform/extensions/node/extensionHostStarterWorkerMain.js', require).fsPath, + ); + this._worker.on('message', callback); + this._worker.on('error', onErrorCallback); + // this._worker.on('exit', (code) => { + // console.log(`worker exited with code `, code); + // }); + } + + getId(): number { + return 1; + } + + postMessage(message: any, transfer: ArrayBuffer[]): void { + this._worker.postMessage(message, transfer); + } + + dispose(): void { + this._worker.terminate(); + } +} + +class ExtensionHostStarterWorkerHost implements IExtensionHostStarterWorkerHost { + constructor( + @ILogService private readonly _logService: ILogService + ) { } + + public async logInfo(message: string): Promise { + this._logService.info(message); + } +} + +export class WorkerMainProcessExtensionHostStarter implements IDisposable, IExtensionHostStarter { + _serviceBrand: undefined; + + private _proxy: ExtensionHostStarter | null; + private readonly _worker: SimpleWorkerClient; + private _shutdown = false; + + constructor( + @ILogService private readonly _logService: ILogService, + @ILifecycleMainService lifecycleMainService: ILifecycleMainService + ) { + this._proxy = null; + + const workerFactory: IWorkerFactory = { + create: (moduleId: string, callback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker => { + const worker = new NodeWorker(callback, onErrorCallback); + worker.postMessage(moduleId, []); + return worker; + } + }; + this._worker = new SimpleWorkerClient( + workerFactory, + 'vs/platform/extensions/node/extensionHostStarterWorker', + new ExtensionHostStarterWorkerHost(this._logService) + ); + this._initialize(); + + // TODO: in the remote integration tests, this leads to the extension + // host processes getting killed brutally, which leads to the + // test resolver not having a chance to deactivate and kill the server processes + // it launches + + // // Abnormal shutdown: terminate extension hosts asap + // lifecycleMainService.onWillKill(async () => { + // this._shutdown = true; + // if (this._proxy) { + // this._proxy.killAllNow(); + // } + // }); + + // Normal shutdown: gracefully await extension host shutdowns + lifecycleMainService.onWillShutdown((e) => { + this._shutdown = true; + if (this._proxy) { + e.join(this._proxy.waitForAllExit(6000)); + } + }); + } + + dispose(): void { + // Intentionally not killing the extension host processes + } + + async _initialize(): Promise { + this._proxy = await this._worker.getProxyObject(); + this._logService.info(`ExtensionHostStarterWorker created`); + } + + onDynamicStdout(id: string): Event { + return this._proxy!.onDynamicStdout(id); + } + + onDynamicStderr(id: string): Event { + return this._proxy!.onDynamicStderr(id); + } + + onDynamicMessage(id: string): Event { + return this._proxy!.onDynamicMessage(id); + } + + onDynamicError(id: string): Event<{ error: SerializedError; }> { + return this._proxy!.onDynamicError(id); + } + + onDynamicExit(id: string): Event<{ code: number; signal: string; }> { + return this._proxy!.onDynamicExit(id); + } + + async createExtensionHost(): Promise<{ id: string; }> { + const proxy = await this._worker.getProxyObject(); + if (this._shutdown) { + throw canceled(); + } + return proxy.createExtensionHost(); + } + + async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number; }> { + const proxy = await this._worker.getProxyObject(); + if (this._shutdown) { + throw canceled(); + } + return proxy.start(id, opts); + } + + async enableInspectPort(id: string): Promise { + const proxy = await this._worker.getProxyObject(); + if (this._shutdown) { + throw canceled(); + } + return proxy.enableInspectPort(id); + } + + async kill(id: string): Promise { + const proxy = await this._worker.getProxyObject(); + if (this._shutdown) { + throw canceled(); + } + return proxy.kill(id); + } +} diff --git a/src/vs/platform/extensions/node/extensionHostStarter.ts b/src/vs/platform/extensions/node/extensionHostStarter.ts index 162b85cd85d..c71a39fb46d 100644 --- a/src/vs/platform/extensions/node/extensionHostStarter.ts +++ b/src/vs/platform/extensions/node/extensionHostStarter.ts @@ -15,6 +15,13 @@ import { ILogService } from 'vs/platform/log/common/log'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { mixin } from 'vs/base/common/objects'; import { cwd } from 'vs/base/common/process'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { Promises, timeout } from 'vs/base/common/async'; + +export interface IPartialLogService { + readonly _serviceBrand: undefined; + info(message: string): void; +} class ExtensionHostProcess extends Disposable { @@ -34,27 +41,26 @@ class ExtensionHostProcess extends Disposable { readonly onExit = this._onExit.event; private _process: ChildProcess | null = null; + private _hasExited: boolean = false; constructor( public readonly id: string, - @ILogService private readonly _logService: ILogService + @ILogService private readonly _logService: IPartialLogService ) { super(); } - register(disposable: IDisposable) { - this._register(disposable); - } - start(opts: IExtensionHostProcessOptions): { pid: number; } { + const sw = StopWatch.create(false); this._process = fork( FileAccess.asFileUri('bootstrap-fork', require).fsPath, ['--type=extensionHost', '--skipWorkspaceStorageLock'], mixin({ cwd: cwd() }, opts), ); + const forkTime = sw.elapsed(); const pid = this._process.pid; - this._logService.info(`Starting extension host with pid ${pid}.`); + this._logService.info(`Starting extension host with pid ${pid} (fork() took ${forkTime} ms).`); const stdoutDecoder = new StringDecoder('utf-8'); this._process.stdout?.on('data', (chunk) => { @@ -77,6 +83,7 @@ class ExtensionHostProcess extends Disposable { }); this._process.on('exit', (code: number, signal: string) => { + this._hasExited = true; this._onExit.fire({ pid, code, signal }); }); @@ -115,6 +122,21 @@ class ExtensionHostProcess extends Disposable { this._logService.info(`Killing extension host with pid ${this._process.pid}.`); this._process.kill(); } + + async waitForExit(maxWaitTimeMs: number): Promise { + if (!this._process) { + return; + } + const pid = this._process.pid; + this._logService.info(`Waiting for extension host with pid ${pid} to exit.`); + await Promise.race([Event.toPromise(this.onExit), timeout(maxWaitTimeMs)]); + + if (!this._hasExited) { + // looks like we timed out + this._logService.info(`Extension host with pid ${pid} did not exit within ${maxWaitTimeMs}ms.`); + this._process.kill(); + } + } } export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter { @@ -122,10 +144,10 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter private static _lastId: number = 0; - private readonly _extHosts: Map; + protected readonly _extHosts: Map; constructor( - @ILogService private readonly _logService: ILogService + @ILogService private readonly _logService: IPartialLogService ) { this._extHosts = new Map(); } @@ -196,6 +218,20 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter } extHostProcess.kill(); } + + async killAllNow(): Promise { + for (const [, extHost] of this._extHosts) { + extHost.kill(); + } + } + + async waitForAllExit(maxWaitTimeMs: number): Promise { + const exitPromises: Promise[] = []; + for (const [, extHost] of this._extHosts) { + exitPromises.push(extHost.waitForExit(maxWaitTimeMs)); + } + return Promises.settled(exitPromises).then(() => { }); + } } registerSingleton(IExtensionHostStarter, ExtensionHostStarter, true); diff --git a/src/vs/platform/extensions/node/extensionHostStarterWorker.ts b/src/vs/platform/extensions/node/extensionHostStarterWorker.ts new file mode 100644 index 00000000000..6bd329425ab --- /dev/null +++ b/src/vs/platform/extensions/node/extensionHostStarterWorker.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 { ExtensionHostStarter, IPartialLogService } from 'vs/platform/extensions/node/extensionHostStarter'; + +export interface IExtensionHostStarterWorkerHost { + logInfo(message: string): Promise; +} + +/** + * The `create` function needs to be there by convention because + * we are loaded via the `vs/base/common/worker/simpleWorker` utility. + */ +export function create(host: IExtensionHostStarterWorkerHost) { + const partialLogService: IPartialLogService = { + _serviceBrand: undefined, + info: (message: string): void => { + host.logInfo(message); + } + }; + return new ExtensionHostStarter(partialLogService); +} diff --git a/src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts b/src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts new file mode 100644 index 00000000000..b4efa0c798f --- /dev/null +++ b/src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +(function () { + 'use strict'; + + const loader = require('../../../loader'); + const bootstrap = require('../../../../bootstrap'); + const path = require('path'); + const parentPort = require('worker_threads').parentPort; + + // Bootstrap: NLS + const nlsConfig = bootstrap.setupNLS(); + + // Bootstrap: Loader + loader.config({ + baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../../'), { isWindows: process.platform === 'win32' }), + catchError: true, + nodeRequire: require, + nodeMain: __filename, + 'vs/nls': nlsConfig, + amdModulesPattern: /^vs\//, + recordStats: true + }); + + let isFirstMessage = true; + let beforeReadyMessages: any[] = []; + + const initialMessageHandler = (data: any) => { + if (!isFirstMessage) { + beforeReadyMessages.push(data); + return; + } + + isFirstMessage = false; + loadCode(data); + }; + + parentPort.on('message', initialMessageHandler); + + const loadCode = function (moduleId: string) { + loader([moduleId], function (ws: any) { + setTimeout(() => { + + const messageHandler = ws.create((msg: any, transfer?: ArrayBuffer[]) => { + parentPort.postMessage(msg, transfer); + }, null); + parentPort.off('message', initialMessageHandler); + parentPort.on('message', (data: any) => { + messageHandler.onmessage(data); + }); + while (beforeReadyMessages.length > 0) { + const msg = beforeReadyMessages.shift()!; + messageHandler.onmessage(msg); + } + + }); + }, (err: any) => console.error(err)); + }; + + parentPort.on('messageerror', (err: Error) => { + console.error(err); + }); +})(); diff --git a/src/vs/platform/files/common/watcher.ts b/src/vs/platform/files/common/watcher.ts index 4314712e66b..eb1d60df636 100644 --- a/src/vs/platform/files/common/watcher.ts +++ b/src/vs/platform/files/common/watcher.ts @@ -185,20 +185,20 @@ export function toFileChanges(changes: IDiskFileChange[]): IFileChange[] { })); } -export function normalizeFileChanges(changes: IDiskFileChange[]): IDiskFileChange[] { +export function coalesceEvents(changes: IDiskFileChange[]): IDiskFileChange[] { // Build deltas - const normalizer = new EventNormalizer(); + const coalescer = new EventCoalescer(); for (const event of changes) { - normalizer.processEvent(event); + coalescer.processEvent(event); } - return normalizer.normalize(); + return coalescer.coalesce(); } -class EventNormalizer { +class EventCoalescer { - private readonly normalized = new Set(); + private readonly coalesced = new Set(); private readonly mapPathToChange = new Map(); private toKey(event: IDiskFileChange): string { @@ -232,7 +232,7 @@ class EventNormalizer { // Ignore CREATE followed by DELETE in one go else if (currentChangeType === FileChangeType.ADDED && newChangeType === FileChangeType.DELETED) { this.mapPathToChange.delete(this.toKey(event)); - this.normalized.delete(existingEvent); + this.coalesced.delete(existingEvent); } // Flatten DELETE followed by CREATE into CHANGE @@ -255,12 +255,12 @@ class EventNormalizer { } if (keepEvent) { - this.normalized.add(event); + this.coalesced.add(event); this.mapPathToChange.set(this.toKey(event), event); } } - normalize(): IDiskFileChange[] { + coalesce(): IDiskFileChange[] { const addOrChangeEvents: IDiskFileChange[] = []; const deletedPaths: string[] = []; @@ -271,7 +271,7 @@ class EventNormalizer { // 1.) split ADD/CHANGE and DELETED events // 2.) sort short deleted paths to the top // 3.) for each DELETE, check if there is a deleted parent and ignore the event in that case - return Array.from(this.normalized).filter(e => { + return Array.from(this.coalesced).filter(e => { if (e.type !== FileChangeType.DELETED) { addOrChangeEvents.push(e); diff --git a/src/vs/platform/files/node/watcher/nodejs/watcherService.ts b/src/vs/platform/files/node/watcher/nodejs/watcherService.ts index 346cdb0b656..983b291e229 100644 --- a/src/vs/platform/files/node/watcher/nodejs/watcherService.ts +++ b/src/vs/platform/files/node/watcher/nodejs/watcherService.ts @@ -10,7 +10,7 @@ import { realpath } from 'vs/base/node/extpath'; import { SymlinkSupport } from 'vs/base/node/pfs'; import { CHANGE_BUFFER_DELAY, watchFile, watchFolder } from 'vs/base/node/watcher'; import { FileChangeType } from 'vs/platform/files/common/files'; -import { IDiskFileChange, ILogMessage, normalizeFileChanges } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, ILogMessage, coalesceEvents } from 'vs/platform/files/common/watcher'; export class FileWatcher extends Disposable { private isDisposed: boolean | undefined; @@ -95,19 +95,19 @@ export class FileWatcher extends Disposable { const fileChanges = this.fileChangesBuffer; this.fileChangesBuffer = []; - // Event normalization - const normalizedFileChanges = normalizeFileChanges(fileChanges); + // Event coalsecer + const coalescedFileChanges = coalesceEvents(fileChanges); // Logging if (this.verboseLogging) { - for (const event of normalizedFileChanges) { + for (const event of coalescedFileChanges) { this.onVerbose(`>> normalized ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`); } } // Fire - if (normalizedFileChanges.length > 0) { - this.onDidFilesChange(normalizedFileChanges); + if (coalescedFileChanges.length > 0) { + this.onDidFilesChange(coalescedFileChanges); } }); } diff --git a/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts b/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts index 0b7bd2a804a..7b415d8b902 100644 --- a/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts +++ b/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts @@ -18,7 +18,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { realcaseSync, realpathSync } from 'vs/base/node/extpath'; import { FileChangeType } from 'vs/platform/files/common/files'; import { IWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcher'; -import { IDiskFileChange, ILogMessage, normalizeFileChanges, IWatchRequest } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, ILogMessage, coalesceEvents, IWatchRequest } from 'vs/platform/files/common/watcher'; import { watchFolder } from 'vs/base/node/watcher'; interface IWatcher extends IDisposable { @@ -192,7 +192,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService { undeliveredFileEvents = []; // Broadcast to clients normalized - const normalizedEvents = normalizeFileChanges(this.normalizeEvents(undeliveredFileEventsToEmit, request, realBasePathDiffers, realBasePathLength)); + const normalizedEvents = coalesceEvents(this.normalizeEvents(undeliveredFileEventsToEmit, request, realBasePathDiffers, realBasePathLength)); this.emitEvents(normalizedEvents); }, this.getOptions(watcher)).then(async nsfwWatcher => { diff --git a/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts b/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts index a731395f51b..63a859ec83d 100644 --- a/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts +++ b/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts @@ -22,7 +22,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { realcaseSync, realpathSync } from 'vs/base/node/extpath'; import { watchFolder } from 'vs/base/node/watcher'; import { FileChangeType } from 'vs/platform/files/common/files'; -import { IDiskFileChange, ILogMessage, normalizeFileChanges, IWatchRequest, IWatcherService } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, ILogMessage, coalesceEvents, IWatchRequest, IWatcherService } from 'vs/platform/files/common/watcher'; export interface IWatcher { @@ -377,14 +377,19 @@ export class ParcelWatcherService extends Disposable implements IWatcherService // Check for excludes const rawEvents = this.handleExcludes(parcelEvents, excludes); - // Normalize and detect root path deletes + // Normalize events: handle NFC normalization and symlinks const { events: normalizedEvents, rootDeleted } = this.normalizeEvents(rawEvents, watcher.request, realPathDiffers, realPathLength); - // Broadcast to clients coalesced - const coalescedEvents = normalizeFileChanges(normalizedEvents); - this.emitEvents(coalescedEvents); + // Coalesce events: merge events of same kind + const coalescedEvents = coalesceEvents(normalizedEvents); - // Handle root path delete if confirmed from coalseced events + // Filter events: check for specific events we want to exclude + const filteredEvents = this.filterEvents(coalescedEvents, watcher.request, rootDeleted); + + // Broadcast to clients + this.emitEvents(filteredEvents); + + // Handle root path delete if confirmed from coalesced events if (rootDeleted && coalescedEvents.some(event => event.path === watcher.request.path && event.type === FileChangeType.DELETED)) { this.onWatchedPathDeleted(watcher); } @@ -485,6 +490,25 @@ export class ParcelWatcherService extends Disposable implements IWatcherService return { events, rootDeleted }; } + private filterEvents(events: IDiskFileChange[], request: IWatchRequest, rootDeleted: boolean): IDiskFileChange[] { + if (!rootDeleted) { + return events; + } + + return events.filter(event => { + if (event.path === request.path && event.type === FileChangeType.DELETED) { + // Explicitly exclude changes to root if we have any + // to avoid VS Code closing all opened editors which + // can happen e.g. in case of network connectivity + // issues + // (https://github.com/microsoft/vscode/issues/136673) + return false; + } + + return true; + }); + } + private onWatchedPathDeleted(watcher: IWatcher): void { this.warn('Watcher shutdown because watched path got deleted', watcher); @@ -502,9 +526,6 @@ export class ParcelWatcherService extends Disposable implements IWatcherService // Stop watching that parent folder disposable.dispose(); - // Send a manual event given we know the root got added again - this.emitEvents([{ path: watcher.request.path, type: FileChangeType.ADDED }]); - // Restart the file watching this.restartWatching(watcher); } diff --git a/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts b/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts index d5f1ba6bad5..9237d7b6351 100644 --- a/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts @@ -101,13 +101,13 @@ flakySuite('Recursive Watcher (parcel)', () => { } } - async function awaitEvent(service: TestParcelWatcherService, path: string, type: FileChangeType, failOnEventReason?: string): Promise { + function awaitEvent(service: TestParcelWatcherService, path: string, type: FileChangeType, failOnEventReason?: string): Promise { if (loggingEnabled) { console.log(`Awaiting change type '${toMsg(type)}' on file '${path}'`); } // Await the event - await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const disposable = service.onDidChangeFile(events => { for (const event of events) { if (event.path === path && event.type === type) { @@ -124,6 +124,22 @@ flakySuite('Recursive Watcher (parcel)', () => { }); } + function awaitMessage(service: TestParcelWatcherService, type: 'trace' | 'warn' | 'error' | 'info' | 'debug'): Promise { + if (loggingEnabled) { + console.log(`Awaiting message of type ${type}`); + } + + // Await the message + return new Promise(resolve => { + const disposable = service.onDidLogMessage(msg => { + if (msg.type === type) { + disposable.dispose(); + resolve(); + } + }); + }); + } + test('basics', async function () { await service.watch([{ path: testDir, excludes: [] }]); @@ -212,14 +228,27 @@ flakySuite('Recursive Watcher (parcel)', () => { await Promises.writeFile(copiedFilepath, 'Hello Change'); await changeFuture; + // Create new file + const anotherNewFilePath = join(testDir, 'deep', 'anotherNewFile.txt'); + changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.ADDED); + await Promises.writeFile(anotherNewFilePath, 'Hello Another World'); + await changeFuture; + + await timeout(1500); // ensure the previous added event is flushed by now (can happen on macOS with fsevents) + // Read file does not emit event - changeFuture = awaitEvent(service, copiedFilepath, FileChangeType.UPDATED, 'unexpected-event-from-read-file'); - await Promises.readFile(copiedFilepath); + changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-read-file'); + await Promises.readFile(anotherNewFilePath); await Promise.race([timeout(100), changeFuture]); // Stat file does not emit event - changeFuture = awaitEvent(service, copiedFilepath, FileChangeType.UPDATED, 'unexpected-event-from-stat'); - await Promises.stat(copiedFilepath); + changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-stat'); + await Promises.stat(anotherNewFilePath); + await Promise.race([timeout(100), changeFuture]); + + // Stat folder does not emit event + changeFuture = awaitEvent(service, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat'); + await Promises.stat(copiedFolderpath); await Promise.race([timeout(100), changeFuture]); // Delete file @@ -434,22 +463,19 @@ flakySuite('Recursive Watcher (parcel)', () => { await service.watch([{ path: watchedPath, excludes: [] }]); - // Delete watched path - let changeFuture: Promise = awaitEvent(service, watchedPath, FileChangeType.DELETED); + // Delete watched path and await + const warnFuture = awaitMessage(service, 'warn'); await Promises.rm(watchedPath, RimRafMode.UNLINK); - await changeFuture; + await warnFuture; // Restore watched path - changeFuture = awaitEvent(service, watchedPath, FileChangeType.ADDED); await Promises.mkdir(watchedPath); - await changeFuture; - - await timeout(20); // restart is delayed + await timeout(200); // restart is delayed await service.whenReady(); // Verify events come in again const newFilePath = join(watchedPath, 'newFile.txt'); - changeFuture = awaitEvent(service, newFilePath, FileChangeType.ADDED); + const changeFuture = awaitEvent(service, newFilePath, FileChangeType.ADDED); await Promises.writeFile(newFilePath, 'Hello World'); await changeFuture; }); diff --git a/src/vs/platform/files/test/node/watcherNormalizer.test.ts b/src/vs/platform/files/test/node/watcherCoalescer.test.ts similarity index 92% rename from src/vs/platform/files/test/node/watcherNormalizer.test.ts rename to src/vs/platform/files/test/node/watcherCoalescer.test.ts index 988be68c599..7853f6ce565 100644 --- a/src/vs/platform/files/test/node/watcherNormalizer.test.ts +++ b/src/vs/platform/files/test/node/watcherCoalescer.test.ts @@ -9,7 +9,7 @@ import { isLinux, isWindows } from 'vs/base/common/platform'; import { isEqual } from 'vs/base/common/resources'; import { URI as uri } from 'vs/base/common/uri'; import { FileChangesEvent, FileChangeType, IFileChange } from 'vs/platform/files/common/files'; -import { IDiskFileChange, normalizeFileChanges, toFileChanges } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, coalesceEvents, toFileChanges } from 'vs/platform/files/common/watcher'; class TestFileWatcher { private readonly _onDidFilesChange: Emitter<{ raw: IFileChange[], event: FileChangesEvent }>; @@ -28,12 +28,12 @@ class TestFileWatcher { private onRawFileEvents(events: IDiskFileChange[]): void { - // Normalize - let normalizedEvents = normalizeFileChanges(events); + // Coalesce + let coalescedEvents = coalesceEvents(events); // Emit through event emitter - if (normalizedEvents.length > 0) { - this._onDidFilesChange.fire({ raw: toFileChanges(normalizedEvents), event: this.toFileChangesEvent(normalizedEvents) }); + if (coalescedEvents.length > 0) { + this._onDidFilesChange.fire({ raw: toFileChanges(coalescedEvents), event: this.toFileChangesEvent(coalescedEvents) }); } } @@ -118,7 +118,7 @@ suite('Watcher Events Normalizer', () => { }); }); - test('event normalization: ignore CREATE followed by DELETE', done => { + test('event coalescer: ignore CREATE followed by DELETE', done => { const watch = new TestFileWatcher(); const created = uri.file('/users/data/src/related'); @@ -143,7 +143,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: flatten DELETE followed by CREATE into CHANGE', done => { + test('event coalescer: flatten DELETE followed by CREATE into CHANGE', done => { const watch = new TestFileWatcher(); const deleted = uri.file('/users/data/src/related'); @@ -169,7 +169,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: ignore UPDATE when CREATE received', done => { + test('event coalescer: ignore UPDATE when CREATE received', done => { const watch = new TestFileWatcher(); const created = uri.file('/users/data/src/related'); @@ -196,7 +196,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: apply DELETE', done => { + test('event coalescer: apply DELETE', done => { const watch = new TestFileWatcher(); const updated = uri.file('/users/data/src/related'); @@ -225,7 +225,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: track case renames', done => { + test('event coalescer: track case renames', done => { const watch = new TestFileWatcher(); const oldPath = uri.file('/users/data/src/added'); diff --git a/src/vs/platform/keyboardLayout/common/keyboardMapper.ts b/src/vs/platform/keyboardLayout/common/keyboardMapper.ts index 20203f46f0f..7382b329668 100644 --- a/src/vs/platform/keyboardLayout/common/keyboardMapper.ts +++ b/src/vs/platform/keyboardLayout/common/keyboardMapper.ts @@ -10,7 +10,7 @@ export interface IKeyboardMapper { dumpDebugInfo(): string; resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[]; resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding; - resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[]; + resolveUserBinding(parts: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[]; } export class CachedKeyboardMapper implements IKeyboardMapper { diff --git a/src/vs/platform/log/common/fileLog.ts b/src/vs/platform/log/common/fileLog.ts index 97e5badf0c5..0be7b474679 100644 --- a/src/vs/platform/log/common/fileLog.ts +++ b/src/vs/platform/log/common/fileLog.ts @@ -154,7 +154,7 @@ export class FileLoggerService extends AbstractLoggerService implements ILoggerS protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { const logger = new BufferLogService(logLevel); - whenProviderRegistered(resource, this.fileService).then(() => (logger).logger = this.instantiationService.createInstance(FileLogger, basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters)); + whenProviderRegistered(resource, this.fileService).then(() => (logger).logger = this.instantiationService.createInstance(FileLogger, options?.name || basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters)); return logger; } } diff --git a/src/vs/platform/log/node/loggerService.ts b/src/vs/platform/log/node/loggerService.ts index 00b5c188e10..8ad7ad94f48 100644 --- a/src/vs/platform/log/node/loggerService.ts +++ b/src/vs/platform/log/node/loggerService.ts @@ -23,11 +23,7 @@ export class LoggerService extends AbstractLoggerService implements ILoggerServi protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { if (resource.scheme === Schemas.file) { - const logger = new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, logLevel); - if (options?.donotUseFormatters) { - (logger).clearFormatters(); - } - return logger; + return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel); } else { return new FileLogger(options?.name ?? basename(resource), resource, logLevel, !!options?.donotUseFormatters, this.fileService); } diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index a9824d79e28..9c7bc2b69a0 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -7,12 +7,16 @@ import * as spdlog from 'spdlog'; import { ByteSize } from 'vs/platform/files/common/files'; import { AbstractMessageLogger, ILogger, LogLevel } from 'vs/platform/log/common/log'; -async function createSpdLogLogger(name: string, logfilePath: string, filesize: number, filecount: number): Promise { +async function createSpdLogLogger(name: string, logfilePath: string, filesize: number, filecount: number, donotUseFormatters: boolean): Promise { // Do not crash if spdlog cannot be loaded try { const _spdlog = await import('spdlog'); _spdlog.setFlushOn(LogLevel.Trace); - return _spdlog.createAsyncRotatingLogger(name, logfilePath, filesize, filecount); + const logger = await _spdlog.createAsyncRotatingLogger(name, logfilePath, filesize, filecount); + if (donotUseFormatters) { + logger.clearFormatters(); + } + return logger; } catch (e) { console.error(e); } @@ -49,14 +53,15 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger { private _logger: spdlog.Logger | undefined; constructor( - private readonly name: string, - private readonly filepath: string, - private readonly rotating: boolean, - level: LogLevel + name: string, + filepath: string, + rotating: boolean, + donotUseFormatters: boolean, + level: LogLevel, ) { super(); this.setLevel(level); - this._loggerCreationPromise = this._createSpdLogLogger(); + this._loggerCreationPromise = this._createSpdLogLogger(name, filepath, rotating, donotUseFormatters); this._register(this.onDidChangeLogLevel(level => { if (this._logger) { this._logger.setLevel(level); @@ -64,20 +69,18 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger { })); } - private _createSpdLogLogger(): Promise { - const filecount = this.rotating ? 6 : 1; + private async _createSpdLogLogger(name: string, filepath: string, rotating: boolean, donotUseFormatters: boolean): Promise { + const filecount = rotating ? 6 : 1; const filesize = (30 / filecount) * ByteSize.MB; - return createSpdLogLogger(this.name, this.filepath, filesize, filecount) - .then(logger => { - if (logger) { - this._logger = logger; - this._logger.setLevel(this.getLevel()); - for (const { level, message } of this.buffer) { - log(this._logger, level, message); - } - this.buffer = []; - } - }); + const logger = await createSpdLogLogger(name, filepath, filesize, filecount, donotUseFormatters); + if (logger) { + this._logger = logger; + this._logger.setLevel(this.getLevel()); + for (const { level, message } of this.buffer) { + log(this._logger, level, message); + } + this.buffer = []; + } } protected log(level: LogLevel, message: string): void { @@ -88,14 +91,6 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger { } } - clearFormatters(): void { - if (this._logger) { - this._logger.clearFormatters(); - } else { - this._loggerCreationPromise.then(() => this.clearFormatters()); - } - } - override flush(): void { if (this._logger) { this._logger.flush(); diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index da5cb11136c..37d19f0bcad 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -57,7 +57,7 @@ else { // Running out of sources if (Object.keys(product).length === 0) { Object.assign(product, { - version: '1.62.0-dev', + version: '1.63.0-dev', nameShort: 'Code - OSS Dev', nameLong: 'Code - OSS Dev', applicationName: 'code-oss', diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index af83a56d6b3..ecf80672720 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -24,6 +24,8 @@ export interface ResolvedOptions { export interface TunnelDescription { remoteAddress: { port: number, host: string }; localAddress: { port: number, host: string } | string; + privacy?: string; + protocol?: string; } export interface TunnelInformation { environmentTunnels?: TunnelDescription[]; diff --git a/src/vs/platform/remote/node/tunnelService.ts b/src/vs/platform/remote/node/tunnelService.ts index 2a6c75f4532..322b743d2c9 100644 --- a/src/vs/platform/remote/node/tunnelService.ts +++ b/src/vs/platform/remote/node/tunnelService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as net from 'net'; -import { findFreePortFaster } from 'vs/base/node/ports'; +import { BROWSER_RESTRICTED_PORTS, findFreePortFaster } from 'vs/base/node/ports'; import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; @@ -18,8 +18,18 @@ import { AbstractTunnelService, isAllInterfaces, ISharedTunnelsService as IShare import { ISignService } from 'vs/platform/sign/common/sign'; async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise { - const tunnel = new NodeRemoteTunnel(options, defaultTunnelHost, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort); - return tunnel.waitForReady(); + let readyTunnel: NodeRemoteTunnel | undefined; + for (let attempts = 3; attempts; attempts--) { + if (readyTunnel) { + readyTunnel.dispose(); + } + const tunnel = new NodeRemoteTunnel(options, defaultTunnelHost, tunnelRemoteHost, tunnelRemotePort, tunnelLocalPort); + readyTunnel = await tunnel.waitForReady(); + if ((tunnelLocalPort && BROWSER_RESTRICTED_PORTS[tunnelLocalPort]) || !BROWSER_RESTRICTED_PORTS[readyTunnel.tunnelLocalPort]) { + break; + } + } + return readyTunnel!; } class NodeRemoteTunnel extends Disposable implements RemoteTunnel { diff --git a/src/vs/platform/theme/common/iconRegistry.ts b/src/vs/platform/theme/common/iconRegistry.ts index 505b300927c..928e5151918 100644 --- a/src/vs/platform/theme/common/iconRegistry.ts +++ b/src/vs/platform/theme/common/iconRegistry.ts @@ -292,3 +292,4 @@ export const gotoPreviousLocation = registerIcon('goto-previous-location', Codic export const gotoNextLocation = registerIcon('goto-next-location', Codicons.Codicon.arrowDown, localize('nextChangeIcon', 'Icon for goto next editor location.')); export const syncing = ThemeIcon.modify(Codicons.Codicon.sync, 'spin'); +export const spinningLoading = ThemeIcon.modify(Codicons.Codicon.loading, 'spin'); diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 07ffaf39c47..ef3c013d23c 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -63,6 +63,14 @@ export namespace ThemeIcon { return { id }; } + export function getModifier(icon: ThemeIcon): string | undefined { + const tildeIndex = icon.id.lastIndexOf('~'); + if (tildeIndex !== -1) { + return icon.id.substring(tildeIndex + 1); + } + return undefined; + } + export function isEqual(ti1: ThemeIcon, ti2: ThemeIcon): boolean { return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id; } diff --git a/src/vs/server/remoteCli.ts b/src/vs/server/remoteCli.ts index fcc82d710ae..28dfc625974 100644 --- a/src/vs/server/remoteCli.ts +++ b/src/vs/server/remoteCli.ts @@ -236,6 +236,7 @@ export function main(desc: ProductDescription, args: string[]): void { } else { const cliCwd = dirname(cliCommand); const env = { ...process.env, ELECTRON_RUN_AS_NODE: '1' }; + newCommandline.unshift('--ms-enable-electron-run-as-node'); newCommandline.unshift('resources/app/out/cli.js'); if (parsedArgs['verbose']) { console.log(`Invoking: ${cliCommand} ${newCommandline.join(' ')} in ${cliCwd}`); diff --git a/src/vs/server/remoteExtensionHostAgentCli.ts b/src/vs/server/remoteExtensionHostAgentCli.ts index 61b0e8baec5..f6b66dffed6 100644 --- a/src/vs/server/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/remoteExtensionHostAgentCli.ts @@ -75,7 +75,7 @@ class CliMain extends Disposable { const environmentService = new ServerEnvironmentService(this.args, productService); services.set(IServerEnvironmentService, environmentService); - const logService: ILogService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, getLogLevel(environmentService))); + const logService: ILogService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, false, getLogLevel(environmentService))); services.set(ILogService, logService); logService.trace(`Remote configuration data at ${this.remoteDataFolder}`); logService.trace('process arguments:', this.args); diff --git a/src/vs/server/remoteExtensionHostAgentServer.ts b/src/vs/server/remoteExtensionHostAgentServer.ts index d755bca9589..a54bbbe1d82 100644 --- a/src/vs/server/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/remoteExtensionHostAgentServer.ts @@ -1043,7 +1043,7 @@ const getOrCreateSpdLogService: (environmentService: IServerEnvironmentService) let _logService: ILogService | null; return function getLogService(environmentService: IServerEnvironmentService): ILogService { if (!_logService) { - _logService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, getLogLevel(environmentService))); + _logService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, false, getLogLevel(environmentService))); } return _logService; }; diff --git a/src/vs/server/remoteTerminalChannel.ts b/src/vs/server/remoteTerminalChannel.ts index 2c31f0cd99e..5c47282ff63 100644 --- a/src/vs/server/remoteTerminalChannel.ts +++ b/src/vs/server/remoteTerminalChannel.ts @@ -15,7 +15,7 @@ import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { createRandomIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; import { ILogService } from 'vs/platform/log/common/log'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { IPtyService, IShellLaunchConfig, ITerminalProfile, ITerminalsLayoutInfo } from 'vs/platform/terminal/common/terminal'; +import { IPtyService, IShellLaunchConfig, ITerminalProfile } from 'vs/platform/terminal/common/terminal'; import { IGetTerminalLayoutInfoArgs, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { createRemoteURITransformer } from 'vs/server/remoteUriTransformer'; @@ -124,8 +124,8 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case '$getProfiles': return this._getProfiles.apply(this, args); case '$getEnvironment': return this._getEnvironment(); case '$getWslPath': return this._getWslPath(args[0]); - case '$getTerminalLayoutInfo': return this._getTerminalLayoutInfo(args); - case '$setTerminalLayoutInfo': return this._setTerminalLayoutInfo(args); + case '$getTerminalLayoutInfo': return this._ptyService.getTerminalLayoutInfo(args); + case '$setTerminalLayoutInfo': return this._ptyService.setTerminalLayoutInfo(args); case '$serializeTerminalState': return this._ptyService.serializeTerminalState.apply(this._ptyService, args); case '$reviveTerminalProcesses': return this._ptyService.reviveTerminalProcesses.apply(this._ptyService, args); case '$setUnicodeVersion': return this._ptyService.setUnicodeVersion.apply(this._ptyService, args); @@ -315,13 +315,6 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< return this._ptyService.getWslPath(original); } - private _setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): void { - this._ptyService.setTerminalLayoutInfo(args); - } - - private async _getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise { - return this._ptyService.getTerminalLayoutInfo(args); - } private _reduceConnectionGraceTime(): Promise { return this._ptyService.reduceConnectionGraceTime(); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts deleted file mode 100644 index f5f1304f3d9..00000000000 --- a/src/vs/vscode.proposed.d.ts +++ /dev/null @@ -1,2847 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * This is the place for API experiments and proposals. - * These API are NOT stable and subject to change. They are only available in the Insiders - * distribution and CANNOT be used in published extensions. - * - * To test these API in local environment: - * - Use Insiders release of 'VS Code'. - * - Add `"enableProposedApi": true` to your package.json. - * - Copy this file to your project. - */ - -declare module 'vscode' { - - // eslint-disable-next-line vscode-dts-region-comments - //#region @alexdima - resolvers - - export interface MessageOptions { - /** - * Do not render a native message box. - */ - useCustom?: boolean; - } - - export interface RemoteAuthorityResolverContext { - resolveAttempt: number; - } - - export class ResolvedAuthority { - readonly host: string; - readonly port: number; - readonly connectionToken: string | undefined; - - constructor(host: string, port: number, connectionToken?: string); - } - - export interface ResolvedOptions { - extensionHostEnv?: { [key: string]: string | null; }; - - isTrusted?: boolean; - } - - export interface TunnelPrivacy { - themeIcon: string; - id: string; - label: string; - } - - export interface TunnelOptions { - remoteAddress: { port: number, host: string; }; - // The desired local port. If this port can't be used, then another will be chosen. - localAddressPort?: number; - label?: string; - /** - * @deprecated Use privacy instead - */ - public?: boolean; - privacy?: string; - protocol?: string; - } - - export interface TunnelDescription { - remoteAddress: { port: number, host: string; }; - //The complete local address(ex. localhost:1234) - localAddress: { port: number, host: string; } | string; - /** - * @deprecated Use privacy instead - */ - public?: boolean; - privacy?: string; - // If protocol is not provided it is assumed to be http, regardless of the localAddress. - protocol?: string; - } - - export interface Tunnel extends TunnelDescription { - // Implementers of Tunnel should fire onDidDispose when dispose is called. - onDidDispose: Event; - dispose(): void | Thenable; - } - - /** - * Used as part of the ResolverResult if the extension has any candidate, - * published, or forwarded ports. - */ - export interface TunnelInformation { - /** - * Tunnels that are detected by the extension. The remotePort is used for display purposes. - * The localAddress should be the complete local address (ex. localhost:1234) for connecting to the port. Tunnels provided through - * detected are read-only from the forwarded ports UI. - */ - environmentTunnels?: TunnelDescription[]; - - } - - export interface TunnelCreationOptions { - /** - * True when the local operating system will require elevation to use the requested local port. - */ - elevationRequired?: boolean; - } - - export enum CandidatePortSource { - None = 0, - Process = 1, - Output = 2 - } - - export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; - - export class RemoteAuthorityResolverError extends Error { - static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError; - static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError; - - constructor(message?: string); - } - - export interface RemoteAuthorityResolver { - /** - * Resolve the authority part of the current opened `vscode-remote://` URI. - * - * This method will be invoked once during the startup of the editor and again each time - * the editor detects a disconnection. - * - * @param authority The authority part of the current opened `vscode-remote://` URI. - * @param context A context indicating if this is the first call or a subsequent call. - */ - resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable; - - /** - * Get the canonical URI (if applicable) for a `vscode-remote://` URI. - * - * @returns The canonical URI or undefined if the uri is already canonical. - */ - getCanonicalURI?(uri: Uri): ProviderResult; - - /** - * Can be optionally implemented if the extension can forward ports better than the core. - * When not implemented, the core will use its default forwarding logic. - * When implemented, the core will use this to forward ports. - * - * To enable the "Change Local Port" action on forwarded ports, make sure to set the `localAddress` of - * the returned `Tunnel` to a `{ port: number, host: string; }` and not a string. - */ - tunnelFactory?: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined; - - /**p - * Provides filtering for candidate ports. - */ - showCandidatePort?: (host: string, port: number, detail: string) => Thenable; - - /** - * Lets the resolver declare which tunnel factory features it supports. - * UNDER DISCUSSION! MAY CHANGE SOON. - */ - tunnelFeatures?: { - elevation: boolean; - /** - * @deprecated Use privacy instead - */ - public: boolean; - /** - * One of the the options must have the ID "private". - */ - privacyOptions: TunnelPrivacy[]; - }; - - candidatePortSource?: CandidatePortSource; - } - - /** - * More options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. - */ - export interface AuthenticationGetSessionOptions { - /** - * Whether we should attempt to reauthenticate even if there is already a session available. - * - * If true, a modal dialog will be shown asking the user to sign in again. This is mostly used for scenarios - * where the token needs to be re minted because it has lost some authorization. - * - * Defaults to false. - */ - forceNewSession?: boolean | { detail: string }; - /** - * Whether we should show the indication to sign in in the Accounts menu. - * - * If false, the user will be shown a badge on the Accounts menu with an option to sign in for the extension. - * If true, no indication will be shown. - * - * Defaults to false. - */ - silent?: boolean; - } - - export namespace authentication { - /** - * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not - * registered, or if the user does not consent to sharing authentication information with - * the extension. If there are multiple sessions with the same scopes, the user will be shown a - * quickpick to select which account they would like to use. - * - * Currently, there are only two authentication providers that are contributed from built in extensions - * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. - * @param providerId The id of the provider to use - * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider - * @param options The {@link AuthenticationGetSessionOptions} to use - * @returns A thenable that resolves to an authentication session - */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | { detail: string } }): Thenable; - export function hasSession(providerId: string, scopes: readonly string[]): Thenable; - } - - export namespace workspace { - /** - * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. - * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips. - * - * @throws When run in an environment without a remote. - * - * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. - */ - export function openTunnel(tunnelOptions: TunnelOptions): Thenable; - - /** - * Gets an array of the currently available tunnels. This does not include environment tunnels, only tunnels that have been created by the user. - * Note that these are of type TunnelDescription and cannot be disposed. - */ - export let tunnels: Thenable; - - /** - * Fired when the list of tunnels has changed. - */ - export const onDidChangeTunnels: Event; - } - - export interface ResourceLabelFormatter { - scheme: string; - authority?: string; - formatting: ResourceLabelFormatting; - } - - export interface ResourceLabelFormatting { - label: string; // myLabel:/${path} - // For historic reasons we use an or string here. Once we finalize this API we should start using enums instead and adopt it in extensions. - // eslint-disable-next-line vscode-dts-literal-or-types - separator: '/' | '\\' | ''; - tildify?: boolean; - normalizeDriveLetter?: boolean; - workspaceSuffix?: string; - workspaceTooltip?: string; - authorityPrefix?: string; - stripPathStartingSeparator?: boolean; - } - - export namespace workspace { - export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; - export function registerResourceLabelFormatter(formatter: ResourceLabelFormatter): Disposable; - } - - export namespace env { - - /** - * The authority part of the current opened `vscode-remote://` URI. - * Defined by extensions, e.g. `ssh-remote+${host}` for remotes using a secure shell. - * - * *Note* that the value is `undefined` when there is no remote extension host but that the - * value is defined in all extension hosts (local and remote) in case a remote extension host - * exists. Use {@link Extension.extensionKind} to know if - * a specific extension runs remote or not. - */ - export const remoteAuthority: string | undefined; - - } - - //#endregion - - //#region editor insets: https://github.com/microsoft/vscode/issues/85682 - - export interface WebviewEditorInset { - readonly editor: TextEditor; - readonly line: number; - readonly height: number; - readonly webview: Webview; - readonly onDidDispose: Event; - dispose(): void; - } - - export namespace window { - export function createWebviewTextEditorInset(editor: TextEditor, line: number, height: number, options?: WebviewOptions): WebviewEditorInset; - } - - //#endregion - - //#region read/write in chunks: https://github.com/microsoft/vscode/issues/84515 - - export interface FileSystemProvider { - open?(resource: Uri, options: { create: boolean; }): number | Thenable; - close?(fd: number): void | Thenable; - read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - } - - //#endregion - - //#region TextSearchProvider: https://github.com/microsoft/vscode/issues/59921 - - /** - * The parameters of a query for text search. - */ - export interface TextSearchQuery { - /** - * The text pattern to search for. - */ - pattern: string; - - /** - * Whether or not `pattern` should match multiple lines of text. - */ - isMultiline?: boolean; - - /** - * Whether or not `pattern` should be interpreted as a regular expression. - */ - isRegExp?: boolean; - - /** - * Whether or not the search should be case-sensitive. - */ - isCaseSensitive?: boolean; - - /** - * Whether or not to search for whole word matches only. - */ - isWordMatch?: boolean; - } - - /** - * A file glob pattern to match file paths against. - * TODO@roblourens merge this with the GlobPattern docs/definition in vscode.d.ts. - * @see {@link GlobPattern} - */ - export type GlobString = string; - - /** - * Options common to file and text search - */ - export interface SearchOptions { - /** - * The root folder to search within. - */ - folder: Uri; - - /** - * Files that match an `includes` glob pattern should be included in the search. - */ - includes: GlobString[]; - - /** - * Files that match an `excludes` glob pattern should be excluded from the search. - */ - excludes: GlobString[]; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles: boolean; - } - - /** - * Options to specify the size of the result text preview. - * These options don't affect the size of the match itself, just the amount of preview text. - */ - export interface TextSearchPreviewOptions { - /** - * The maximum number of lines in the preview. - * Only search providers that support multiline search will ever return more than one line in the match. - */ - matchLines: number; - - /** - * The maximum number of characters included per line. - */ - charsPerLine: number; - } - - /** - * Options that apply to text search. - */ - export interface TextSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults: number; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Exclude files larger than `maxFileSize` in bytes. - */ - maxFileSize?: number; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - /** - * Represents the severiry of a TextSearchComplete message. - */ - export enum TextSearchCompleteMessageType { - Information = 1, - Warning = 2, - } - - /** - * A message regarding a completed search. - */ - export interface TextSearchCompleteMessage { - /** - * Markdown text of the message. - */ - text: string, - /** - * Whether the source of the message is trusted, command links are disabled for untrusted message sources. - * Messaged are untrusted by default. - */ - trusted?: boolean, - /** - * The message type, this affects how the message will be rendered. - */ - type: TextSearchCompleteMessageType, - } - - /** - * Information collected when text search is complete. - */ - export interface TextSearchComplete { - /** - * Whether the search hit the limit on the maximum number of search results. - * `maxResults` on {@linkcode TextSearchOptions} specifies the max number of results. - * - If exactly that number of matches exist, this should be false. - * - If `maxResults` matches are returned and more exist, this should be true. - * - If search hits an internal limit which is less than `maxResults`, this should be true. - */ - limitHit?: boolean; - - /** - * Additional information regarding the state of the completed search. - * - * Messages with "Information" style support links in markdown syntax: - * - Click to [run a command](command:workbench.action.OpenQuickPick) - * - Click to [open a website](https://aka.ms) - * - * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. - */ - message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; - } - - /** - * A preview of the text result. - */ - export interface TextSearchMatchPreview { - /** - * The matching lines of text, or a portion of the matching line that contains the match. - */ - text: string; - - /** - * The Range within `text` corresponding to the text of the match. - * The number of matches must match the TextSearchMatch's range property. - */ - matches: Range | Range[]; - } - - /** - * A match from a text search - */ - export interface TextSearchMatch { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * The range of the match within the document, or multiple ranges for multiple matches. - */ - ranges: Range | Range[]; - - /** - * A preview of the text match. - */ - preview: TextSearchMatchPreview; - } - - /** - * A line of context surrounding a TextSearchMatch. - */ - export interface TextSearchContext { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * One line of text. - * previewOptions.charsPerLine applies to this - */ - text: string; - - /** - * The line number of this line of context. - */ - lineNumber: number; - } - - export type TextSearchResult = TextSearchMatch | TextSearchContext; - - /** - * A TextSearchProvider provides search results for text results inside files in the workspace. - */ - export interface TextSearchProvider { - /** - * Provide results that match the given text pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; - } - - //#endregion - - //#region FileSearchProvider: https://github.com/microsoft/vscode/issues/73524 - - /** - * The parameters of a query for file search. - */ - export interface FileSearchQuery { - /** - * The search pattern to match against file paths. - */ - pattern: string; - } - - /** - * Options that apply to file search. - */ - export interface FileSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults?: number; - - /** - * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, - * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. - */ - session?: CancellationToken; - } - - /** - * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. - * - * A FileSearchProvider is the more powerful of two ways to implement file search in the editor. Use a FileSearchProvider if you wish to search within a folder for - * all files that match the user's query. - * - * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, - * and in that case, every file in the folder should be returned. - */ - export interface FileSearchProvider { - /** - * Provide the set of files that match a certain file path pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching files. - * @param token A cancellation token. - */ - provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; - } - - export namespace workspace { - /** - * Register a search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; - - /** - * Register a text search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; - } - - //#endregion - - //#region findTextInFiles: https://github.com/microsoft/vscode/issues/59924 - - /** - * Options that can be set on a findTextInFiles search. - */ - export interface FindTextInFilesOptions { - /** - * A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern - * will be matched against the file paths of files relative to their workspace. Use a {@link RelativePattern relative pattern} - * to restrict the search results to a {@link WorkspaceFolder workspace folder}. - */ - include?: GlobPattern; - - /** - * A {@link GlobPattern glob pattern} that defines files and folders to exclude. The glob pattern - * will be matched against the file paths of resulting matches relative to their workspace. When `undefined`, default excludes will - * apply. - */ - exclude?: GlobPattern; - - /** - * Whether to use the default and user-configured excludes. Defaults to true. - */ - useDefaultExcludes?: boolean; - - /** - * The maximum number of results to search for - */ - maxResults?: number; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles?: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles?: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks?: boolean; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - export namespace workspace { - /** - * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - - /** - * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - } - - //#endregion - - //#region diff command: https://github.com/microsoft/vscode/issues/84899 - - /** - * The contiguous set of modified lines in a diff. - */ - export interface LineChange { - readonly originalStartLineNumber: number; - readonly originalEndLineNumber: number; - readonly modifiedStartLineNumber: number; - readonly modifiedEndLineNumber: number; - } - - export namespace commands { - - /** - * Registers a diff information command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Diff information commands are different from ordinary {@link commands.registerCommand commands} as - * they only execute when there is an active diff editor when the command is called, and the diff - * information has been computed. Also, the command handler of an editor command has access to - * the diff information. - * - * @param command A unique identifier for the command. - * @param callback A command handler function with access to the {@link LineChange diff information}. - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; - } - - //#endregion - - // eslint-disable-next-line vscode-dts-region-comments - //#region @roblourens: new debug session option for simple UI 'managedByParent' (see https://github.com/microsoft/vscode/issues/128588) - - /** - * Options for {@link debug.startDebugging starting a debug session}. - */ - export interface DebugSessionOptions { - - debugUI?: { - /** - * When true, the debug toolbar will not be shown for this session, the window statusbar color will not be changed, and the debug viewlet will not be automatically revealed. - */ - simple?: boolean; - } - - /** - * When true, a save will not be triggered for open editors when starting a debug session, regardless of the value of the `debug.saveBeforeStart` setting. - */ - suppressSaveBeforeStart?: boolean; - } - - //#endregion - - // eslint-disable-next-line vscode-dts-region-comments - //#region @weinand: variables view action contributions - - /** - * A DebugProtocolVariableContainer is an opaque stand-in type for the intersection of the Scope and Variable types defined in the Debug Adapter Protocol. - * See https://microsoft.github.io/debug-adapter-protocol/specification#Types_Scope and https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable. - */ - export interface DebugProtocolVariableContainer { - // Properties: the intersection of DAP's Scope and Variable types. - } - - /** - * A DebugProtocolVariable is an opaque stand-in type for the Variable type defined in the Debug Adapter Protocol. - * See https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable. - */ - export interface DebugProtocolVariable { - // Properties: see details [here](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_Variable). - } - - //#endregion - - // eslint-disable-next-line vscode-dts-region-comments - //#region @joaomoreno: SCM validation - - /** - * Represents the validation type of the Source Control input. - */ - export enum SourceControlInputBoxValidationType { - - /** - * Something not allowed by the rules of a language or other means. - */ - Error = 0, - - /** - * Something suspicious but allowed. - */ - Warning = 1, - - /** - * Something to inform about but not a problem. - */ - Information = 2 - } - - export interface SourceControlInputBoxValidation { - - /** - * The validation message to display. - */ - readonly message: string | MarkdownString; - - /** - * The validation type. - */ - readonly type: SourceControlInputBoxValidationType; - } - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Shows a transient contextual message on the input. - */ - showValidationMessage(message: string | MarkdownString, type: SourceControlInputBoxValidationType): void; - - /** - * A validation function for the input box. It's possible to change - * the validation provider simply by setting this property to a different function. - */ - validateInput?(value: string, cursorPosition: number): ProviderResult; - } - - //#endregion - - // eslint-disable-next-line vscode-dts-region-comments - //#region @joaomoreno: SCM selected provider - - export interface SourceControl { - - /** - * Whether the source control is selected. - */ - readonly selected: boolean; - - /** - * An event signaling when the selection state changes. - */ - readonly onDidChangeSelection: Event; - } - - //#endregion - - //#region Terminal data write event https://github.com/microsoft/vscode/issues/78502 - - export interface TerminalDataWriteEvent { - /** - * The {@link Terminal} for which the data was written. - */ - readonly terminal: Terminal; - /** - * The data being written. - */ - readonly data: string; - } - - namespace window { - /** - * An event which fires when the terminal's child pseudo-device is written to (the shell). - * In other words, this provides access to the raw data stream from the process running - * within the terminal, including VT sequences. - */ - export const onDidWriteTerminalData: Event; - } - - //#endregion - - //#region Terminal dimensions property and change event https://github.com/microsoft/vscode/issues/55718 - - /** - * An {@link Event} which fires when a {@link Terminal}'s dimensions change. - */ - export interface TerminalDimensionsChangeEvent { - /** - * The {@link Terminal} for which the dimensions have changed. - */ - readonly terminal: Terminal; - /** - * The new value for the {@link Terminal.dimensions terminal's dimensions}. - */ - readonly dimensions: TerminalDimensions; - } - - export namespace window { - /** - * An event which fires when the {@link Terminal.dimensions dimensions} of the terminal change. - */ - export const onDidChangeTerminalDimensions: Event; - } - - export interface Terminal { - /** - * The current dimensions of the terminal. This will be `undefined` immediately after the - * terminal is created as the dimensions are not known until shortly after the terminal is - * created. - */ - readonly dimensions: TerminalDimensions | undefined; - } - - //#endregion - - //#region Terminal location https://github.com/microsoft/vscode/issues/45407 - - export interface TerminalOptions { - location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; - } - - export interface ExtensionTerminalOptions { - location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; - } - - export enum TerminalLocation { - Panel = 1, - Editor = 2, - } - - export interface TerminalEditorLocationOptions { - /** - * A view column in which the {@link Terminal terminal} should be shown in the editor area. - * Use {@link ViewColumn.Active active} to open in the active editor group, other values are - * adjusted to be `Min(column, columnCount + 1)`, the - * {@link ViewColumn.Active active}-column is not adjusted. Use - * {@linkcode ViewColumn.Beside} to open the editor to the side of the currently active one. - */ - viewColumn: ViewColumn; - /** - * An optional flag that when `true` will stop the {@link Terminal} from taking focus. - */ - preserveFocus?: boolean; - } - - export interface TerminalSplitLocationOptions { - /** - * The parent terminal to split this terminal beside. This works whether the parent terminal - * is in the panel or the editor area. - */ - parentTerminal: Terminal; - } - - //#endregion - - //#region Terminal name change event https://github.com/microsoft/vscode/issues/114898 - - export interface Pseudoterminal { - /** - * An event that when fired allows changing the name of the terminal. - * - * **Example:** Change the terminal name to "My new terminal". - * ```typescript - * const writeEmitter = new vscode.EventEmitter(); - * const changeNameEmitter = new vscode.EventEmitter(); - * const pty: vscode.Pseudoterminal = { - * onDidWrite: writeEmitter.event, - * onDidChangeName: changeNameEmitter.event, - * open: () => changeNameEmitter.fire('My new terminal'), - * close: () => {} - * }; - * vscode.window.createTerminal({ name: 'My terminal', pty }); - * ``` - */ - onDidChangeName?: Event; - } - - //#endregion - - // eslint-disable-next-line vscode-dts-region-comments - //#region @jrieken -> exclusive document filters - - export interface DocumentFilter { - readonly exclusive?: boolean; - } - - //#endregion - - //#region Tree View: https://github.com/microsoft/vscode/issues/61313 @alexr00 - export interface TreeView extends Disposable { - reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number; }): Thenable; - } - //#endregion - - //#region Custom Tree View Drag and Drop https://github.com/microsoft/vscode/issues/32592 - /** - * A data provider that provides tree data - */ - export interface TreeDataProvider { - /** - * An optional event to signal that an element or root has changed. - * This will trigger the view to update the changed element/root and its children recursively (if shown). - * To signal that root has changed, do not pass any argument or pass `undefined` or `null`. - */ - onDidChangeTreeData2?: Event; - } - - export interface TreeViewOptions { - /** - * An optional interface to implement drag and drop in the tree view. - */ - dragAndDropController?: DragAndDropController; - } - - export interface TreeDataTransferItem { - asString(): Thenable; - } - - export interface TreeDataTransfer { - /** - * A map containing a mapping of the mime type of the corresponding data. - * The type for tree elements is text/treeitem. - * For example, you can reconstruct the your tree elements: - * ```ts - * JSON.parse(await (items.get('text/treeitem')!.asString())) - * ``` - */ - items: { get: (mimeType: string) => TreeDataTransferItem | undefined }; - } - - export interface DragAndDropController extends Disposable { - readonly supportedTypes: string[]; - - /** - * todo@API maybe - * - * When the user drops an item from this DragAndDropController on **another tree item** in **the same tree**, - * `onWillDrop` will be called with the dropped tree item. This is the DragAndDropController's opportunity to - * package the data from the dropped tree item into whatever format they want the target tree item to receive. - * - * The returned `TreeDataTransfer` will be merged with the original`TreeDataTransfer` for the operation. - * - * Note for implementation later: This means that the `text/treeItem` mime type will go away. - * - * @param source - */ - // onWillDrop?(source: T): Thenable; - - /** - * Extensions should fire `TreeDataProvider.onDidChangeTreeData` for any elements that need to be refreshed. - * - * @param source - * @param target - */ - onDrop(source: TreeDataTransfer, target: T): Thenable; - } - //#endregion - - //#region Task presentation group: https://github.com/microsoft/vscode/issues/47265 - export interface TaskPresentationOptions { - /** - * Controls whether the task is executed in a specific terminal group using split panes. - */ - group?: string; - - /** - * Controls whether the terminal is closed after executing the task. - */ - close?: boolean; - } - //#endregion - - //#region Custom editor move https://github.com/microsoft/vscode/issues/86146 - - // TODO: Also for custom editor - - export interface CustomTextEditorProvider { - - /** - * Handle when the underlying resource for a custom editor is renamed. - * - * This allows the webview for the editor be preserved throughout the rename. If this method is not implemented, - * the editor will destroy the previous custom editor and create a replacement one. - * - * @param newDocument New text document to use for the custom editor. - * @param existingWebviewPanel Webview panel for the custom editor. - * @param token A cancellation token that indicates the result is no longer needed. - * - * @return Thenable indicating that the webview editor has been moved. - */ - // eslint-disable-next-line vscode-dts-provider-naming - moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel, token: CancellationToken): Thenable; - } - - //#endregion - - //#region allow QuickPicks to skip sorting: https://github.com/microsoft/vscode/issues/73904 - - export interface QuickPick extends QuickInput { - /** - * An optional flag to sort the final results by index of first query match in label. Defaults to true. - */ - sortByLabel: boolean; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/132068 - - export interface QuickPick extends QuickInput { - - /* - * An optional flag to maintain the scroll position of the quick pick when the quick pick items are updated. Defaults to false. - */ - keepScrollPosition?: boolean; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/124970, Cell Execution State - - /** - * The execution state of a notebook cell. - */ - export enum NotebookCellExecutionState { - /** - * The cell is idle. - */ - Idle = 1, - /** - * Execution for the cell is pending. - */ - Pending = 2, - /** - * The cell is currently executing. - */ - Executing = 3, - } - - /** - * An event describing a cell execution state change. - */ - export interface NotebookCellExecutionStateChangeEvent { - /** - * The {@link NotebookCell cell} for which the execution state has changed. - */ - readonly cell: NotebookCell; - - /** - * The new execution state of the cell. - */ - readonly state: NotebookCellExecutionState; - } - - export namespace notebooks { - - /** - * An {@link Event} which fires when the execution state of a cell has changed. - */ - // todo@API this is an event that is fired for a property that cells don't have and that makes me wonder - // how a correct consumer works, e.g the consumer could have been late and missed an event? - export const onDidChangeNotebookCellExecutionState: Event; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/106744, Notebook, deprecated & misc - - export interface NotebookCellOutput { - id: string; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/106744, NotebookEditor - - /** - * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. - */ - export enum NotebookEditorRevealType { - /** - * The range will be revealed with as little scrolling as possible. - */ - Default = 0, - - /** - * The range will always be revealed in the center of the viewport. - */ - InCenter = 1, - - /** - * If the range is outside the viewport, it will be revealed in the center of the viewport. - * Otherwise, it will be revealed with as little scrolling as possible. - */ - InCenterIfOutsideViewport = 2, - - /** - * The range will always be revealed at the top of the viewport. - */ - AtTop = 3 - } - - /** - * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. - */ - export interface NotebookEditor { - /** - * The document associated with this notebook editor. - */ - //todo@api rename to notebook? - readonly document: NotebookDocument; - - /** - * The selections on this notebook editor. - * - * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; - */ - selections: NotebookRange[]; - - /** - * The current visible ranges in the editor (vertically). - */ - readonly visibleRanges: NotebookRange[]; - - /** - * Scroll as indicated by `revealType` in order to reveal the given range. - * - * @param range A range. - * @param revealType The scrolling strategy for revealing `range`. - */ - revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; - - /** - * The column in which this editor shows. - */ - readonly viewColumn?: ViewColumn; - } - - export interface NotebookDocumentMetadataChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the document metadata have changed. - */ - //todo@API rename to notebook? - readonly document: NotebookDocument; - } - - export interface NotebookCellsChangeData { - readonly start: number; - // todo@API end? Use NotebookCellRange instead? - readonly deletedCount: number; - // todo@API removedCells, deletedCells? - readonly deletedItems: NotebookCell[]; - // todo@API addedCells, insertedCells, newCells? - readonly items: NotebookCell[]; - } - - export interface NotebookCellsChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the cells have changed. - */ - //todo@API rename to notebook? - readonly document: NotebookDocument; - readonly changes: ReadonlyArray; - } - - export interface NotebookCellOutputsChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the cell outputs have changed. - */ - //todo@API remove? use cell.notebook instead? - readonly document: NotebookDocument; - // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell - readonly cells: NotebookCell[]; - } - - export interface NotebookCellMetadataChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the cell metadata have changed. - */ - //todo@API remove? use cell.notebook instead? - readonly document: NotebookDocument; - // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell - readonly cell: NotebookCell; - } - - export interface NotebookEditorSelectionChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the selections have changed. - */ - readonly notebookEditor: NotebookEditor; - readonly selections: ReadonlyArray - } - - export interface NotebookEditorVisibleRangesChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. - */ - readonly notebookEditor: NotebookEditor; - readonly visibleRanges: ReadonlyArray; - } - - - export interface NotebookDocumentShowOptions { - viewColumn?: ViewColumn; - preserveFocus?: boolean; - preview?: boolean; - selections?: NotebookRange[]; - } - - export namespace notebooks { - - - - export const onDidSaveNotebookDocument: Event; - - export const onDidChangeNotebookDocumentMetadata: Event; - export const onDidChangeNotebookCells: Event; - - // todo@API add onDidChangeNotebookCellOutputs - export const onDidChangeCellOutputs: Event; - - // todo@API add onDidChangeNotebookCellMetadata - export const onDidChangeCellMetadata: Event; - } - - export namespace window { - export const visibleNotebookEditors: NotebookEditor[]; - export const onDidChangeVisibleNotebookEditors: Event; - export const activeNotebookEditor: NotebookEditor | undefined; - export const onDidChangeActiveNotebookEditor: Event; - export const onDidChangeNotebookEditorSelection: Event; - export const onDidChangeNotebookEditorVisibleRanges: Event; - - export function showNotebookDocument(uri: Uri, options?: NotebookDocumentShowOptions): Thenable; - export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/106744, NotebookEditorEdit - - // todo@API add NotebookEdit-type which handles all these cases? - // export class NotebookEdit { - // range: NotebookRange; - // newCells: NotebookCellData[]; - // newMetadata?: NotebookDocumentMetadata; - // constructor(range: NotebookRange, newCells: NotebookCellData) - // } - - // export class NotebookCellEdit { - // newMetadata?: NotebookCellMetadata; - // } - - // export interface WorkspaceEdit { - // set(uri: Uri, edits: TextEdit[] | NotebookEdit[]): void - // } - - export interface WorkspaceEdit { - // todo@API add NotebookEdit-type which handles all these cases? - replaceNotebookMetadata(uri: Uri, value: { [key: string]: any }): void; - replaceNotebookCells(uri: Uri, range: NotebookRange, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; - replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: { [key: string]: any }, metadata?: WorkspaceEditEntryMetadata): void; - } - - export interface NotebookEditorEdit { - replaceMetadata(value: { [key: string]: any }): void; - replaceCells(start: number, end: number, cells: NotebookCellData[]): void; - replaceCellMetadata(index: number, metadata: { [key: string]: any }): void; - } - - export interface NotebookEditor { - /** - * Perform an edit on the notebook associated with this notebook editor. - * - * The given callback-function is invoked with an {@link NotebookEditorEdit edit-builder} which must - * be used to make edits. Note that the edit-builder is only valid while the - * callback executes. - * - * @param callback A function which can create edits using an {@link NotebookEditorEdit edit-builder}. - * @return A promise that resolves with a value indicating if the edits could be applied. - */ - // @jrieken REMOVE maybe - edit(callback: (editBuilder: NotebookEditorEdit) => void): Thenable; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/106744, NotebookEditorDecorationType - - export interface NotebookEditor { - setDecorations(decorationType: NotebookEditorDecorationType, range: NotebookRange): void; - } - - export interface NotebookDecorationRenderOptions { - backgroundColor?: string | ThemeColor; - borderColor?: string | ThemeColor; - top?: ThemableDecorationAttachmentRenderOptions; - } - - export interface NotebookEditorDecorationType { - readonly key: string; - dispose(): void; - } - - export namespace notebooks { - export function createNotebookEditorDecorationType(options: NotebookDecorationRenderOptions): NotebookEditorDecorationType; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/106744, NotebookConcatTextDocument - - export namespace notebooks { - /** - * Create a document that is the concatenation of all notebook cells. By default all code-cells are included - * but a selector can be provided to narrow to down the set of cells. - * - * @param notebook - * @param selector - */ - // todo@API really needed? we didn't find a user here - export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; - } - - export interface NotebookConcatTextDocument { - readonly uri: Uri; - readonly isClosed: boolean; - dispose(): void; - readonly onDidChange: Event; - readonly version: number; - getText(): string; - getText(range: Range): string; - - offsetAt(position: Position): number; - positionAt(offset: number): Position; - validateRange(range: Range): Range; - validatePosition(position: Position): Position; - - locationAt(positionOrRange: Position | Range): Location; - positionAt(location: Location): Position; - contains(uri: Uri): boolean; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/106744, NotebookContentProvider - - - interface NotebookDocumentBackup { - /** - * Unique identifier for the backup. - * - * This id is passed back to your extension in `openNotebook` when opening a notebook editor from a backup. - */ - readonly id: string; - - /** - * Delete the current backup. - * - * This is called by the editor when it is clear the current backup is no longer needed, such as when a new backup - * is made or when the file is saved. - */ - delete(): void; - } - - interface NotebookDocumentBackupContext { - readonly destination: Uri; - } - - interface NotebookDocumentOpenContext { - readonly backupId?: string; - readonly untitledDocumentData?: Uint8Array; - } - - // todo@API use openNotebookDOCUMENT to align with openCustomDocument etc? - // todo@API rename to NotebookDocumentContentProvider - export interface NotebookContentProvider { - - readonly options?: NotebookDocumentContentOptions; - readonly onDidChangeNotebookContentOptions?: Event; - - /** - * Content providers should always use {@link FileSystemProvider file system providers} to - * resolve the raw content for `uri` as the resouce is not necessarily a file on disk. - */ - openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext, token: CancellationToken): NotebookData | Thenable; - - // todo@API use NotebookData instead - saveNotebook(document: NotebookDocument, token: CancellationToken): Thenable; - - // todo@API use NotebookData instead - saveNotebookAs(targetResource: Uri, document: NotebookDocument, token: CancellationToken): Thenable; - - // todo@API use NotebookData instead - backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable; - } - - export namespace workspace { - - // TODO@api use NotebookDocumentFilter instead of just notebookType:string? - // TODO@API options duplicates the more powerful variant on NotebookContentProvider - export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions): Disposable; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/106744, LiveShare - - export interface NotebookRegistrationData { - displayName: string; - filenamePattern: (GlobPattern | { include: GlobPattern; exclude: GlobPattern; })[]; - exclusive?: boolean; - } - - export namespace workspace { - // SPECIAL overload with NotebookRegistrationData - export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions, registrationData?: NotebookRegistrationData): Disposable; - // SPECIAL overload with NotebookRegistrationData - export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions, registration?: NotebookRegistrationData): Disposable; - } - - //#endregion - - //#region @https://github.com/microsoft/vscode/issues/123601, notebook messaging - - /** - * Represents a script that is loaded into the notebook renderer before rendering output. This allows - * to provide and share functionality for notebook markup and notebook output renderers. - */ - export class NotebookRendererScript { - - /** - * APIs that the preload provides to the renderer. These are matched - * against the `dependencies` and `optionalDependencies` arrays in the - * notebook renderer contribution point. - */ - provides: string[]; - - /** - * URI of the JavaScript module to preload. - * - * This module must export an `activate` function that takes a context object that contains the notebook API. - */ - uri: Uri; - - /** - * @param uri URI of the JavaScript module to preload - * @param provides Value for the `provides` property - */ - constructor(uri: Uri, provides?: string | string[]); - } - - export interface NotebookController { - /** - * The human-readable label used to categorise controllers. - */ - kind?: string; - - // todo@API allow add, not remove - readonly rendererScripts: NotebookRendererScript[]; - - /** - * An event that fires when a {@link NotebookController.rendererScripts renderer script} has send a message to - * the controller. - */ - readonly onDidReceiveMessage: Event<{ editor: NotebookEditor, message: any }>; - - /** - * Send a message to the renderer of notebook editors. - * - * Note that only editors showing documents that are bound to this controller - * are receiving the message. - * - * @param message The message to send. - * @param editor A specific editor to send the message to. When `undefined` all applicable editors are receiving the message. - * @returns A promise that resolves to a boolean indicating if the message has been send or not. - */ - postMessage(message: any, editor?: NotebookEditor): Thenable; - - //todo@API validate this works - asWebviewUri(localResource: Uri): Uri; - } - - export namespace notebooks { - - export function createNotebookController(id: string, viewType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable, rendererScripts?: NotebookRendererScript[]): NotebookController; - } - - //#endregion - - //#region @eamodio - timeline: https://github.com/microsoft/vscode/issues/84297 - - export class TimelineItem { - /** - * A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred. - */ - timestamp: number; - - /** - * A human-readable string describing the timeline item. - */ - label: string; - - /** - * Optional id for the timeline item. It must be unique across all the timeline items provided by this source. - * - * If not provided, an id is generated using the timeline item's timestamp. - */ - id?: string; - - /** - * The icon path or {@link ThemeIcon} for the timeline item. - */ - iconPath?: Uri | { light: Uri; dark: Uri; } | ThemeIcon; - - /** - * A human readable string describing less prominent details of the timeline item. - */ - description?: string; - - /** - * The tooltip text when you hover over the timeline item. - */ - detail?: string; - - /** - * The {@link Command} that should be executed when the timeline item is selected. - */ - command?: Command; - - /** - * Context value of the timeline item. This can be used to contribute specific actions to the item. - * For example, a timeline item is given a context value as `commit`. When contributing actions to `timeline/item/context` - * using `menus` extension point, you can specify context value for key `timelineItem` in `when` expression like `timelineItem == commit`. - * ``` - * "contributes": { - * "menus": { - * "timeline/item/context": [ - * { - * "command": "extension.copyCommitId", - * "when": "timelineItem == commit" - * } - * ] - * } - * } - * ``` - * This will show the `extension.copyCommitId` action only for items where `contextValue` is `commit`. - */ - contextValue?: string; - - /** - * Accessibility information used when screen reader interacts with this timeline item. - */ - accessibilityInformation?: AccessibilityInformation; - - /** - * @param label A human-readable string describing the timeline item - * @param timestamp A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred - */ - constructor(label: string, timestamp: number); - } - - export interface TimelineChangeEvent { - /** - * The {@link Uri} of the resource for which the timeline changed. - */ - uri: Uri; - - /** - * A flag which indicates whether the entire timeline should be reset. - */ - reset?: boolean; - } - - export interface Timeline { - readonly paging?: { - /** - * A provider-defined cursor specifying the starting point of timeline items which are after the ones returned. - * Use `undefined` to signal that there are no more items to be returned. - */ - readonly cursor: string | undefined; - }; - - /** - * An array of {@link TimelineItem timeline items}. - */ - readonly items: readonly TimelineItem[]; - } - - export interface TimelineOptions { - /** - * A provider-defined cursor specifying the starting point of the timeline items that should be returned. - */ - cursor?: string; - - /** - * An optional maximum number timeline items or the all timeline items newer (inclusive) than the timestamp or id that should be returned. - * If `undefined` all timeline items should be returned. - */ - limit?: number | { timestamp: number; id?: string; }; - } - - export interface TimelineProvider { - /** - * An optional event to signal that the timeline for a source has changed. - * To signal that the timeline for all resources (uris) has changed, do not pass any argument or pass `undefined`. - */ - onDidChange?: Event; - - /** - * An identifier of the source of the timeline items. This can be used to filter sources. - */ - readonly id: string; - - /** - * A human-readable string describing the source of the timeline items. This can be used as the display label when filtering sources. - */ - readonly label: string; - - /** - * Provide {@link TimelineItem timeline items} for a {@link Uri}. - * - * @param uri The {@link Uri} of the file to provide the timeline for. - * @param options A set of options to determine how results should be returned. - * @param token A cancellation token. - * @return The {@link TimelineResult timeline result} or a thenable that resolves to such. The lack of a result - * can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideTimeline(uri: Uri, options: TimelineOptions, token: CancellationToken): ProviderResult; - } - - export namespace workspace { - /** - * Register a timeline provider. - * - * Multiple providers can be registered. In that case, providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param scheme A scheme or schemes that defines which documents this provider is applicable to. Can be `*` to target all documents. - * @param provider A timeline provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTimelineProvider(scheme: string | string[], provider: TimelineProvider): Disposable; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/91555 - - export enum StandardTokenType { - Other = 0, - Comment = 1, - String = 2, - RegEx = 4 - } - - export interface TokenInformation { - type: StandardTokenType; - range: Range; - } - - export namespace languages { - export function getTokenInformationAtPosition(document: TextDocument, position: Position): Thenable; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/16221 - - // todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range) - // todo@API add "mini-markdown" for links and styles - // (done) remove description - // (done) rename to InlayHint - // (done) add InlayHintKind with type, argument, etc - - export namespace languages { - /** - * Register a inlay hints provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An inlay hints provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable; - } - - export enum InlayHintKind { - Other = 0, - Type = 1, - Parameter = 2, - } - - /** - * Inlay hint information. - */ - export class InlayHint { - /** - * The text of the hint. - */ - // todo@API label? - text: string; - /** - * The position of this hint. - */ - position: Position; - /** - * The kind of this hint. - */ - kind?: InlayHintKind; - /** - * Whitespace before the hint. - */ - whitespaceBefore?: boolean; - /** - * Whitespace after the hint. - */ - whitespaceAfter?: boolean; - - // todo@API make range first argument - constructor(text: string, position: Position, kind?: InlayHintKind); - } - - /** - * The inlay hints provider interface defines the contract between extensions and - * the inlay hints feature. - */ - export interface InlayHintsProvider { - - /** - * An optional event to signal that inlay hints have changed. - * @see {@link EventEmitter} - */ - //todo@API needs proper doc (like others) - onDidChangeInlayHints?: Event; - - /** - * - * @param model The document in which the command was invoked. - * @param range The range for which inlay hints should be computed. - * @param token A cancellation token. - * @return A list of inlay hints or a thenable that resolves to such. - */ - provideInlayHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; - } - //#endregion - - //#region https://github.com/microsoft/vscode/issues/104436 - - export enum ExtensionRuntime { - /** - * The extension is running in a NodeJS extension host. Runtime access to NodeJS APIs is available. - */ - Node = 1, - /** - * The extension is running in a Webworker extension host. Runtime access is limited to Webworker APIs. - */ - Webworker = 2 - } - - export interface ExtensionContext { - readonly extensionRuntime: ExtensionRuntime; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/102091 - - export interface TextDocument { - - /** - * The {@link NotebookDocument notebook} that contains this document as a notebook cell or `undefined` when - * the document is not contained by a notebook (this should be the more frequent case). - */ - notebook: NotebookDocument | undefined; - } - //#endregion - - //#region proposed test APIs https://github.com/microsoft/vscode/issues/107467 - export namespace tests { - /** - * Requests that tests be run by their controller. - * @param run Run options to use. - * @param token Cancellation token for the test run - */ - export function runTests(run: TestRunRequest, token?: CancellationToken): Thenable; - - /** - * Returns an observer that watches and can request tests. - */ - export function createTestObserver(): TestObserver; - /** - * List of test results stored by the editor, sorted in descending - * order by their `completedAt` time. - */ - export const testResults: ReadonlyArray; - - /** - * Event that fires when the {@link testResults} array is updated. - */ - export const onDidChangeTestResults: Event; - } - - export interface TestObserver { - /** - * List of tests returned by test provider for files in the workspace. - */ - readonly tests: ReadonlyArray; - - /** - * An event that fires when an existing test in the collection changes, or - * null if a top-level test was added or removed. When fired, the consumer - * should check the test item and all its children for changes. - */ - readonly onDidChangeTest: Event; - - /** - * Dispose of the observer, allowing the editor to eventually tell test - * providers that they no longer need to update tests. - */ - dispose(): void; - } - - export interface TestsChangeEvent { - /** - * List of all tests that are newly added. - */ - readonly added: ReadonlyArray; - - /** - * List of existing tests that have updated. - */ - readonly updated: ReadonlyArray; - - /** - * List of existing tests that have been removed. - */ - readonly removed: ReadonlyArray; - } - - /** - * A test item is an item shown in the "test explorer" view. It encompasses - * both a suite and a test, since they have almost or identical capabilities. - */ - export interface TestItem { - /** - * Marks the test as outdated. This can happen as a result of file changes, - * for example. In "auto run" mode, tests that are outdated will be - * automatically rerun after a short delay. Invoking this on a - * test with children will mark the entire subtree as outdated. - * - * Extensions should generally not override this method. - */ - // todo@api still unsure about this - invalidateResults(): void; - } - - - /** - * TestResults can be provided to the editor in {@link tests.publishTestResult}, - * or read from it in {@link tests.testResults}. - * - * The results contain a 'snapshot' of the tests at the point when the test - * run is complete. Therefore, information such as its {@link Range} may be - * out of date. If the test still exists in the workspace, consumers can use - * its `id` to correlate the result instance with the living test. - */ - export interface TestRunResult { - /** - * Unix milliseconds timestamp at which the test run was completed. - */ - readonly completedAt: number; - - /** - * Optional raw output from the test run. - */ - readonly output?: string; - - /** - * List of test results. The items in this array are the items that - * were passed in the {@link tests.runTests} method. - */ - readonly results: ReadonlyArray>; - } - - /** - * A {@link TestItem}-like interface with an associated result, which appear - * or can be provided in {@link TestResult} interfaces. - */ - export interface TestResultSnapshot { - /** - * Unique identifier that matches that of the associated TestItem. - * This is used to correlate test results and tests in the document with - * those in the workspace (test explorer). - */ - readonly id: string; - - /** - * Parent of this item. - */ - readonly parent?: TestResultSnapshot; - - /** - * URI this TestItem is associated with. May be a file or file. - */ - readonly uri?: Uri; - - /** - * Display name describing the test case. - */ - readonly label: string; - - /** - * Optional description that appears next to the label. - */ - readonly description?: string; - - /** - * Location of the test item in its `uri`. This is only meaningful if the - * `uri` points to a file. - */ - readonly range?: Range; - - /** - * State of the test in each task. In the common case, a test will only - * be executed in a single task and the length of this array will be 1. - */ - readonly taskStates: ReadonlyArray; - - /** - * Optional list of nested tests for this item. - */ - readonly children: Readonly[]; - } - - export interface TestSnapshotTaskState { - /** - * Current result of the test. - */ - readonly state: TestResultState; - - /** - * The number of milliseconds the test took to run. This is set once the - * `state` is `Passed`, `Failed`, or `Errored`. - */ - readonly duration?: number; - - /** - * Associated test run message. Can, for example, contain assertion - * failure information if the test fails. - */ - readonly messages: ReadonlyArray; - } - - /** - * Possible states of tests in a test run. - */ - export enum TestResultState { - // Test will be run, but is not currently running. - Queued = 1, - // Test is currently running - Running = 2, - // Test run has passed - Passed = 3, - // Test run has failed (on an assertion) - Failed = 4, - // Test run has been skipped - Skipped = 5, - // Test run failed for some other reason (compilation error, timeout, etc) - Errored = 6 - } - - //#endregion - - //#region Opener service (https://github.com/microsoft/vscode/issues/109277) - - /** - * Details if an `ExternalUriOpener` can open a uri. - * - * The priority is also used to rank multiple openers against each other and determine - * if an opener should be selected automatically or if the user should be prompted to - * select an opener. - * - * The editor will try to use the best available opener, as sorted by `ExternalUriOpenerPriority`. - * If there are multiple potential "best" openers for a URI, then the user will be prompted - * to select an opener. - */ - export enum ExternalUriOpenerPriority { - /** - * The opener is disabled and will never be shown to users. - * - * Note that the opener can still be used if the user specifically - * configures it in their settings. - */ - None = 0, - - /** - * The opener can open the uri but will not cause a prompt on its own - * since the editor always contributes a built-in `Default` opener. - */ - Option = 1, - - /** - * The opener can open the uri. - * - * The editor's built-in opener has `Default` priority. This means that any additional `Default` - * openers will cause the user to be prompted to select from a list of all potential openers. - */ - Default = 2, - - /** - * The opener can open the uri and should be automatically selected over any - * default openers, include the built-in one from the editor. - * - * A preferred opener will be automatically selected if no other preferred openers - * are available. If multiple preferred openers are available, then the user - * is shown a prompt with all potential openers (not just preferred openers). - */ - Preferred = 3, - } - - /** - * Handles opening uris to external resources, such as http(s) links. - * - * Extensions can implement an `ExternalUriOpener` to open `http` links to a webserver - * inside of the editor instead of having the link be opened by the web browser. - * - * Currently openers may only be registered for `http` and `https` uris. - */ - export interface ExternalUriOpener { - - /** - * Check if the opener can open a uri. - * - * @param uri The uri being opened. This is the uri that the user clicked on. It has - * not yet gone through port forwarding. - * @param token Cancellation token indicating that the result is no longer needed. - * - * @return Priority indicating if the opener can open the external uri. - */ - canOpenExternalUri(uri: Uri, token: CancellationToken): ExternalUriOpenerPriority | Thenable; - - /** - * Open a uri. - * - * This is invoked when: - * - * - The user clicks a link which does not have an assigned opener. In this case, first `canOpenExternalUri` - * is called and if the user selects this opener, then `openExternalUri` is called. - * - The user sets the default opener for a link in their settings and then visits a link. - * - * @param resolvedUri The uri to open. This uri may have been transformed by port forwarding, so it - * may not match the original uri passed to `canOpenExternalUri`. Use `ctx.originalUri` to check the - * original uri. - * @param ctx Additional information about the uri being opened. - * @param token Cancellation token indicating that opening has been canceled. - * - * @return Thenable indicating that the opening has completed. - */ - openExternalUri(resolvedUri: Uri, ctx: OpenExternalUriContext, token: CancellationToken): Thenable | void; - } - - /** - * Additional information about the uri being opened. - */ - interface OpenExternalUriContext { - /** - * The uri that triggered the open. - * - * This is the original uri that the user clicked on or that was passed to `openExternal.` - * Due to port forwarding, this may not match the `resolvedUri` passed to `openExternalUri`. - */ - readonly sourceUri: Uri; - } - - /** - * Additional metadata about a registered `ExternalUriOpener`. - */ - interface ExternalUriOpenerMetadata { - - /** - * List of uri schemes the opener is triggered for. - * - * Currently only `http` and `https` are supported. - */ - readonly schemes: readonly string[] - - /** - * Text displayed to the user that explains what the opener does. - * - * For example, 'Open in browser preview' - */ - readonly label: string; - } - - namespace window { - /** - * Register a new `ExternalUriOpener`. - * - * When a uri is about to be opened, an `onOpenExternalUri:SCHEME` activation event is fired. - * - * @param id Unique id of the opener, such as `myExtension.browserPreview`. This is used in settings - * and commands to identify the opener. - * @param opener Opener to register. - * @param metadata Additional information about the opener. - * - * @returns Disposable that unregisters the opener. - */ - export function registerExternalUriOpener(id: string, opener: ExternalUriOpener, metadata: ExternalUriOpenerMetadata): Disposable; - } - - interface OpenExternalOptions { - /** - * Allows using openers contributed by extensions through `registerExternalUriOpener` - * when opening the resource. - * - * If `true`, the editor will check if any contributed openers can handle the - * uri, and fallback to the default opener behavior. - * - * If it is string, this specifies the id of the `ExternalUriOpener` - * that should be used if it is available. Use `'default'` to force the editor's - * standard external opener to be used. - */ - readonly allowContributedOpeners?: boolean | string; - } - - namespace env { - export function openExternal(target: Uri, options?: OpenExternalOptions): Thenable; - } - - //#endregion - - //#region https://github.com/Microsoft/vscode/issues/15178 - - /** - * Represents a tab within the window - */ - export interface Tab { - /** - * The text displayed on the tab - */ - readonly label: string; - - /** - * The index of the tab within the column - */ - readonly index: number; - - /** - * The column which the tab belongs to - */ - readonly viewColumn: ViewColumn; - - /** - * The resource represented by the tab if available. - * Note: Not all tabs have a resource associated with them. - */ - readonly resource: Uri | undefined; - - /** - * The identifier of the view contained in the tab - * This is equivalent to `viewType` for custom editors and `notebookType` for notebooks. - * The built-in text editor has an id of 'default' for all configurations. - */ - readonly viewId: string | undefined; - - /** - * All the resources and viewIds represented by a tab - * {@link Tab.resource resource} and {@link Tab.viewId viewId} will - * always be at index 0. - */ - readonly additionalResourcesAndViewIds: readonly { - readonly resource: Uri | undefined, - readonly viewId: string | undefined - }[]; - - /** - * Whether or not the tab is currently active - * Dictated by being the selected tab in the active group - */ - readonly isActive: boolean; - - /** - * Moves a tab to the given index within the column. - * If the index is out of range, the tab will be moved to the end of the column. - * If the column is out of range, a new one will be created after the last existing column. - * @param index The index to move the tab to - * @param viewColumn The column to move the tab into - */ - move(index: number, viewColumn: ViewColumn): Thenable; - - /** - * Closes the tab. This makes the tab object invalid and the tab - * should no longer be used for further actions. - */ - close(): Thenable; - } - - export namespace window { - /** - * A list of all opened tabs - * Ordered from left to right - */ - export const tabs: readonly Tab[]; - - /** - * The currently active tab - * Undefined if no tabs are currently opened - */ - export const activeTab: Tab | undefined; - - /** - * An {@link Event} which fires when the array of {@link window.tabs tabs} - * has changed. - */ - export const onDidChangeTabs: Event; - - /** - * An {@link Event} which fires when the {@link window.activeTab activeTab} - * has changed. - */ - export const onDidChangeActiveTab: Event; - - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/120173 - /** - * The object describing the properties of the workspace trust request - */ - export interface WorkspaceTrustRequestOptions { - /** - * Custom message describing the user action that requires workspace - * trust. If omitted, a generic message will be displayed in the workspace - * trust request dialog. - */ - readonly message?: string; - } - - export namespace workspace { - /** - * Prompt the user to chose whether to trust the current workspace - * @param options Optional object describing the properties of the - * workspace trust request. - */ - export function requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Thenable; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/115616 @alexr00 - export enum PortAutoForwardAction { - Notify = 1, - OpenBrowser = 2, - OpenPreview = 3, - Silent = 4, - Ignore = 5, - OpenBrowserOnce = 6 - } - - export class PortAttributes { - /** - * The port number associated with this this set of attributes. - */ - port: number; - - /** - * The action to be taken when this port is detected for auto forwarding. - */ - autoForwardAction: PortAutoForwardAction; - - /** - * Creates a new PortAttributes object - * @param port the port number - * @param autoForwardAction the action to take when this port is detected - */ - constructor(port: number, autoForwardAction: PortAutoForwardAction); - } - - export interface PortAttributesProvider { - /** - * Provides attributes for the given port. For ports that your extension doesn't know about, simply - * return undefined. For example, if `providePortAttributes` is called with ports 3000 but your - * extension doesn't know anything about 3000 you should return undefined. - */ - providePortAttributes(port: number, pid: number | undefined, commandLine: string | undefined, token: CancellationToken): ProviderResult; - } - - export namespace workspace { - /** - * If your extension listens on ports, consider registering a PortAttributesProvider to provide information - * about the ports. For example, a debug extension may know about debug ports in it's debuggee. By providing - * this information with a PortAttributesProvider the extension can tell the editor that these ports should be - * ignored, since they don't need to be user facing. - * - * @param portSelector If registerPortAttributesProvider is called after you start your process then you may already - * know the range of ports or the pid of your process. All properties of a the portSelector must be true for your - * provider to get called. - * The `portRange` is start inclusive and end exclusive. - * @param provider The PortAttributesProvider - */ - export function registerPortAttributesProvider(portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: PortAttributesProvider): Disposable; - } - //#endregion - - //#region https://github.com/microsoft/vscode/issues/119904 @eamodio - - export interface SourceControlInputBox { - - /** - * Sets focus to the input. - */ - focus(): void; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima - - export namespace languages { - /** - * Registers an inline completion provider. - */ - export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; - } - - export interface InlineCompletionItemProvider { - /** - * Provides inline completion items for the given position and document. - * If inline completions are enabled, this method will be called whenever the user stopped typing. - * It will also be called when the user explicitly triggers inline completions or asks for the next or previous inline completion. - * Use `context.triggerKind` to distinguish between these scenarios. - */ - provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult | T[]>; - } - - export interface InlineCompletionContext { - /** - * How the completion was triggered. - */ - readonly triggerKind: InlineCompletionTriggerKind; - - /** - * Provides information about the currently selected item in the autocomplete widget if it is visible. - * - * If set, provided inline completions must extend the text of the selected item - * and use the same range, otherwise they are not shown as preview. - * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, - * the inline completion must also replace `.` and start with `.log`, for example `.log()`. - * - * Inline completion providers are requested again whenever the selected item changes. - * - * The user must configure `"editor.suggest.preview": true` for this feature. - */ - readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; - } - - export interface SelectedCompletionInfo { - range: Range; - text: string; - } - - /** - * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. - */ - export enum InlineCompletionTriggerKind { - /** - * Completion was triggered automatically while editing. - * It is sufficient to return a single completion item in this case. - */ - Automatic = 0, - - /** - * Completion was triggered explicitly by a user gesture. - * Return multiple completion items to enable cycling through them. - */ - Explicit = 1, - } - - export class InlineCompletionList { - items: T[]; - - constructor(items: T[]); - } - - export class InlineCompletionItem { - /** - * The text to replace the range with. - * - * The text the range refers to should be a prefix of this value and must be a subword (`AB` and `BEF` are subwords of `ABCDEF`, but `Ab` is not). - */ - text: string; - - /** - * The range to replace. - * Must begin and end on the same line. - * - * Prefer replacements over insertions to avoid cache invalidation: - * Instead of reporting a completion that inserts an extension at the end of a word, - * the whole word should be replaced with the extended word. - */ - range?: Range; - - /** - * An optional {@link Command} that is executed *after* inserting this completion. - */ - command?: Command; - - constructor(text: string, range?: Range, command?: Command); - } - - - /** - * Be aware that this API will not ever be finalized. - */ - export namespace window { - export function getInlineCompletionItemController(provider: InlineCompletionItemProvider): InlineCompletionController; - } - - /** - * Be aware that this API will not ever be finalized. - */ - export interface InlineCompletionController { - /** - * Is fired when an inline completion item is shown to the user. - */ - // eslint-disable-next-line vscode-dts-event-naming - readonly onDidShowCompletionItem: Event>; - } - - /** - * Be aware that this API will not ever be finalized. - */ - export interface InlineCompletionItemDidShowEvent { - completionItem: T; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/126280 @mjbvz - - export interface NotebookCellData { - /** - * Mime type determines how the cell's `value` is interpreted. - * - * The mime selects which notebook renders is used to render the cell. - * - * If not set, internally the cell is treated as having a mime type of `text/plain`. - * Cells that set `language` to `markdown` instead are treated as `text/markdown`. - */ - mime?: string; - } - - export interface NotebookCell { - /** - * Mime type determines how the markup cell's `value` is interpreted. - * - * The mime selects which notebook renders is used to render the cell. - * - * If not set, internally the cell is treated as having a mime type of `text/plain`. - * Cells that set `language` to `markdown` instead are treated as `text/markdown`. - */ - mime: string | undefined; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/123713 @connor4312 - export interface TestRun { - /** - * Test coverage provider for this result. An extension can defer setting - * this until after a run is complete and coverage is available. - */ - coverageProvider?: TestCoverageProvider - // ... - } - - /** - * Provides information about test coverage for a test result. - * Methods on the provider will not be called until the test run is complete - */ - export interface TestCoverageProvider { - /** - * Returns coverage information for all files involved in the test run. - * @param token A cancellation token. - * @return Coverage metadata for all files involved in the test. - */ - provideFileCoverage(token: CancellationToken): ProviderResult; - - /** - * Give a FileCoverage to fill in more data, namely {@link FileCoverage.detailedCoverage}. - * The editor will only resolve a FileCoverage once, and onyl if detailedCoverage - * is undefined. - * - * @param coverage A coverage object obtained from {@link provideFileCoverage} - * @param token A cancellation token. - * @return The resolved file coverage, or a thenable that resolves to one. It - * is OK to return the given `coverage`. When no result is returned, the - * given `coverage` will be used. - */ - resolveFileCoverage?(coverage: T, token: CancellationToken): ProviderResult; - } - - /** - * A class that contains information about a covered resource. A count can - * be give for lines, branches, and functions in a file. - */ - export class CoveredCount { - /** - * Number of items covered in the file. - */ - covered: number; - /** - * Total number of covered items in the file. - */ - total: number; - - /** - * @param covered Value for {@link CovereredCount.covered} - * @param total Value for {@link CovereredCount.total} - */ - constructor(covered: number, total: number); - } - - /** - * Contains coverage metadata for a file. - */ - export class FileCoverage { - /** - * File URI. - */ - readonly uri: Uri; - - /** - * Statement coverage information. If the reporter does not provide statement - * coverage information, this can instead be used to represent line coverage. - */ - statementCoverage: CoveredCount; - - /** - * Branch coverage information. - */ - branchCoverage?: CoveredCount; - - /** - * Function coverage information. - */ - functionCoverage?: CoveredCount; - - /** - * Detailed, per-statement coverage. If this is undefined, the editor will - * call {@link TestCoverageProvider.resolveFileCoverage} when necessary. - */ - detailedCoverage?: DetailedCoverage[]; - - /** - * Creates a {@link FileCoverage} instance with counts filled in from - * the coverage details. - * @param uri Covered file URI - * @param detailed Detailed coverage information - */ - static fromDetails(uri: Uri, details: readonly DetailedCoverage[]): FileCoverage; - - /** - * @param uri Covered file URI - * @param statementCoverage Statement coverage information. If the reporter - * does not provide statement coverage information, this can instead be - * used to represent line coverage. - * @param branchCoverage Branch coverage information - * @param functionCoverage Function coverage information - */ - constructor( - uri: Uri, - statementCoverage: CoveredCount, - branchCoverage?: CoveredCount, - functionCoverage?: CoveredCount, - ); - } - - /** - * Contains coverage information for a single statement or line. - */ - export class StatementCoverage { - /** - * The number of times this statement was executed. If zero, the - * statement will be marked as un-covered. - */ - executionCount: number; - - /** - * Statement location. - */ - location: Position | Range; - - /** - * Coverage from branches of this line or statement. If it's not a - * conditional, this will be empty. - */ - branches: BranchCoverage[]; - - /** - * @param location The statement position. - * @param executionCount The number of times this statement was - * executed. If zero, the statement will be marked as un-covered. - * @param branches Coverage from branches of this line. If it's not a - * conditional, this should be omitted. - */ - constructor(executionCount: number, location: Position | Range, branches?: BranchCoverage[]); - } - - /** - * Contains coverage information for a branch of a {@link StatementCoverage}. - */ - export class BranchCoverage { - /** - * The number of times this branch was executed. If zero, the - * branch will be marked as un-covered. - */ - executionCount: number; - - /** - * Branch location. - */ - location?: Position | Range; - - /** - * @param executionCount The number of times this branch was executed. - * @param location The branch position. - */ - constructor(executionCount: number, location?: Position | Range); - } - - /** - * Contains coverage information for a function or method. - */ - export class FunctionCoverage { - /** - * The number of times this function was executed. If zero, the - * function will be marked as un-covered. - */ - executionCount: number; - - /** - * Function location. - */ - location: Position | Range; - - /** - * @param executionCount The number of times this function was executed. - * @param location The function position. - */ - constructor(executionCount: number, location: Position | Range); - } - - export type DetailedCoverage = StatementCoverage | FunctionCoverage; - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/129037 - - enum LanguageStatusSeverity { - Information = 0, - Warning = 1, - Error = 2 - } - - interface LanguageStatusItem { - readonly id: string; - selector: DocumentSelector; - // todo@jrieken replace with boolean ala needsAttention - severity: LanguageStatusSeverity; - name: string | undefined; - text: string; - detail?: string; - command: Command | undefined; - accessibilityInformation?: AccessibilityInformation; - dispose(): void; - } - - namespace languages { - export function createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem; - } - - //#endregion - - //#region https://github.com/microsoft/vscode/issues/88716 - export interface QuickPickItem { - buttons?: QuickInputButton[]; - } - export interface QuickPick extends QuickInput { - readonly onDidTriggerItemButton: Event>; - } - export interface QuickPickItemButtonEvent { - button: QuickInputButton; - item: T; - } - - //#endregion - - //#region @eamodio https://github.com/microsoft/vscode/issues/133935 - - export interface SourceControl { - actionButton?: Command; - } - - //#endregion - - //#region @sandy081 https://github.com/microsoft/vscode/issues/132183 - - export interface OutputChannel { - /* - * Replaces the existing contents of the channel with the given value. - */ - replace(value: string): void; - } - - //#endregion -} diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 18dddc41b8c..96a88b5c886 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -10,7 +10,7 @@ import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { reviveWebviewContentOptions } from 'vs/workbench/api/browser/mainThreadWebviews'; -import { ExtHostContext, ExtHostEditorInsetsShape, IExtHostContext, IWebviewOptions, MainContext, MainThreadEditorInsetsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, ExtHostEditorInsetsShape, IExtHostContext, IWebviewContentOptions, MainContext, MainThreadEditorInsetsShape } from 'vs/workbench/api/common/extHost.protocol'; import { IWebviewService, IWebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { extHostNamedCustomer } from '../common/extHostCustomers'; @@ -70,7 +70,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { this._disposables.dispose(); } - async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise { + async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewContentOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise { let editor: IActiveCodeEditor | undefined; id = id.substr(0, id.indexOf(',')); //todo@jrieken HACK @@ -121,7 +121,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { inset.webview.html = value; } - $setOptions(handle: number, options: IWebviewOptions): void { + $setOptions(handle: number, options: IWebviewContentOptions): void { const inset = this.getInset(handle); inset.webview.contentOptions = reviveWebviewContentOptions(options); } diff --git a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts index 8a7cff3c95e..304d31cd546 100644 --- a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts +++ b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts @@ -37,9 +37,14 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { private _forwardMarkers(resources: readonly URI[]): void { const data: [UriComponents, IMarkerData[]][] = []; for (const resource of resources) { - const markerData = this._markerService.read({ resource }).filter(marker => !this._activeOwners.has(marker.owner)); - if (markerData.length > 0) { - data.push([resource, markerData]); + const allMarkerData = this._markerService.read({ resource }); + if (allMarkerData.length === 0) { + data.push([resource, []]); + } else { + const forgeinMarkerData = allMarkerData.filter(marker => !this._activeOwners.has(marker.owner)); + if (forgeinMarkerData.length > 0) { + data.push([resource, forgeinMarkerData]); + } } } if (data.length > 0) { diff --git a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts index 6baffac194d..c5aec867363 100644 --- a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItems, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItem, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; interface QuickInputSession { input: IQuickInput; - handlesToItems: Map; + handlesToItems: Map; } function reviveIconPathUris(iconPath: { dark: URI; light?: URI | undefined; }) { @@ -27,7 +27,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { private readonly _proxy: ExtHostQuickOpenShape; private readonly _quickInputService: IQuickInputService; private readonly _items: Record = {}; @@ -42,8 +42,8 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { public dispose(): void { } - $show(instance: number, options: IPickOptions, token: CancellationToken): Promise { - const contents = new Promise((resolve, reject) => { + $show(instance: number, options: IPickOptions, token: CancellationToken): Promise { + const contents = new Promise((resolve, reject) => { this._items[instance] = { resolve, reject }; }); @@ -51,7 +51,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { ...options, onDidFocus: el => { if (el) { - this._proxy.$onItemSelected((el).handle); + this._proxy.$onItemSelected((el).handle); } } }; @@ -73,7 +73,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } - $setItems(instance: number, items: TransferQuickPickItems[]): Promise { + $setItems(instance: number, items: TransferQuickPickItem[]): Promise { if (this._items[instance]) { this._items[instance].resolve(items); delete this._items[instance]; @@ -140,13 +140,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { // Add extra events specific for quickpick const quickpick = input as IQuickPick; quickpick.onDidChangeActive(items => { - this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItems).handle)); + this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItem).handle)); }); quickpick.onDidChangeSelection(items => { - this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItems).handle)); + this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItem).handle)); }); quickpick.onDidTriggerItemButton((e) => { - this._proxy.$onDidTriggerItemButton(sessionId, (e.item as TransferQuickPickItems).handle, (e.button as TransferQuickInputButton).handle); + this._proxy.$onDidTriggerItemButton(sessionId, (e.item as TransferQuickPickItem).handle, (e.button as TransferQuickInputButton).handle); }); } @@ -169,7 +169,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } else if (param === 'items') { handlesToItems.clear(); - params[param].forEach((item: TransferQuickPickItems) => { + params[param].forEach((item: TransferQuickPickItem) => { if (item.buttons) { item.buttons = item.buttons.map((button: TransferQuickInputButton) => { if (button.iconPath) { diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 75328bdf9ae..0e36a47285a 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -431,15 +431,6 @@ export class MainThreadSCM implements MainThreadSCMShape { repository.input.visible = visible; } - $setInputBoxFocus(sourceControlHandle: number): void { - const repository = this._repositories.get(sourceControlHandle); - if (!repository) { - return; - } - - repository.input.setFocus(); - } - $showValidationMessage(sourceControlHandle: number, message: string | IMarkdownString, type: InputValidationType) { const repository = this._repositories.get(sourceControlHandle); if (!repository) { diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index 415f674a902..a1d54ed6423 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -153,7 +153,9 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun return (await this.tunnelService.tunnels).map(tunnel => { return { remoteAddress: { port: tunnel.tunnelRemotePort, host: tunnel.tunnelRemoteHost }, - localAddress: tunnel.localAddress + localAddress: tunnel.localAddress, + privacy: tunnel.privacy, + protocol: tunnel.protocol }; }); } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 1aad68ab99a..d355e0876c4 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -5,17 +5,17 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { EditorGroupColumn, columnToEditorGroup, editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; import { WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager'; import { ICreateWebViewShowOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; +import { columnToEditorGroup, editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -151,13 +151,8 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc extensionData: extHostProtocol.WebviewExtensionDescription, handle: extHostProtocol.WebviewHandle, viewType: string, - initData: { - title: string; - webviewOptions: extHostProtocol.IWebviewOptions; - panelOptions: extHostProtocol.IWebviewPanelOptions; - serializeBuffersForPostMessage: boolean; - }, - showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; }, + initData: extHostProtocol.IWebviewInitData, + showOptions: extHostProtocol.WebviewPanelShowOptions, ): void { const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); if (showOptions) { @@ -192,7 +187,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc webview.setName(value); } - public $setIconPath(handle: extHostProtocol.WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void { + public $setIconPath(handle: extHostProtocol.WebviewHandle, value: extHostProtocol.IWebviewIconPath | undefined): void { const webview = this.getWebviewInput(handle); webview.iconPath = reviveWebviewIcon(value); } @@ -316,12 +311,14 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc } } -function reviveWebviewIcon( - value: { light: UriComponents, dark: UriComponents; } | undefined -): WebviewIcons | undefined { - return value - ? { light: URI.revive(value.light), dark: URI.revive(value.dark) } - : undefined; +function reviveWebviewIcon(value: extHostProtocol.IWebviewIconPath | undefined): WebviewIcons | undefined { + if (!value) { + return undefined; + } + return { + light: URI.revive(value.light), + dark: URI.revive(value.dark), + }; } function reviveWebviewOptions(panelOptions: extHostProtocol.IWebviewPanelOptions): WebviewOptions { diff --git a/src/vs/workbench/api/browser/mainThreadWebviews.ts b/src/vs/workbench/api/browser/mainThreadWebviews.ts index 0870dcc05d6..25b3f61f574 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviews.ts @@ -55,7 +55,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma webview.html = value; } - public $setOptions(handle: extHostProtocol.WebviewHandle, options: extHostProtocol.IWebviewOptions): void { + public $setOptions(handle: extHostProtocol.WebviewHandle, options: extHostProtocol.IWebviewContentOptions): void { const webview = this.getWebview(handle); webview.contentOptions = reviveWebviewContentOptions(options); } @@ -123,10 +123,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } export function reviveWebviewExtension(extensionData: extHostProtocol.WebviewExtensionDescription): WebviewExtensionDescription { - return { id: extensionData.id, location: URI.revive(extensionData.location) }; + return { + id: extensionData.id, + location: URI.revive(extensionData.location), + }; } -export function reviveWebviewContentOptions(webviewOptions: extHostProtocol.IWebviewOptions): WebviewContentOptions { +export function reviveWebviewContentOptions(webviewOptions: extHostProtocol.IWebviewContentOptions): WebviewContentOptions { return { allowScripts: webviewOptions.enableScripts, allowForms: webviewOptions.enableForms, diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 63fcf147042..4ad9fee8a91 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -10,7 +10,7 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ExtensionIdentifier, IExtensionDescription, isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -25,6 +25,7 @@ import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files' import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/browser/remoteExplorer'; import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm'; import { WebviewViewPane } from 'vs/workbench/contrib/webviewView/browser/webviewViewPane'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -404,7 +405,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return; } - if (entry.key === 'remote' && !isProposedApiEnabled(extension.description)) { + if (entry.key === 'remote' && !isProposedApiEnabled(extension.description, undefined)) { collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enableProposedApi' turned on to be added to 'Remote'.", entry.key)); return; } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3313761cc00..6f343668646 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -49,7 +49,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import type * as vscode from 'vscode'; -import { checkProposedApiEnabled, IExtensionDescription, isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { values } from 'vs/base/common/collections'; import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets'; import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService'; @@ -90,6 +90,7 @@ import { matchesScheme } from 'vs/platform/opener/common/opener'; import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors'; import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments'; import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive'; +import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -217,7 +218,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I informOnce(); } if (typeof filter.exclusive === 'boolean') { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'documentFiltersExclusive'); } } return selector; @@ -226,14 +227,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const authentication: typeof vscode.authentication = { getSession(providerId: string, scopes: readonly string[], options?: vscode.AuthenticationGetSessionOptions) { - if (options?.forceNewSession || options?.silent) { - checkProposedApiEnabled(extension); + if (options?.forceNewSession) { + checkProposedApiEnabled(extension, 'authSession'); } return extHostAuthentication.getSession(extension, providerId, scopes, options as any); }, // TODO: remove this after GHPR and Codespaces move off of it async hasSession(providerId: string, scopes: readonly string[]) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'authSession'); return !!(await extHostAuthentication.getSession(extension, providerId, scopes, { silent: true } as any)); }, get onDidChangeSessions(): Event { @@ -270,7 +271,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, undefined, undefined, extension); }, registerDiffInformationCommand: (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'diffCommand'); return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { const activeTextEditor = extHostDocumentsAndEditors.activeEditor(true); if (!activeTextEditor) { @@ -338,7 +339,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return getRemoteName(initData.remote.authority); }, get remoteAuthority() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return initData.remote.authority; }, get uiKind() { @@ -359,19 +360,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTesting.createTestController(provider, label); }, createTestObserver() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.createTestObserver(); }, runTests(provider) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.runTests(provider); }, get onDidChangeTestResults() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.onResultsChanged; }, get testResults() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.results; }, }; @@ -483,7 +484,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); }, registerInlineCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider): vscode.Disposable { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'inlineCompletions'); return extHostLanguageFeatures.registerInlineCompletionsProvider(extension, checkSelector(selector), provider); }, registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { @@ -508,15 +509,15 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageFeatures.setLanguageConfiguration(extension, language, configuration); }, getTokenInformationAtPosition(doc: vscode.TextDocument, pos: vscode.Position) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tokenInformation'); return extHostLanguages.tokenAtPosition(doc, pos); }, registerInlayHintsProvider(selector: vscode.DocumentSelector, provider: vscode.InlayHintsProvider): vscode.Disposable { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'inlayHints'); return extHostLanguageFeatures.registerInlayHintsProvider(extension, selector, provider); }, createLanguageStatusItem(id: string, selector: vscode.DocumentSelector): vscode.LanguageStatusItem { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'languageStatus'); return extHostLanguages.createLanguageStatusItem(extension, id, selector); } }; @@ -573,14 +574,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables); }, onDidChangeTerminalDimensions(listener, thisArg?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'terminalDimensions'); return extHostTerminalService.onDidChangeTerminalDimensions(listener, thisArg, disposables); }, onDidChangeTerminalState(listener, thisArg?, disposables?) { return extHostTerminalService.onDidChangeTerminalState(listener, thisArg, disposables); }, onDidWriteTerminalData(listener, thisArg?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'terminalDataWriteEvent'); return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables); }, get state() { @@ -599,7 +600,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return >extHostMessageService.showMessage(extension, Severity.Error, message, rest[0], >rest.slice(1)); }, showQuickPick(items: any, options?: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { - return extHostQuickOpen.showQuickPick(items, isProposedApiEnabled(extension), options, token); + return extHostQuickOpen.showQuickPick(items, isProposedApiEnabled(extension, undefined), options, token); }, showWorkspaceFolderPick(options?: vscode.WorkspaceFolderPickOptions) { return extHostQuickOpen.showWorkspaceFolderPick(options); @@ -648,13 +649,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWebviewPanels.createWebviewPanel(extension, viewType, title, showOptions, options); }, createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options?: vscode.WebviewOptions): vscode.WebviewEditorInset { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'editorInsets'); return extHostEditorInsets.createWebviewEditorInset(editor, line, height, options, extension); }, createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.ExtensionTerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { if (typeof nameOrOptions === 'object') { if ('location' in nameOrOptions) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'terminalLocation'); } if ('pty' in nameOrOptions) { return extHostTerminalService.createExtensionTerminal(nameOrOptions); @@ -688,7 +689,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostUrls.registerUriHandler(extension.identifier, handler); }, createQuickPick(): vscode.QuickPick { - return extHostQuickOpen.createQuickPick(extension.identifier, isProposedApiEnabled(extension)); + return extHostQuickOpen.createQuickPick(extension.identifier, isProposedApiEnabled(extension, undefined)); }, createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); @@ -707,55 +708,55 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWebviewViews.registerWebviewViewProvider(extension, viewId, provider, options?.webviewOptions); }, get activeNotebookEditor(): vscode.NotebookEditor | undefined { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.activeNotebookEditor; }, onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables); }, get visibleNotebookEditors() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.visibleNotebookEditors; }, get onDidChangeVisibleNotebookEditors() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeVisibleNotebookEditors; }, onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookEditors.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables); }, onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookEditors.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables); }, showNotebookDocument(uriOrDocument, options?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.showNotebookDocument(uriOrDocument, options); }, registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'externalUriOpener'); return extHostUriOpeners.registerExternalUriOpener(extension.identifier, id, opener, metadata); }, get tabs() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.tabs; }, get activeTab() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.activeTab; }, get onDidChangeTabs() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.onDidChangeTabs; }, get onDidChangeActiveTab() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.onDidChangeActiveTab; }, getInlineCompletionItemController(provider: vscode.InlineCompletionItemProvider): vscode.InlineCompletionController { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'inlineCompletions'); return InlineCompletionController.get(provider); } }; @@ -804,6 +805,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.identifier, token); }, findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback: vscode.FindTextInFilesOptions | ((result: vscode.TextSearchResult) => void), callbackOrToken?: vscode.CancellationToken | ((result: vscode.TextSearchResult) => void), token?: vscode.CancellationToken) => { + checkProposedApiEnabled(extension, 'findTextInFiles'); let options: vscode.FindTextInFilesOptions; let callback: (result: vscode.TextSearchResult) => void; @@ -890,11 +892,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.onDidCloseNotebookDocument; }, registerNotebookSerializer(viewType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData) { - return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options, isProposedApiEnabled(extension) ? registration : undefined); + return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options, isProposedApiEnabled(extension, 'notebookLiveShare') ? registration : undefined); }, registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData) => { - checkProposedApiEnabled(extension); - return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options, isProposedApiEnabled(extension) ? registration : undefined); + checkProposedApiEnabled(extension, 'notebookContentProvider'); + return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options, isProposedApiEnabled(extension, 'notebookLiveShare') ? registration : undefined); }, onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { return configProvider.onDidChangeConfiguration(listener, thisArgs, disposables); @@ -913,25 +915,25 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTask.registerTaskProvider(extension, type, provider); }, registerFileSystemProvider(scheme, provider, options) { - return extHostFileSystem.registerFileSystemProvider(extension.identifier, scheme, provider, options); + return extHostFileSystem.registerFileSystemProvider(extension, scheme, provider, options); }, get fs() { return extHostConsumerFileSystem.value; }, registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'fileSearchProvider'); return extHostSearch.registerFileSearchProvider(scheme, provider); }, registerTextSearchProvider: (scheme: string, provider: vscode.TextSearchProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'textSearchProvider'); return extHostSearch.registerTextSearchProvider(scheme, provider); }, registerRemoteAuthorityResolver: (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver); }, registerResourceLabelFormatter: (formatter: vscode.ResourceLabelFormatter) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostLabelService.$registerResourceLabelFormatter(formatter); }, onDidCreateFiles: (listener, thisArg, disposables) => { @@ -953,7 +955,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); }, openTunnel: (forward: vscode.TunnelOptions) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostTunnelService.openTunnel(extension, forward).then(value => { if (!value) { throw new Error('cannot open tunnel'); @@ -962,26 +964,26 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }); }, get tunnels() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostTunnelService.getTunnels(); }, onDidChangeTunnels: (listener, thisArg?, disposables?) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables); }, registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'portsAttributes'); return extHostTunnelService.registerPortsAttributesProvider(portSelector, provider); }, registerTimelineProvider: (scheme: string | string[], provider: vscode.TimelineProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'timeline'); return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter); }, get isTrusted() { return extHostWorkspace.trusted; }, requestWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'workspaceTrust'); return extHostWorkspace.requestWorkspaceTrust(options); }, onDidGrantWorkspaceTrust: (listener, thisArgs?, disposables?) => { @@ -1094,44 +1096,44 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespace: notebook const notebooks: typeof vscode.notebooks = { createNotebookController(id: string, notebookType: string, label: string, handler?, rendererScripts?: vscode.NotebookRendererScript[]) { - return extHostNotebookKernels.createNotebookController(extension, id, notebookType, label, handler, isProposedApiEnabled(extension) ? rendererScripts : undefined); + return extHostNotebookKernels.createNotebookController(extension, id, notebookType, label, handler, isProposedApiEnabled(extension, 'notebookMessaging') ? rendererScripts : undefined); }, registerNotebookCellStatusBarItemProvider: (notebookType: string, provider: vscode.NotebookCellStatusBarItemProvider) => { return extHostNotebook.registerNotebookCellStatusBarItemProvider(extension, notebookType, provider); }, get onDidSaveNotebookDocument(): Event { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookDocuments.onDidSaveNotebookDocument; }, createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditorDecorationType'); return extHostNotebookEditors.createNotebookEditorDecorationType(options); }, createRendererMessaging(rendererId) { return extHostNotebookRenderers.createRendererMessaging(extension, rendererId); }, onDidChangeNotebookDocumentMetadata(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookDocuments.onDidChangeNotebookDocumentMetadata(listener, thisArgs, disposables); }, onDidChangeNotebookCells(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables); }, onDidChangeNotebookCellExecutionState(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookCellExecutionState'); return extHostNotebook.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables); }, onDidChangeCellOutputs(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables); }, onDidChangeCellMetadata(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables); }, createConcatTextDocument(notebook, selector) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookConcatTextDocument'); return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector); }, }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2291bc2eea0..d935e9ab65a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -510,7 +510,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $sendProcessExit(terminalId: number, exitCode: number | undefined): void; } -export interface TransferQuickPickItems extends quickInput.IQuickPickItem { +export interface TransferQuickPickItem extends quickInput.IQuickPickItem { handle: number; buttons?: TransferQuickInputButton[]; } @@ -548,7 +548,7 @@ export interface TransferQuickPick extends BaseTransferQuickInput { buttons?: TransferQuickInputButton[]; - items?: TransferQuickPickItems[]; + items?: TransferQuickPickItem[]; activeItems?: number[]; @@ -593,8 +593,8 @@ export interface IInputBoxOptions { } export interface MainThreadQuickOpenShape extends IDisposable { - $show(instance: number, options: quickInput.IPickOptions, token: CancellationToken): Promise; - $setItems(instance: number, items: TransferQuickPickItems[]): Promise; + $show(instance: number, options: quickInput.IPickOptions, token: CancellationToken): Promise; + $setItems(instance: number, items: TransferQuickPickItem[]): Promise; $setError(instance: number, error: Error): Promise; $input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise; $createOrUpdate(params: TransferQuickInput): Promise; @@ -618,11 +618,11 @@ export interface MainThreadTelemetryShape extends IDisposable { } export interface MainThreadEditorInsetsShape extends IDisposable { - $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise; + $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewContentOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise; $disposeEditorInset(handle: number): void; $setHtml(handle: number, value: string): void; - $setOptions(handle: number, options: IWebviewOptions): void; + $setOptions(handle: number, options: IWebviewContentOptions): void; $postMessage(handle: number, value: any): Promise; } @@ -681,12 +681,12 @@ export interface IWebviewPortMapping { readonly extensionHostPort: number; } -export interface IWebviewOptions { +export interface IWebviewContentOptions { readonly enableScripts?: boolean; readonly enableForms?: boolean; readonly enableCommandUris?: boolean; - readonly localResourceRoots?: ReadonlyArray; - readonly portMapping?: ReadonlyArray; + readonly localResourceRoots?: readonly UriComponents[]; + readonly portMapping?: readonly IWebviewPortMapping[]; } export interface IWebviewPanelOptions { @@ -729,27 +729,34 @@ export interface WebviewMessageArrayBufferReference { export interface MainThreadWebviewsShape extends IDisposable { $setHtml(handle: WebviewHandle, value: string): void; - $setOptions(handle: WebviewHandle, options: IWebviewOptions): void; + $setOptions(handle: WebviewHandle, options: IWebviewContentOptions): void; $postMessage(handle: WebviewHandle, value: string, ...buffers: VSBuffer[]): Promise } +export interface IWebviewIconPath { + readonly light: UriComponents; + readonly dark: UriComponents; +} + +export interface IWebviewInitData { + readonly title: string; + readonly webviewOptions: IWebviewContentOptions; + readonly panelOptions: IWebviewPanelOptions; + readonly serializeBuffersForPostMessage: boolean; +} + export interface MainThreadWebviewPanelsShape extends IDisposable { $createWebviewPanel( extension: WebviewExtensionDescription, handle: WebviewHandle, viewType: string, - initData: { - title: string; - webviewOptions: IWebviewOptions; - panelOptions: IWebviewPanelOptions; - serializeBuffersForPostMessage: boolean; - }, + initData: IWebviewInitData, showOptions: WebviewPanelShowOptions, ): void; $disposeWebview(handle: WebviewHandle): void; $reveal(handle: WebviewHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewHandle, value: string): void; - $setIconPath(handle: WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void; + $setIconPath(handle: WebviewHandle, value: IWebviewIconPath | undefined): void; $registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void; $unregisterSerializer(viewType: string): void; @@ -796,7 +803,7 @@ export interface ExtHostWebviewPanelsShape { initData: { title: string; state: any; - webviewOptions: IWebviewOptions; + webviewOptions: IWebviewContentOptions; panelOptions: IWebviewPanelOptions; }, position: EditorGroupColumn, @@ -810,7 +817,7 @@ export interface ExtHostCustomEditorsShape { viewType: string, initData: { title: string; - webviewOptions: IWebviewOptions; + webviewOptions: IWebviewContentOptions; panelOptions: IWebviewPanelOptions; }, position: EditorGroupColumn, @@ -1096,7 +1103,6 @@ export interface MainThreadSCMShape extends IDisposable { $setInputBoxValue(sourceControlHandle: number, value: string): void; $setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void; $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void; - $setInputBoxFocus(sourceControlHandle: number): void; $showValidationMessage(sourceControlHandle: number, message: string | IMarkdownString, type: InputValidationType): void; $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void; } diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts index 35eb01c390c..5f9a06d01de 100644 --- a/src/vs/workbench/api/common/extHostCustomEditors.ts +++ b/src/vs/workbench/api/common/extHostCustomEditors.ts @@ -253,7 +253,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor viewType: string, initData: { title: string; - webviewOptions: extHostProtocol.IWebviewOptions; + webviewOptions: extHostProtocol.IWebviewContentOptions; panelOptions: extHostProtocol.IWebviewPanelOptions; }, position: EditorGroupColumn, diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index abd8d5c0b34..7be1359c2ec 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -17,11 +17,11 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { MissingExtensionDependency, ActivationKind } from 'vs/workbench/services/extensions/common/extensions'; +import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import * as errors from 'vs/base/common/errors'; import type * as vscode from 'vscode'; -import { checkProposedApiEnabled, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { VSBuffer } from 'vs/base/common/buffer'; import { ExtensionGlobalMemento, ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes'; @@ -447,7 +447,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return extension; }, get extensionRuntime() { - checkProposedApiEnabled(extensionDescription); + checkProposedApiEnabled(extensionDescription, 'extensionRuntime'); return that.extensionRuntime; }, get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); } diff --git a/src/vs/workbench/api/common/extHostFileSystem.ts b/src/vs/workbench/api/common/extHostFileSystem.ts index f759e84dc31..f66041d6c8e 100644 --- a/src/vs/workbench/api/common/extHostFileSystem.ts +++ b/src/vs/workbench/api/common/extHostFileSystem.ts @@ -15,7 +15,8 @@ import { State, StateMachine, LinkComputer, Edge } from 'vs/editor/common/modes/ import { commonPrefixLength } from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; import { VSBuffer } from 'vs/base/common/buffer'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; class FsLinkProvider { @@ -133,7 +134,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } } - registerFileSystemProvider(extension: ExtensionIdentifier, scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) { + registerFileSystemProvider(extension: IExtensionDescription, scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) { if (this._registeredSchemes.has(scheme)) { throw new Error(`a provider for the scheme '${scheme}' is already registered`); @@ -160,11 +161,12 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { if (typeof provider.open === 'function' && typeof provider.close === 'function' && typeof provider.read === 'function' && typeof provider.write === 'function' ) { + checkProposedApiEnabled(extension, 'fsChunks'); capabilities += files.FileSystemProviderCapabilities.FileOpenReadWriteClose; } this._proxy.$registerFileSystemProvider(handle, scheme, capabilities).catch(err => { - console.error(`FAILED to register filesystem provider of ${extension.value}-extension for the scheme ${scheme}`); + console.error(`FAILED to register filesystem provider of ${extension.identifier.value}-extension for the scheme ${scheme}`); console.error(err); }); diff --git a/src/vs/workbench/api/common/extHostLogService.ts b/src/vs/workbench/api/common/extHostLogService.ts index 553a4557e5a..e369d85eecf 100644 --- a/src/vs/workbench/api/common/extHostLogService.ts +++ b/src/vs/workbench/api/common/extHostLogService.ts @@ -5,6 +5,7 @@ import { ILoggerService, LogService } from 'vs/platform/log/common/log'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; export class ExtHostLogService extends LogService { @@ -14,7 +15,7 @@ export class ExtHostLogService extends LogService { @ILoggerService loggerService: ILoggerService, @IExtHostInitDataService initData: IExtHostInitDataService, ) { - super(loggerService.createLogger(initData.logFile)); + super(loggerService.createLogger(initData.logFile, { name: ExtensionHostLogFileName })); } } diff --git a/src/vs/workbench/api/common/extHostMessageService.ts b/src/vs/workbench/api/common/extHostMessageService.ts index d33e16ce3d7..be719110986 100644 --- a/src/vs/workbench/api/common/extHostMessageService.ts +++ b/src/vs/workbench/api/common/extHostMessageService.ts @@ -6,8 +6,9 @@ import Severity from 'vs/base/common/severity'; import type * as vscode from 'vscode'; import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol'; -import { checkProposedApiEnabled, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; function isMessageItem(item: any): item is vscode.MessageItem { return item && item.title; @@ -43,7 +44,7 @@ export class ExtHostMessageService { } if (options.useCustom) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); } const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = []; diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index b307608415c..75895d19dcf 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -10,7 +10,7 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { checkProposedApiEnabled, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto } from 'vs/workbench/api/common/extHost.protocol'; import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -21,6 +21,7 @@ import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConve import { NotebookCellOutput } from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri } from 'vs/workbench/api/common/shared/webview'; import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as vscode from 'vscode'; @@ -166,11 +167,11 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { _update(); }, get kind() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookControllerKind'); return data.kind ?? ''; }, set kind(value) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookControllerKind'); data.kind = value; _update(); }, @@ -233,11 +234,11 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { // --- ipc onDidReceiveMessage: onDidReceiveMessage.event, postMessage(message, editor) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookMessaging'); return that._proxy.$postMessage(handle, editor && that._extHostNotebook.getIdByEditor(editor), message); }, asWebviewUri(uri: URI) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookMessaging'); return asWebviewUri(uri, that._initData.remote); }, }; diff --git a/src/vs/workbench/api/common/extHostNotebookRenderers.ts b/src/vs/workbench/api/common/extHostNotebookRenderers.ts index 6e50023991c..3cc008e1010 100644 --- a/src/vs/workbench/api/common/extHostNotebookRenderers.ts +++ b/src/vs/workbench/api/common/extHostNotebookRenderers.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter } from 'vs/base/common/event'; -import { IExtensionManifest, isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtHostNotebookRenderersShape, IMainContext, MainContext, MainThreadNotebookRenderersShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { ExtHostNotebookEditor } from 'vs/workbench/api/common/extHostNotebookEditor'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; @@ -24,14 +25,14 @@ export class ExtHostNotebookRenderers implements ExtHostNotebookRenderersShape { this._rendererMessageEmitters.get(rendererId)?.fire({ editor: editor.apiEditor, message }); } - public createRendererMessaging(manifest: IExtensionManifest, rendererId: string): vscode.NotebookRendererMessaging { + public createRendererMessaging(manifest: IExtensionDescription, rendererId: string): vscode.NotebookRendererMessaging { if (!manifest.contributes?.notebookRenderer?.some(r => r.id === rendererId)) { throw new Error(`Extensions may only call createRendererMessaging() for renderers they contribute (got ${rendererId})`); } // In the stable API, the editor is given as an empty object, and this map // is used to maintain references. This can be removed after editor finalization. - const notebookEditorVisible = isProposedApiEnabled(manifest); + const notebookEditorVisible = isProposedApiEnabled(manifest, 'notebookEditor'); const notebookEditorAliases = new WeakMap<{}, vscode.NotebookEditor>(); const messaging: vscode.NotebookRendererMessaging = { diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index 551bbb17973..186031d7205 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { IExtensionDescription, checkProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogger, ILoggerService } from 'vs/platform/log/common/log'; import { OutputChannelUpdateMode } from 'vs/workbench/contrib/output/common/output'; import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; @@ -127,7 +127,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { this.channels.set(channel.id, channel); channel.visible = channel.id === this.visibleChannelId; }); - return this.createExtHostOutputChannel(name, extHostOutputChannel, extension); + return this.createExtHostOutputChannel(name, extHostOutputChannel); } private async doCreateOutputChannel(name: string, extension: IExtensionDescription): Promise { @@ -145,12 +145,9 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { return this.outputDirectoryPromise; } - private createExtHostOutputChannel(name: string, channelPromise: Promise, extensionDescription: IExtensionDescription): vscode.OutputChannel { + private createExtHostOutputChannel(name: string, channelPromise: Promise): vscode.OutputChannel { let disposed = false; - const validate = (checkProposedApi?: boolean) => { - if (checkProposedApi) { - checkProposedApiEnabled(extensionDescription); - } + const validate = () => { if (disposed) { throw new Error('Channel has been closed'); } @@ -170,7 +167,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { channelPromise.then(channel => channel.clear()); }, replace(value: string): void { - validate(true); + validate(); channelPromise.then(channel => channel.replace(value)); }, show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index f05cc14db02..f5ec6a46edc 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -10,7 +10,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace'; import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; -import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickPickItems, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol'; +import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickPickItem, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol'; import { URI } from 'vs/base/common/uri'; import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/common/extHostTypes'; import { isPromiseCanceledError } from 'vs/base/common/errors'; @@ -87,7 +87,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx return itemsPromise.then(items => { - const pickItems: TransferQuickPickItems[] = []; + const pickItems: TransferQuickPickItem[] = []; for (let handle = 0; handle < items.length; handle++) { const item = items[handle]; @@ -193,7 +193,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx // ---- QuickInput createQuickPick(extensionId: ExtensionIdentifier, enableProposedApi: boolean): QuickPick { - const session: ExtHostQuickPick = new ExtHostQuickPick(extensionId, enableProposedApi, () => this._sessions.delete(session._id)); + const session: ExtHostQuickPick = new ExtHostQuickPick(extensionId, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; } @@ -531,7 +531,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx private readonly _onDidChangeSelectionEmitter = new Emitter(); private readonly _onDidTriggerItemButtonEmitter = new Emitter>(); - constructor(extensionId: ExtensionIdentifier, private readonly enableProposedApi: boolean, onDispose: () => void) { + constructor(extensionId: ExtensionIdentifier, onDispose: () => void) { super(extensionId, onDispose); this._disposables.push( this._onDidChangeActiveEmitter, @@ -561,16 +561,13 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx detail: item.detail, picked: item.picked, alwaysShow: item.alwaysShow, - // Proposed API only at the moment - buttons: item.buttons && this.enableProposedApi - ? item.buttons.map((button, i) => { - return { - ...getIconPathOrClass(button), - tooltip: button.tooltip, - handle: i - }; - }) - : undefined, + buttons: item.buttons?.map((button, i) => { + return { + ...getIconPathOrClass(button), + tooltip: button.tooltip, + handle: i + }; + }), })) }); } diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 11aa457b084..574976beb09 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -16,11 +16,12 @@ import type * as vscode from 'vscode'; import { ISplice } from 'vs/base/common/sequence'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { checkProposedApiEnabled, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MarshalledId } from 'vs/base/common/marshalling'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type ProviderHandle = number; type GroupHandle = number; @@ -229,13 +230,13 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { private _validateInput: IValidateInput | undefined; get validateInput(): IValidateInput | undefined { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmValidation'); return this._validateInput; } set validateInput(fn: IValidateInput | undefined) { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmValidation'); if (fn && typeof fn !== 'function') { throw new Error(`[${this._extension.identifier.value}]: Invalid SCM input box validation function`); @@ -266,18 +267,8 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { // noop } - focus(): void { - checkProposedApiEnabled(this._extension); - - if (!this._visible) { - this.visible = true; - } - - this._proxy.$setInputBoxFocus(this._sourceControlHandle); - } - showValidationMessage(message: string | vscode.MarkdownString, type: vscode.SourceControlInputBoxValidationType) { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmValidation'); this._proxy.$showValidationMessage(this._sourceControlHandle, message, type as any); } @@ -511,11 +502,11 @@ class ExtHostSourceControl implements vscode.SourceControl { private _actionButtonDisposables = new MutableDisposable(); private _actionButton: vscode.Command | undefined; get actionButton(): vscode.Command | undefined { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmActionButton'); return this._actionButton; } set actionButton(actionButton: vscode.Command | undefined) { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmActionButton'); this._actionButtonDisposables.value = new DisposableStore(); this._actionButton = actionButton; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index fc80501f22c..4f193198645 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1776,7 +1776,7 @@ export enum TaskPanelKind { @es5ClassCompat export class TaskGroup implements vscode.TaskGroup { - isDefault?: boolean; + isDefault: boolean | undefined; private _id: string; public static Clean: TaskGroup = new TaskGroup('clean', 'Clean'); diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index d472ed4414d..2eb67a7bf10 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -176,7 +176,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`); } - public createNewWebview(handle: string, options: extHostProtocol.IWebviewOptions, extension: IExtensionDescription): ExtHostWebview { + public createNewWebview(handle: string, options: extHostProtocol.IWebviewContentOptions, extension: IExtensionDescription): ExtHostWebview { const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService); this._webviews.set(handle, webview); @@ -202,7 +202,7 @@ export function serializeWebviewOptions( extension: IExtensionDescription, workspace: IExtHostWorkspace | undefined, options: vscode.WebviewOptions, -): extHostProtocol.IWebviewOptions { +): extHostProtocol.IWebviewContentOptions { return { enableCommandUris: options.enableCommandUris, enableScripts: options.enableScripts, @@ -212,7 +212,7 @@ export function serializeWebviewOptions( }; } -export function reviveOptions(options: extHostProtocol.IWebviewOptions): vscode.WebviewOptions { +export function reviveOptions(options: extHostProtocol.IWebviewContentOptions): vscode.WebviewOptions { return { enableCommandUris: options.enableCommandUris, enableScripts: options.enableScripts, diff --git a/src/vs/workbench/api/common/extHostWebviewPanels.ts b/src/vs/workbench/api/common/extHostWebviewPanels.ts index c76c364c392..188bda392c1 100644 --- a/src/vs/workbench/api/common/extHostWebviewPanels.ts +++ b/src/vs/workbench/api/common/extHostWebviewPanels.ts @@ -281,7 +281,7 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel initData: { title: string; state: any; - webviewOptions: extHostProtocol.IWebviewOptions; + webviewOptions: extHostProtocol.IWebviewContentOptions; panelOptions: extHostProtocol.IWebviewPanelOptions; }, position: EditorGroupColumn diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 29e02c1c08b..5ee2881e6f8 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -16,7 +16,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Iterable } from 'vs/base/common/iterator'; import { index } from 'vs/base/common/arrays'; -import { isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; interface IAPIMenu { readonly key: string; @@ -634,7 +634,7 @@ commandsExtensionPoint.setHandler(extensions => { title, source: extension.description.displayName ?? extension.description.name, shortTitle, - tooltip: isProposedApiEnabled(extension.description) ? title : undefined, + tooltip: isProposedApiEnabled(extension.description, undefined) ? title : undefined, category, precondition: ContextKeyExpr.deserialize(enablement), icon: absoluteIcon @@ -764,7 +764,7 @@ menusExtensionPoint.setHandler(extensions => { return; } - if (menu.proposed && !isProposedApiEnabled(extension.description)) { + if (menu.proposed && !isProposedApiEnabled(extension.description, undefined)) { collector.error(localize('proposedAPI.invalid', "{0} is a proposed menu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value)); return; } diff --git a/src/vs/workbench/api/node/extHostLoggerService.ts b/src/vs/workbench/api/node/extHostLoggerService.ts index 5a4a0800476..67d9d52e055 100644 --- a/src/vs/workbench/api/node/extHostLoggerService.ts +++ b/src/vs/workbench/api/node/extHostLoggerService.ts @@ -14,11 +14,7 @@ export class ExtHostLoggerService extends BaseExtHostLoggerService { protected override doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { if (resource.scheme === Schemas.file) { - const logger = new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, logLevel); - if (options?.donotUseFormatters) { - (logger).clearFormatters(); - } - return logger; + return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel); } return super.doCreateLogger(resource, logLevel, options); } diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 04cc1be035b..17a1962f940 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -59,11 +59,15 @@ export class ToggleActivityBarVisibilityAction extends Action2 { category: CATEGORIES.View, f1: true, toggled: ContextKeyExpr.equals('config.workbench.activityBar.visible', true), - menu: { + menu: [{ id: MenuId.MenubarAppearanceMenu, group: '2_workbench_layout', order: 4 - } + }, { + id: MenuId.LayoutControlMenu, + group: '0_workbench_layout', + order: 3 + }] }); } @@ -226,11 +230,15 @@ registerAction2(class extends Action2 { category: CATEGORIES.View, f1: true, toggled: EditorAreaVisibleContext, - menu: { + menu: [{ id: MenuId.MenubarAppearanceMenu, group: '2_workbench_layout', order: 5 - } + }, { + id: MenuId.LayoutControlMenu, + group: '0_workbench_layout', + order: 5 + }] }); } @@ -274,40 +282,53 @@ class ToggleSidebarVisibilityAction extends Action2 { registerAction2(ToggleSidebarVisibilityAction); -MenuRegistry.appendMenuItems([{ - id: MenuId.ViewContainerTitleContext, - item: { - group: '3_workbench_layout_move', - command: { - id: ToggleSidebarVisibilityAction.ID, - title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"), - }, - when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), - order: 2 - } -}, { - id: MenuId.ViewTitleContext, - item: { - group: '3_workbench_layout_move', - command: { - id: ToggleSidebarVisibilityAction.ID, - title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"), - }, - when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), - order: 2 - } -}, { - id: MenuId.MenubarAppearanceMenu, - item: { - group: '2_workbench_layout', - command: { - id: ToggleSidebarVisibilityAction.ID, - title: localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"), - toggled: SideBarVisibleContext - }, - order: 1 +MenuRegistry.appendMenuItems([ + { + id: MenuId.ViewContainerTitleContext, + item: { + group: '3_workbench_layout_move', + command: { + id: ToggleSidebarVisibilityAction.ID, + title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"), + }, + when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), + order: 2 + } + }, { + id: MenuId.ViewTitleContext, + item: { + group: '3_workbench_layout_move', + command: { + id: ToggleSidebarVisibilityAction.ID, + title: localize('compositePart.hideSideBarLabel', "Hide Side Bar"), + }, + when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), + order: 2 + } + }, { + id: MenuId.MenubarAppearanceMenu, + item: { + group: '2_workbench_layout', + command: { + id: ToggleSidebarVisibilityAction.ID, + title: localize({ key: 'miShowSidebar', comment: ['&& denotes a mnemonic'] }, "Show &&Side Bar"), + toggled: SideBarVisibleContext + }, + order: 1 + } + }, { + id: MenuId.LayoutControlMenu, + item: { + group: '0_workbench_layout', + command: { + id: ToggleSidebarVisibilityAction.ID, + title: localize('miShowSidebarNoMnnemonic', "Show Side Bar"), + toggled: SideBarVisibleContext + }, + order: 0 + } } -}]); +]); // --- Toggle Statusbar Visibility @@ -328,11 +349,15 @@ export class ToggleStatusbarVisibilityAction extends Action2 { category: CATEGORIES.View, f1: true, toggled: ContextKeyExpr.equals('config.workbench.statusBar.visible', true), - menu: { + menu: [{ id: MenuId.MenubarAppearanceMenu, group: '2_workbench_layout', order: 3 - } + }, { + id: MenuId.LayoutControlMenu, + group: '0_workbench_layout', + order: 1 + }] }); } diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts index c94724f8f2d..582f2b0abc3 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts @@ -9,7 +9,7 @@ import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES, Extensions as WorkbenchExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { ActiveAuxiliaryContext, AuxiliaryBarVisibleContext } from 'vs/workbench/common/auxiliarybar'; +import { AuxiliaryBarVisibleContext } from 'vs/workbench/common/auxiliarybar'; import { ViewContainerLocation, ViewContainerLocationToString } from 'vs/workbench/common/views'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -62,6 +62,19 @@ class FocusAuxiliaryBarAction extends Action { } MenuRegistry.appendMenuItems([ + { + id: MenuId.LayoutControlMenu, + item: { + group: '0_workbench_layout', + command: { + id: ToggleAuxiliaryBarAction.ID, + title: localize({ key: 'miShowAuxiliaryBar', comment: ['&& denotes a mnemonic'] }, "Show Si&&de Panel"), + toggled: AuxiliaryBarVisibleContext + }, + when: ContextKeyExpr.equals('config.workbench.experimental.sidePanel.enabled', true), + order: 4 + } + }, { id: MenuId.MenubarAppearanceMenu, item: { @@ -69,7 +82,7 @@ MenuRegistry.appendMenuItems([ command: { id: ToggleAuxiliaryBarAction.ID, title: localize({ key: 'miShowAuxiliaryBar', comment: ['&& denotes a mnemonic'] }, "Show Si&&de Panel"), - toggled: ActiveAuxiliaryContext + toggled: AuxiliaryBarVisibleContext }, when: ContextKeyExpr.equals('config.workbench.experimental.sidePanel.enabled', true), order: 5 diff --git a/src/vs/workbench/browser/parts/banner/bannerPart.ts b/src/vs/workbench/browser/parts/banner/bannerPart.ts index c6fba558bae..0de6530c073 100644 --- a/src/vs/workbench/browser/parts/banner/bannerPart.ts +++ b/src/vs/workbench/browser/parts/banner/bannerPart.ts @@ -7,11 +7,11 @@ import 'vs/css!./media/bannerpart'; import { localize } from 'vs/nls'; import { $, addDisposableListener, append, asCSSUrl, clearNode, EventType } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Part } from 'vs/workbench/browser/part'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { Action } from 'vs/base/common/actions'; @@ -27,12 +27,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyCode } from 'vs/base/common/keyCodes'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; - - -// Icons - -const bannerCloseIcon = registerCodicon('banner-close', Codicon.close); - +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; // Theme support @@ -257,7 +252,7 @@ export class BannerPart extends Part implements IBannerService { // Action const actionBarContainer = append(this.element, $('div.action-container')); this.actionBar = this._register(new ActionBar(actionBarContainer)); - const closeAction = this._register(new Action('banner.close', 'Close Banner', bannerCloseIcon.classNames, true, () => this.close(item))); + const closeAction = this._register(new Action('banner.close', 'Close Banner', ThemeIcon.asClassName(widgetClose), true, () => this.close(item))); this.actionBar.push(closeAction, { icon: true, label: false }); this.actionBar.setFocusable(false); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index fcfea9b352d..6f08ebfa1e7 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -41,6 +41,8 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { IOutline } from 'vs/workbench/services/outline/browser/outline'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { Codicon } from 'vs/base/common/codicons'; class OutlineItem extends BreadcrumbsItem { @@ -150,6 +152,8 @@ export interface IBreadcrumbsControlOptions { showPlaceholder: boolean; } +const separatorIcon = registerIcon('breadcrumb-separator', Codicon.chevronRight, localize('separatorIcon', 'Icon for the separator in the breadcrumbs.')); + export class BreadcrumbsControl { static readonly HEIGHT = 22; @@ -208,7 +212,7 @@ export class BreadcrumbsControl { this._cfTitleScrollbarSizing = BreadcrumbsConfig.TitleScrollbarSizing.bindTo(configurationService); const sizing = this._cfTitleScrollbarSizing.getValue() ?? 'default'; - this._widget = new BreadcrumbsWidget(this.domNode, BreadcrumbsControl.SCROLLBAR_SIZES[sizing]); + this._widget = new BreadcrumbsWidget(this.domNode, BreadcrumbsControl.SCROLLBAR_SIZES[sizing], separatorIcon); this._widget.onDidSelectItem(this._onSelectEvent, this, this._disposables); this._widget.onDidFocusItem(this._onFocusEvent, this, this._disposables); this._widget.onDidChangeFocus(this._updateCkBreadcrumbsActive, this, this._disposables); diff --git a/src/vs/workbench/browser/parts/editor/editorPanes.ts b/src/vs/workbench/browser/parts/editor/editorPanes.ts index 13e9fbc0e8d..86562c72954 100644 --- a/src/vs/workbench/browser/parts/editor/editorPanes.ts +++ b/src/vs/workbench/browser/parts/editor/editorPanes.ts @@ -12,7 +12,7 @@ import { IEditorPaneRegistry, IEditorPaneDescriptor } from 'vs/workbench/browser import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorProgressService, IOperation, LongRunningOperation } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress'; import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; import { Emitter } from 'vs/base/common/event'; import { assertIsDefined } from 'vs/base/common/types'; @@ -146,20 +146,8 @@ export class EditorPanes extends Disposable { // Editor pane const pane = this.doShowEditorPane(descriptor); - // Show progress while setting input after a certain timeout. - // If the workbench is opening be more relaxed about progress - // showing by increasing the delay a little bit to reduce flicker. - const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200); - // Apply input to pane - let changed: boolean; - let cancelled: boolean; - try { - changed = await this.doSetInput(pane, operation, editor, options, context); - cancelled = !operation.isCurrent(); - } finally { - operation.stop(); - } + const { changed, cancelled } = await this.doSetInput(pane, editor, options, context); // Focus unless cancelled if (!cancelled) { @@ -263,22 +251,36 @@ export class EditorPanes extends Disposable { this._onDidChangeSizeConstraints.fire(undefined); } - private async doSetInput(editorPane: EditorPane, operation: IOperation, editor: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext): Promise { - const forceReload = options?.forceReload; - const inputMatches = editorPane.input?.matches(editor); + private async doSetInput(editorPane: EditorPane, editor: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext): Promise<{ changed: boolean, cancelled: boolean }> { - // If the input did not change, return early and only apply the options - // unless the options instruct us to force open it even if it is the same - if (inputMatches && !forceReload) { + // If the input did not change, return early and only + // apply the options unless the options instruct us to + // force open it even if it is the same + const inputMatches = editorPane.input?.matches(editor); + if (inputMatches && !options?.forceReload) { editorPane.setOptions(options); + + return { changed: false, cancelled: false }; } - // Otherwise set the input to the editor pane - else { + // Start a new editor input operation to report progress + // and to support cancellation. Any new operation that is + // started will cancel the previous one. + const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200); + + // Set the input to the editor pane + let cancelled = false; + try { await editorPane.setInput(editor, options, context, operation.token); + + if (!operation.isCurrent()) { + cancelled = true; + } + } finally { + operation.stop(); } - return !inputMatches; + return { changed: !inputMatches, cancelled }; } private doHideActiveEditorPane(): void { diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index b33393a5192..d80ef752be3 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -18,8 +18,9 @@ import { ViewContainerLocation, ViewContainerLocations } from 'vs/workbench/comm import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { IDisposable } from 'vs/workbench/workbench.web.api'; +import { IView } from 'vs/base/browser/ui/grid/grid'; -export interface IPaneCompositePart { +export interface IPaneCompositePart extends IView { readonly onDidPaneCompositeOpen: Event; readonly onDidPaneCompositeClose: Event; diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 730c4fa5e6e..c7e4a15d89e 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -298,6 +298,17 @@ MenuRegistry.appendMenuItems([ }, order: 5 } + }, { + id: MenuId.LayoutControlMenu, + item: { + group: '0_workbench_layout', + command: { + id: TogglePanelAction.ID, + title: localize({ key: 'miShowPanel', comment: ['&& denotes a mnemonic'] }, "Show &&Panel"), + toggled: ActivePanelContext + }, + order: 4 + } }, { id: MenuId.ViewTitleContext, item: { diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 7ac774cb7c6..5daf455db32 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -132,11 +132,34 @@ margin-left: auto; } +.monaco-workbench.mac:not(web) .part.titlebar > .window-controls-container { + position: absolute; + right: 0px; + width: 28px; + display: none; +} + +.monaco-workbench.mac:not(web) .part.titlebar > .window-controls-container.show-layout-control { + display: flex; +} + .monaco-workbench.fullscreen .part.titlebar > .window-controls-container { display: none; background-color: transparent; } +.monaco-workbench .part.titlebar > .window-controls-container.show-layout-control { + width: 160px; +} + +.monaco-workbench .part.titlebar > .window-controls-container > .layout-dropdown-container { + display: none; +} + +.monaco-workbench .part.titlebar > .window-controls-container.show-layout-control > .layout-dropdown-container { + display: inline-block; +} + .monaco-workbench .part.titlebar > .window-controls-container > .window-icon { display: inline-block; line-height: 30px; diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 1cec4e03d11..07a549d5e19 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -8,7 +8,7 @@ import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; +import { IAction, Action, SubmenuAction, Separator, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform'; @@ -38,6 +38,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IsMacNativeContext, IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export type IOpenRecentAction = IAction & { uri: URI, remoteAuthority?: string }; @@ -374,6 +375,7 @@ export class CustomMenubarControl extends MenubarControl { private alwaysOnMnemonics: boolean = false; private focusInsideMenubar: boolean = false; private visible: boolean = true; + private actionRunner: IActionRunner; private readonly webNavigationMenu = this._register(this.menuService.createMenu(MenuId.MenubarHomeMenu, this.contextKeyService)); private readonly _onVisibilityChange: Emitter; @@ -394,6 +396,7 @@ export class CustomMenubarControl extends MenubarControl { @IAccessibilityService accessibilityService: IAccessibilityService, @IThemeService private readonly themeService: IThemeService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, + @ITelemetryService private readonly telemetryService: ITelemetryService, @IHostService hostService: IHostService, @ICommandService commandService: ICommandService ) { @@ -402,6 +405,11 @@ export class CustomMenubarControl extends MenubarControl { this._onVisibilityChange = this._register(new Emitter()); this._onFocusStateChange = this._register(new Emitter()); + this.actionRunner = this._register(new ActionRunner()); + this.actionRunner.onDidRun(e => { + this.telemetryService.publicLog2('workbenchActionExecuted', { id: e.action.id, from: 'menu' }); + }); + this.workspacesService.getRecentlyOpened().then((recentlyOpened) => { this.recentlyOpened = recentlyOpened; }); @@ -811,6 +819,7 @@ export class CustomMenubarControl extends MenubarControl { enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, + actionRunner: this.actionRunner, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode, diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 1ea435e9477..48b0dfd7910 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -12,7 +12,7 @@ import { getZoomFactor } from 'vs/base/browser/browser'; import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, SubmenuAction } from 'vs/base/common/actions'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; @@ -28,14 +28,14 @@ import { trim } from 'vs/base/common/strings'; import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend } from 'vs/base/browser/dom'; import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { template } from 'vs/base/common/labels'; +import { mnemonicButtonLabel, template } from 'vs/base/common/labels'; import { ILabelService } from 'vs/platform/label/common/label'; import { Emitter } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { RunOnceScheduler } from 'vs/base/common/async'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; +import { IMenuService, IMenu, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -43,6 +43,10 @@ import { Schemas } from 'vs/base/common/network'; import { withNullAsUndefined } from 'vs/base/common/types'; import { Codicon, iconRegistry } from 'vs/base/common/codicons'; import { getVirtualWorkspaceLocation } from 'vs/platform/remote/common/remoteHosts'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export class TitlebarPart extends Part implements ITitleService { @@ -66,10 +70,13 @@ export class TitlebarPart extends Part implements ITitleService { declare readonly _serviceBrand: undefined; protected title!: HTMLElement; + protected customMenubar: CustomMenubarControl | undefined; protected appIcon: HTMLElement | undefined; private appIconBadge: HTMLElement | undefined; protected menubar?: HTMLElement; + protected windowControls: HTMLElement | undefined; + private layoutToolbar: ActionBar | undefined; protected lastLayoutDimensions: Dimension | undefined; private titleBarStyle: 'native' | 'custom'; @@ -91,12 +98,13 @@ export class TitlebarPart extends Part implements ITitleService { @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IKeybindingService private readonly keybindingService: IKeybindingService, @IThemeService themeService: IThemeService, @ILabelService private readonly labelService: ILabelService, @IStorageService storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IMenuService menuService: IMenuService, - @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService private readonly menuService: IMenuService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @IHostService private readonly hostService: IHostService, @IProductService private readonly productService: IProductService, ) { @@ -143,6 +151,10 @@ export class TitlebarPart extends Part implements ITitleService { } } } + + if (this.titleBarStyle !== 'native' && this.windowControls && event.affectsConfiguration('workbench.experimental.layoutControl.enabled')) { + this.windowControls.classList.toggle('show-layout-control', this.layoutControlEnabled); + } } protected onMenubarVisibilityChanged(visible: boolean): void { @@ -395,6 +407,53 @@ export class TitlebarPart extends Part implements ITitleService { this.titleUpdater.schedule(); } + if (this.titleBarStyle !== 'native') { + this.windowControls = append(this.element, $('div.window-controls-container')); + this.windowControls.classList.toggle('show-layout-control', this.layoutControlEnabled); + + const layoutDropdownContainer = append(this.windowControls, $('div.layout-dropdown-container')); + this.layoutToolbar = new ActionBar(layoutDropdownContainer, + { + ariaLabel: localize('layoutMenu', "Configure Layout"), + actionViewItemProvider: action => { + if (action instanceof SubmenuAction) { + return new DropdownMenuActionViewItem(action, action.actions, this.contextMenuService, { + classNames: Codicon.editorLayout.classNamesArray, + anchorAlignmentProvider: () => AnchorAlignment.RIGHT, + keybindingProvider: action => this.keybindingService.lookupKeybinding(action.id) + }); + } + return undefined; + } + }); + + + const menu = this._register(this.menuService.createMenu(MenuId.LayoutControlMenu, this.contextKeyService)); + const updateLayoutMenu = () => { + if (!this.layoutToolbar) { + return; + } + + const actions: IAction[] = []; + const toDispose = createAndFillInContextMenuActions(menu, undefined, { primary: [], secondary: actions }); + + this.layoutToolbar.clear(); + this.layoutToolbar.push(new SubmenuAction('stenir', localize('layoutMenu', "Configure Layout"), actions.map(action => { + if (action instanceof MenuItemAction) { + (action as IAction).label = mnemonicButtonLabel(typeof action.item.title === 'string' + ? action.item.title + : action.item.title.mnemonicTitle ?? action.item.title.value, true); + } + return action; + }))); + + toDispose.dispose(); + }; + + menu.onDidChange(updateLayoutMenu); + updateLayoutMenu(); + } + // Context menu on title [EventType.CONTEXT_MENU, EventType.MOUSE_DOWN].forEach(event => { this._register(addDisposableListener(this.title, event, e => { @@ -413,6 +472,10 @@ export class TitlebarPart extends Part implements ITitleService { return; } + if (e.target && this.layoutToolbar && isAncestor(e.target as HTMLElement, this.layoutToolbar.getContainer())) { + return; + } + const active = document.activeElement; setTimeout(() => { if (active instanceof HTMLElement) { @@ -507,6 +570,10 @@ export class TitlebarPart extends Part implements ITitleService { return getMenuBarVisibility(this.configurationService); } + private get layoutControlEnabled(): boolean { + return this.configurationService.getValue('workbench.experimental.layoutControl.enabled'); + } + updateLayout(dimension: Dimension): void { this.lastLayoutDimensions = dimension; diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index 6971905465e..d7472ac31b2 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/style'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground, toolbarHoverBackground, toolbarActiveBackground, toolbarHoverOutline, listFocusHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; +import { iconForeground, foreground, selectionBackground, focusBorder, listHighlightForeground, inputPlaceholderForeground, toolbarHoverBackground, toolbarActiveBackground, toolbarHoverOutline, listFocusHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; import { WORKBENCH_BACKGROUND, TITLE_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; import { isWeb, isIOS, isMacintosh, isWindows } from 'vs/base/common/platform'; import { createMetaElement } from 'vs/base/browser/dom'; @@ -71,51 +71,6 @@ registerThemingParticipant((theme, collector) => { `); } - // Scrollbars - const scrollbarShadowColor = theme.getColor(scrollbarShadow); - if (scrollbarShadowColor) { - collector.addRule(` - .monaco-workbench .monaco-scrollable-element > .shadow.top { - box-shadow: ${scrollbarShadowColor} 0 6px 6px -6px inset; - } - - .monaco-workbench .monaco-scrollable-element > .shadow.left { - box-shadow: ${scrollbarShadowColor} 6px 0 6px -6px inset; - } - - .monaco-workbench .monaco-scrollable-element > .shadow.top.left { - box-shadow: ${scrollbarShadowColor} 6px 6px 6px -6px inset; - } - `); - } - - const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground); - if (scrollbarSliderBackgroundColor) { - collector.addRule(` - .monaco-workbench .monaco-scrollable-element > .scrollbar > .slider { - background: ${scrollbarSliderBackgroundColor}; - } - `); - } - - const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground); - if (scrollbarSliderHoverBackgroundColor) { - collector.addRule(` - .monaco-workbench .monaco-scrollable-element > .scrollbar > .slider:hover { - background: ${scrollbarSliderHoverBackgroundColor}; - } - `); - } - - const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground); - if (scrollbarSliderActiveBackgroundColor) { - collector.addRule(` - .monaco-workbench .monaco-scrollable-element > .scrollbar > .slider.active { - background: ${scrollbarSliderActiveBackgroundColor}; - } - `); - } - // Focus outline const focusOutline = theme.getColor(focusBorder); if (focusOutline) { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 202807796b0..74a8e47247f 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -356,6 +356,11 @@ const registry = Registry.as(ConfigurationExtensions.Con // On Mac, the delay is 1500. 'default': isMacintosh ? 1500 : 500 }, + 'workbench.experimental.layoutControl.enabled': { + 'type': 'boolean', + 'default': product.quality !== 'stable', + 'description': localize('layoutControlEnabled', "Controls whether the layout control button in the custom title bar is enabled."), + }, 'workbench.experimental.sidePanel.enabled': { 'type': 'boolean', 'default': false, diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts index 8f001dfd343..6a0adba6911 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts @@ -553,7 +553,7 @@ class TextEditElementTemplate { this._icon = document.createElement('div'); container.appendChild(this._icon); - this._label = new HighlightedLabel(container, false); + this._label = new HighlightedLabel(container); } dispose(): void { @@ -582,8 +582,8 @@ class TextEditElementTemplate { value += element.inserting; value += element.suffix; - let selectHighlight: IHighlight = { start: element.prefix.length, end: element.prefix.length + element.selecting.length, extraClasses: 'remove' }; - let insertHighlight: IHighlight = { start: selectHighlight.end, end: selectHighlight.end + element.inserting.length, extraClasses: 'insert' }; + let selectHighlight: IHighlight = { start: element.prefix.length, end: element.prefix.length + element.selecting.length, extraClasses: ['remove'] }; + let insertHighlight: IHighlight = { start: selectHighlight.end, end: selectHighlight.end + element.inserting.length, extraClasses: ['insert'] }; let title: string | undefined; let { metadata } = element.edit.textEdit; diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts index b81f646d3c1..73808153cd7 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts @@ -5,20 +5,20 @@ import { localize } from 'vs/nls'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -class InspectKeyMap extends EditorAction { +class InspectKeyMap extends Action2 { constructor() { super({ id: 'workbench.action.inspectKeyMappings', - label: localize('workbench.action.inspectKeyMap', "Developer: Inspect Key Mappings"), - alias: 'Developer: Inspect Key Mappings', - precondition: undefined + title: { value: localize('workbench.action.inspectKeyMap', "Inspect Key Mappings"), original: 'Inspect Key Mappings' }, + category: CATEGORIES.Developer, + f1: true }); } @@ -30,7 +30,7 @@ class InspectKeyMap extends EditorAction { } } -registerEditorAction(InspectKeyMap); +registerAction2(InspectKeyMap); class InspectKeyMapJSON extends Action2 { diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts index 58a2fb2b98f..2f34a0ee93c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts @@ -98,7 +98,7 @@ export class DocumentSymbolGroupRenderer implements ITreeRenderer, _index: number, template: DocumentSymbolGroupTemplate): void { diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 47ec578504a..b8b5f67b4bf 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -149,7 +149,7 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer { if (debugIconBreakpointCurrentStackframeForegroundColor) { collector.addRule(` .monaco-workbench ${ThemeIcon.asCSSSelector(icons.debugStackframe)}, - .monaco-editor .debug-top-stack-frame-column::before { + .monaco-editor .debug-top-stack-frame-column { color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important; } `); diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index 5f69a93eeb5..1c5c4ebd16d 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -17,7 +17,6 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { distinct } from 'vs/base/common/arrays'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; -import { noBreakWhitespace } from 'vs/base/common/strings'; export const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#ffff0033' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); export const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#7abd7a4d' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); @@ -82,7 +81,7 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, isFocuse options: { description: 'top-stack-frame-inline-decoration', before: { - content: noBreakWhitespace, + content: '\uEB8B', inlineClassName: noCharactersBefore ? 'debug-top-stack-frame-column start-of-line' : 'debug-top-stack-frame-column', inlineClassNameAffectsLetterSpacing: true }, diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 39562f28c91..a1e860f8336 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -513,7 +513,7 @@ class SessionsRenderer implements ICompressibleTreeRenderer { if (action instanceof MenuItemAction) { @@ -600,7 +600,7 @@ class ThreadsRenderer implements ICompressibleTreeRenderer { let expression = $('.'); let name = $('.'); let value = $('.'); - let label = new HighlightedLabel(name, false); + let label = new HighlightedLabel(name); renderVariable(variable, { expression, name, value, label }, false, []); assert.strictEqual(label.element.textContent, 'foo'); diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 2737127f12b..ecf29d5e9c8 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -227,12 +227,13 @@ export class ExperimentService extends Disposable implements IExperimentService } protected async getExperiments(): Promise { - if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { + const experimentsUrl = this.configurationService.getValue('_workbench.experimentsUrl') || this.productService.experimentsUrl; + if (!experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { return []; } try { - const context = await this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None); + const context = await this.requestService.request({ type: 'GET', url: experimentsUrl }, CancellationToken.None); if (context.res.statusCode !== 200) { return null; } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 13fd34a28e2..d1ca6333c4d 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -396,6 +396,12 @@ cursor: pointer; } +.extension-editor > .body > .content > .details > .additional-details-container .resources-container > .resources > .resource { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + .extension-editor > .body > .content > .details > .additional-details-container .resources-container > .resources > .resource:hover { text-decoration: underline; } diff --git a/src/vs/workbench/contrib/externalUriOpener/common/contributedOpeners.ts b/src/vs/workbench/contrib/externalUriOpener/common/contributedOpeners.ts index 7c7ec29e9b9..24a9f06dcee 100644 --- a/src/vs/workbench/contrib/externalUriOpener/common/contributedOpeners.ts +++ b/src/vs/workbench/contrib/externalUriOpener/common/contributedOpeners.ts @@ -19,8 +19,6 @@ interface OpenersMemento { [id: string]: RegisteredExternalOpener; } -/** - */ export class ContributedExternalUriOpenersStore extends Disposable { private static readonly STORAGE_ID = 'externalUriOpeners'; @@ -37,8 +35,8 @@ export class ContributedExternalUriOpenersStore extends Disposable { this._memento = new Memento(ContributedExternalUriOpenersStore.STORAGE_ID, storageService); this._mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); - for (const id of Object.keys(this._mementoObject || {})) { - this.add(id, this._mementoObject[id].extensionId, { isCurrentlyRegistered: false }); + for (const [id, value] of Object.entries(this._mementoObject || {})) { + this.add(id, value.extensionId, { isCurrentlyRegistered: false }); } this.invalidateOpenersOnExtensionsChanged(); diff --git a/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts b/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts index 90f7bba43b4..68fc90c3a16 100644 --- a/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts +++ b/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts @@ -90,7 +90,7 @@ export class WorkspaceWatcher extends Disposable { else if (msg.indexOf('EUNKNOWN') >= 0) { this.notificationService.prompt( Severity.Warning, - localize('eshutdownError', "File changes watcher stopped unexpectedly. Please reload the window to enable the watcher again."), + localize('eshutdownError', "File changes watcher stopped unexpectedly. A reload of the window may enable the watcher again unless the workspace cannot be watched for file changes."), [{ label: localize('reload', "Reload"), run: () => this.hostService.reload() diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index 6ffea967e57..3117b72ae95 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -258,7 +258,12 @@ export class InteractiveEditor extends EditorPane { cellExecuteToolbar: MenuId.InteractiveCellExecute, cellExecutePrimary: undefined }, - cellEditorContributions: [], + cellEditorContributions: EditorExtensionsRegistry.getSomeEditorContributions([ + SelectionClipboardContributionID, + ContextMenuController.ID, + ModesHoverController.ID, + MarkerController.ID + ]), options: this.#notebookOptions }); @@ -270,6 +275,9 @@ export class InteractiveEditor extends EditorPane { top: INPUT_EDITOR_PADDING, bottom: INPUT_EDITOR_PADDING }, + hover: { + enabled: true + } } }, { ...{ diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 545dfa31ca6..71baebee2e4 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -361,7 +361,7 @@ class MarkerWidget extends Disposable { for (let index = 0; index < (multiline ? lines.length : 1); index++) { lastLineElement = dom.append(this.messageAndDetailsContainer, dom.$('.marker-message-line')); const messageElement = dom.append(lastLineElement, dom.$('.marker-message')); - const highlightedLabel = new HighlightedLabel(messageElement, false); + const highlightedLabel = new HighlightedLabel(messageElement); highlightedLabel.set(lines[index].length > 1000 ? `${lines[index].substring(0, 1000)}...` : lines[index], lineMatches[index]); if (lines[index] === '') { lastLineElement.style.height = `${VirtualDelegate.LINE_HEIGHT}px`; @@ -374,19 +374,19 @@ class MarkerWidget extends Disposable { parent.classList.add('details-container'); if (marker.source || marker.code) { - const source = new HighlightedLabel(dom.append(parent, dom.$('.marker-source')), false); + const source = new HighlightedLabel(dom.append(parent, dom.$('.marker-source'))); const sourceMatches = filterData && filterData.sourceMatches || []; source.set(marker.source, sourceMatches); if (marker.code) { if (typeof marker.code === 'string') { - const code = new HighlightedLabel(dom.append(parent, dom.$('.marker-code')), false); + const code = new HighlightedLabel(dom.append(parent, dom.$('.marker-code'))); const codeMatches = filterData && filterData.codeMatches || []; code.set(marker.code, codeMatches); } else { // TODO@sandeep: these widgets should be disposed const container = dom.$('.marker-code'); - const code = new HighlightedLabel(container, false); + const code = new HighlightedLabel(container); new Link(parent, { href: marker.code.target.toString(), label: container, title: marker.code.target.toString() }, undefined, this._openerService); const codeMatches = filterData && filterData.codeMatches || []; code.set(marker.code.value, codeMatches); @@ -414,14 +414,14 @@ export class RelatedInformationRenderer implements ITreeRenderer { hasOpenedNotebook.set(true); @@ -83,7 +83,7 @@ registerAction2(class NotebookClearNotebookLayoutAction extends Action2 { id: 'workbench.notebook.layout.gettingStarted', title: localize('workbench.notebook.layout.gettingStarted.label', "Reset notebook getting started"), f1: true, - precondition: ContextKeyExpr.equals(`config.${OpenGettingStarted}`, true), + precondition: ContextKeyExpr.equals(`config.${NotebookSetting.openGettingStarted}`, true), category: CATEGORIES.Developer, }); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions.ts index c764085038f..fe1c2706c22 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions.ts @@ -8,7 +8,7 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { INotebookActionContext, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { CellToolbarLocation } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const TOGGLE_CELL_TOOLBAR_POSITION = 'notebook.toggleCellToolbarPosition'; @@ -33,9 +33,9 @@ export class ToggleCellToolbarPositionAction extends Action2 { // from toolbar const viewType = editor.textModel.viewType; const configurationService = accessor.get(IConfigurationService); - const toolbarPosition = configurationService.getValue(CellToolbarLocation); + const toolbarPosition = configurationService.getValue(NotebookSetting.cellToolbarLocation); const newConfig = this.togglePosition(viewType, toolbarPosition); - await configurationService.updateValue(CellToolbarLocation, newConfig); + await configurationService.updateValue(NotebookSetting.cellToolbarLocation, newConfig); } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts b/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts index 090e1383270..350e3c7e7d1 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile.ts @@ -9,7 +9,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { CellToolbarLocation, CompactView, ConsolidatedRunButton, FocusIndicator, GlobalToolbar, InsertToolbarLocation, ShowCellStatusBar, UndoRedoPerCell } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -22,34 +22,34 @@ export enum NotebookProfileType { const profiles = { [NotebookProfileType.default]: { - [FocusIndicator]: 'gutter', - [InsertToolbarLocation]: 'both', - [GlobalToolbar]: true, - [CellToolbarLocation]: { default: 'right' }, - [CompactView]: true, - [ShowCellStatusBar]: 'visible', - [ConsolidatedRunButton]: true, - [UndoRedoPerCell]: false + [NotebookSetting.focusIndicator]: 'gutter', + [NotebookSetting.insertToolbarLocation]: 'both', + [NotebookSetting.globalToolbar]: true, + [NotebookSetting.cellToolbarLocation]: { default: 'right' }, + [NotebookSetting.compactView]: true, + [NotebookSetting.showCellStatusBar]: 'visible', + [NotebookSetting.consolidatedRunButton]: true, + [NotebookSetting.undoRedoPerCell]: false }, [NotebookProfileType.jupyter]: { - [FocusIndicator]: 'gutter', - [InsertToolbarLocation]: 'notebookToolbar', - [GlobalToolbar]: true, - [CellToolbarLocation]: { default: 'left' }, - [CompactView]: true, - [ShowCellStatusBar]: 'visible', - [ConsolidatedRunButton]: false, - [UndoRedoPerCell]: true + [NotebookSetting.focusIndicator]: 'gutter', + [NotebookSetting.insertToolbarLocation]: 'notebookToolbar', + [NotebookSetting.globalToolbar]: true, + [NotebookSetting.cellToolbarLocation]: { default: 'left' }, + [NotebookSetting.compactView]: true, + [NotebookSetting.showCellStatusBar]: 'visible', + [NotebookSetting.consolidatedRunButton]: false, + [NotebookSetting.undoRedoPerCell]: true }, [NotebookProfileType.colab]: { - [FocusIndicator]: 'border', - [InsertToolbarLocation]: 'betweenCells', - [GlobalToolbar]: false, - [CellToolbarLocation]: { default: 'right' }, - [CompactView]: false, - [ShowCellStatusBar]: 'hidden', - [ConsolidatedRunButton]: true, - [UndoRedoPerCell]: false + [NotebookSetting.focusIndicator]: 'border', + [NotebookSetting.insertToolbarLocation]: 'betweenCells', + [NotebookSetting.globalToolbar]: false, + [NotebookSetting.cellToolbarLocation]: { default: 'right' }, + [NotebookSetting.compactView]: false, + [NotebookSetting.showCellStatusBar]: 'hidden', + [NotebookSetting.consolidatedRunButton]: true, + [NotebookSetting.undoRedoPerCell]: false } }; @@ -101,13 +101,13 @@ export class NotebookProfileContribution extends Disposable { return; } else { // check if settings are already modified - const focusIndicator = configService.getValue(FocusIndicator); - const insertToolbarPosition = configService.getValue(InsertToolbarLocation); - const globalToolbar = configService.getValue(GlobalToolbar); - // const cellToolbarLocation = configService.getValue(CellToolbarLocation); - const compactView = configService.getValue(CompactView); - const showCellStatusBar = configService.getValue(ShowCellStatusBar); - const consolidatedRunButton = configService.getValue(ConsolidatedRunButton); + const focusIndicator = configService.getValue(NotebookSetting.focusIndicator); + const insertToolbarPosition = configService.getValue(NotebookSetting.insertToolbarLocation); + const globalToolbar = configService.getValue(NotebookSetting.globalToolbar); + // const cellToolbarLocation = configService.getValue(NotebookSetting.cellToolbarLocation); + const compactView = configService.getValue(NotebookSetting.compactView); + const showCellStatusBar = configService.getValue(NotebookSetting.showCellStatusBar); + const consolidatedRunButton = configService.getValue(NotebookSetting.consolidatedRunButton); if (focusIndicator === 'border' && insertToolbarPosition === 'both' && globalToolbar === false diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index 83cdd5c305d..c55e7d9928c 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -17,7 +17,7 @@ import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cel import { cellExecutionArgs, CellToolbarOrder, CELL_TITLE_CELL_GROUP_ID, executeNotebookCondition, getContextFromActiveEditor, getContextFromUri, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NotebookAction, NotebookCellAction, NotebookMultiCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, parseMultiCellExecutionArgs } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_MISSING_KERNEL_EXTENSION } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { CellKind, ConsolidatedRunButton, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookSetting, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -207,7 +207,7 @@ registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction { id: MenuId.NotebookCellExecute, when: ContextKeyExpr.and( executeCondition, - ContextKeyExpr.equals(`config.${ConsolidatedRunButton}`, true)) + ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true)) }, { id: MenuId.NotebookCellTitle, @@ -215,7 +215,7 @@ registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction { group: CELL_TITLE_CELL_GROUP_ID, when: ContextKeyExpr.and( executeCondition, - ContextKeyExpr.equals(`config.${ConsolidatedRunButton}`, false)) + ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false)) } ], icon: icons.executeAboveIcon @@ -253,7 +253,7 @@ registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction { id: MenuId.NotebookCellExecute, when: ContextKeyExpr.and( executeCondition, - ContextKeyExpr.equals(`config.${ConsolidatedRunButton}`, true)) + ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true)) }, { id: MenuId.NotebookCellTitle, @@ -261,7 +261,7 @@ registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction { group: CELL_TITLE_CELL_GROUP_ID, when: ContextKeyExpr.and( executeCondition, - ContextKeyExpr.equals(`config.${ConsolidatedRunButton}`, false)) + ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false)) } ], icon: icons.executeBelowIcon @@ -446,7 +446,7 @@ registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_INSERT_BELOW, - precondition: executeThisCellCondition, + precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')), title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -460,14 +460,17 @@ registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction { const idx = context.notebookEditor.getCellIndex(context.cell); const modeService = accessor.get(IModeService); const newFocusMode = context.cell.focusMode === CellFocusMode.Editor ? 'editor' : 'container'; - const executionP = runCell(accessor, context); - const newCell = insertCell(modeService, context.notebookEditor, idx, CellKind.Code, 'below'); + const newCell = insertCell(modeService, context.notebookEditor, idx, context.cell.cellKind, 'below'); if (newCell) { context.notebookEditor.focusNotebookCell(newCell, newFocusMode); } - return executionP; + if (context.cell.cellKind === CellKind.Markup) { + context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_INSERT_BELOW); + } else { + runCell(accessor, context); + } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts index c15d0bfb871..c88434cf848 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts @@ -16,7 +16,7 @@ import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cel import { INotebookActionContext, NotebookAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_EDITOR_EDITABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, GlobalToolbarShowLabel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove'; const INSERT_CODE_CELL_BELOW_COMMAND_ID = 'notebook.cell.insertCodeCellBelow'; @@ -323,7 +323,7 @@ MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, { NOTEBOOK_EDITOR_EDITABLE.isEqualTo(true), ContextKeyExpr.notEquals('config.notebook.insertToolbarLocation', 'betweenCells'), ContextKeyExpr.notEquals('config.notebook.insertToolbarLocation', 'hidden'), - ContextKeyExpr.notEquals(`config.${GlobalToolbarShowLabel}`, false) + ContextKeyExpr.notEquals(`config.${NotebookSetting.globalToolbarShowLabel}`, false) ) }); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts index c3f39c3cb4e..9e16c29d0e4 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts @@ -13,7 +13,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { OpenGettingStarted } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; @@ -23,7 +23,7 @@ registerAction2(class NotebookConfigureLayoutAction extends Action2 { id: 'workbench.notebook.layout.select', title: localize('workbench.notebook.layout.select.label', "Select between Notebook Layouts"), f1: true, - precondition: ContextKeyExpr.equals(`config.${OpenGettingStarted}`, true), + precondition: ContextKeyExpr.equals(`config.${NotebookSetting.openGettingStarted}`, true), category: NOTEBOOK_ACTIONS_CATEGORY, menu: [ { @@ -32,7 +32,7 @@ registerAction2(class NotebookConfigureLayoutAction extends Action2 { when: ContextKeyExpr.and( NOTEBOOK_IS_ACTIVE_EDITOR, ContextKeyExpr.notEquals('config.notebook.globalToolbar', true), - ContextKeyExpr.equals(`config.${OpenGettingStarted}`, true) + ContextKeyExpr.equals(`config.${NotebookSetting.openGettingStarted}`, true) ), order: 0 }, @@ -41,7 +41,7 @@ registerAction2(class NotebookConfigureLayoutAction extends Action2 { group: 'notebookLayout', when: ContextKeyExpr.and( ContextKeyExpr.equals('config.notebook.globalToolbar', true), - ContextKeyExpr.equals(`config.${OpenGettingStarted}`, true) + ContextKeyExpr.equals(`config.${NotebookSetting.openGettingStarted}`, true) ), order: 0 } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 259fda6abfc..14c6dc4b5dd 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -8,13 +8,13 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { DiffElementViewModelBase, getFormatedMetadataJSON, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; +import { DiffElementViewModelBase, getFormatedMetadataJSON, getFormatedOutputJSON, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { CellEditType, CellUri, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellUri, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; @@ -99,6 +99,7 @@ export const fixedDiffEditorOptions: IDiffEditorConstructionOptions = { class PropertyHeader extends Disposable { protected _foldingIndicator!: HTMLElement; protected _statusSpan!: HTMLElement; + protected _description!: HTMLElement; protected _toolbar!: ToolBar; protected _menu!: IMenu; protected _propertyExpanded?: IContextKey; @@ -109,7 +110,7 @@ class PropertyHeader extends Disposable { readonly notebookEditor: INotebookTextDiffEditor, readonly accessor: { updateInfoRendering: (renderOutput: boolean) => void; - checkIfModified: (cell: DiffElementViewModelBase) => boolean; + checkIfModified: (cell: DiffElementViewModelBase) => false | { reason: string | undefined }; getFoldingState: (cell: DiffElementViewModelBase) => PropertyFoldingState; updateFoldingState: (cell: DiffElementViewModelBase, newState: PropertyFoldingState) => void; unChangedLabel: string; @@ -132,14 +133,20 @@ class PropertyHeader extends Disposable { this._foldingIndicator.classList.add(this.accessor.prefix); this._updateFoldingIcon(); const metadataStatus = DOM.append(this.propertyHeaderContainer, DOM.$('div.property-status')); + this._statusSpan = DOM.append(metadataStatus, DOM.$('span')); + this._description = DOM.append(metadataStatus, DOM.$('span.property-description')); if (metadataChanged) { this._statusSpan.textContent = this.accessor.changedLabel; this._statusSpan.style.fontWeight = 'bold'; + if (metadataChanged.reason) { + this._description.textContent = metadataChanged.reason; + } this.propertyHeaderContainer.classList.add('modified'); } else { this._statusSpan.textContent = this.accessor.unChangedLabel; + this._description.textContent = ''; this.propertyHeaderContainer.classList.remove('modified'); } @@ -162,7 +169,7 @@ class PropertyHeader extends Disposable { const scopedContextKeyService = this.contextKeyService.createScoped(cellToolbarContainer); this._register(scopedContextKeyService); const propertyChanged = NOTEBOOK_DIFF_CELL_PROPERTY.bindTo(scopedContextKeyService); - propertyChanged.set(metadataChanged); + propertyChanged.set(!!metadataChanged); this._propertyExpanded = NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED.bindTo(scopedContextKeyService); this._menu = this.menuService.createMenu(this.accessor.menuId, scopedContextKeyService); @@ -224,6 +231,9 @@ class PropertyHeader extends Disposable { if (metadataChanged) { this._statusSpan.textContent = this.accessor.changedLabel; this._statusSpan.style.fontWeight = 'bold'; + if (metadataChanged.reason) { + this._description.textContent = metadataChanged.reason; + } this.propertyHeaderContainer.classList.add('modified'); const actions: IAction[] = []; createAndFillInActionBarActions(this._menu, undefined, actions); @@ -231,6 +241,7 @@ class PropertyHeader extends Disposable { } else { this._statusSpan.textContent = this.accessor.unChangedLabel; this._statusSpan.style.fontWeight = 'normal'; + this._description.textContent = ''; this.propertyHeaderContainer.classList.remove('modified'); this._toolbar.setActions([]); } @@ -612,16 +623,12 @@ abstract class AbstractElementRenderer extends Disposable { } } - private _getFormatedOutputJSON(outputs: IOutputDto[]) { - return JSON.stringify(outputs.map(op => ({ outputs: op.outputs })), undefined, '\t'); - } - private _buildOutputEditor() { this._outputEditorDisposeStore.clear(); if ((this.cell.type === 'modified' || this.cell.type === 'unchanged') && !this.notebookEditor.textModel!.transientOptions.transientOutputs) { - const originalOutputsSource = this._getFormatedOutputJSON(this.cell.original?.outputs || []); - const modifiedOutputsSource = this._getFormatedOutputJSON(this.cell.modified?.outputs || []); + const originalOutputsSource = getFormatedOutputJSON(this.cell.original?.outputs || []); + const modifiedOutputsSource = getFormatedOutputJSON(this.cell.modified?.outputs || []); if (originalOutputsSource !== modifiedOutputsSource) { const mode = this.modeService.create('json'); const originalModel = this.modelService.createModel(originalOutputsSource, mode, undefined, true); @@ -664,7 +671,7 @@ abstract class AbstractElementRenderer extends Disposable { })); this._outputEditorDisposeStore.add(this.cell.modified!.textModel.onDidChangeOutputs(() => { - const modifiedOutputsSource = this._getFormatedOutputJSON(this.cell.modified?.outputs || []); + const modifiedOutputsSource = getFormatedOutputJSON(this.cell.modified?.outputs || []); modifiedModel.setValue(modifiedOutputsSource); this._outputHeader.refresh(); })); @@ -684,7 +691,7 @@ abstract class AbstractElementRenderer extends Disposable { this._outputEditorDisposeStore.add(this._outputEditor); const mode = this.modeService.create('json'); - const originaloutputSource = this._getFormatedOutputJSON( + const originaloutputSource = getFormatedOutputJSON( this.notebookEditor.textModel!.transientOptions.transientOutputs ? [] : this.cell.type === 'insert' diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts index 421d414e96f..fd60f3310ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts @@ -12,7 +12,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { hash } from 'vs/base/common/hash'; import { format } from 'vs/base/common/jsonFormatter'; import { applyEdits } from 'vs/base/common/jsonEdit'; -import { ICellOutput, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellOutput, IOutputDto, IOutputItemDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel'; import { URI } from 'vs/base/common/uri'; import { NotebookDiffEditorEventDispatcher, NotebookDiffViewEventType } from 'vs/workbench/contrib/notebook/browser/diff/eventDispatcher'; @@ -274,8 +274,8 @@ export abstract class DiffElementViewModelBase extends Disposable { this.editorEventDispatcher.emit([{ type: NotebookDiffViewEventType.CellLayoutChanged, source: this._layoutInfo }]); } - abstract checkIfOutputsModified(): boolean; - abstract checkMetadataIfModified(): boolean; + abstract checkIfOutputsModified(): false | { reason: string | undefined; }; + abstract checkMetadataIfModified(): false | { reason: string | undefined; }; abstract isOutputEmpty(): boolean; abstract getRichOutputTotalHeight(): number; abstract getCellByUri(cellUri: URI): IGenericCellViewModel; @@ -375,11 +375,28 @@ export class SideBySideDiffElementViewModel extends DiffElementViewModelBase { } checkIfOutputsModified() { - return !this.mainDocumentTextModel.transientOptions.transientOutputs && !outputsEqual(this.original?.outputs ?? [], this.modified?.outputs ?? []); + if (this.mainDocumentTextModel.transientOptions.transientOutputs) { + return false; + } + + const ret = outputsEqual(this.original?.outputs ?? [], this.modified?.outputs ?? []); + + if (ret === OutputComparison.Unchanged) { + return false; + } + + return { + reason: ret === OutputComparison.Metadata ? 'Output metadata is changed' : undefined + }; } - checkMetadataIfModified(): boolean { - return hash(getFormatedMetadataJSON(this.mainDocumentTextModel, this.original?.metadata || {}, this.original?.language)) !== hash(getFormatedMetadataJSON(this.mainDocumentTextModel, this.modified?.metadata ?? {}, this.modified?.language)); + checkMetadataIfModified() { + const modified = hash(getFormatedMetadataJSON(this.mainDocumentTextModel, this.original?.metadata || {}, this.original?.language)) !== hash(getFormatedMetadataJSON(this.mainDocumentTextModel, this.modified?.metadata ?? {}, this.modified?.language)); + if (modified) { + return { reason: undefined }; + } else { + return false; + } } updateOutputHeight(diffSide: DiffSide, index: number, height: number) { @@ -489,11 +506,11 @@ export class SingleSideDiffElementViewModel extends DiffElementViewModelBase { } - checkIfOutputsModified(): boolean { + checkIfOutputsModified(): false | { reason: string | undefined } { return false; } - checkMetadataIfModified(): boolean { + checkMetadataIfModified(): false | { reason: string | undefined } { return false; } @@ -536,9 +553,15 @@ export class SingleSideDiffElementViewModel extends DiffElementViewModelBase { } } +const enum OutputComparison { + Unchanged = 0, + Metadata = 1, + Other = 2 +} + function outputsEqual(original: ICellOutput[], modified: ICellOutput[]) { if (original.length !== modified.length) { - return false; + return OutputComparison.Other; } const len = original.length; @@ -547,11 +570,11 @@ function outputsEqual(original: ICellOutput[], modified: ICellOutput[]) { const b = modified[i]; if (hash(a.metadata) !== hash(b.metadata)) { - return false; + return OutputComparison.Metadata; } if (a.outputs.length !== b.outputs.length) { - return false; + return OutputComparison.Other; } for (let j = 0; j < a.outputs.length; j++) { @@ -559,22 +582,22 @@ function outputsEqual(original: ICellOutput[], modified: ICellOutput[]) { const bOutputItem = b.outputs[j]; if (aOutputItem.mime !== bOutputItem.mime) { - return false; + return OutputComparison.Other; } if (aOutputItem.data.buffer.length !== bOutputItem.data.buffer.length) { - return false; + return OutputComparison.Other; } for (let k = 0; k < aOutputItem.data.buffer.length; k++) { if (aOutputItem.data.buffer[k] !== bOutputItem.data.buffer[k]) { - return false; + return OutputComparison.Other; } } } } - return true; + return OutputComparison.Unchanged; } export function getFormatedMetadataJSON(documentTextModel: NotebookTextModel, metadata: NotebookCellMetadata, language?: string) { @@ -604,3 +627,38 @@ export function getFormatedMetadataJSON(documentTextModel: NotebookTextModel, me return metadataSource; } + +export function getStreamOutputData(outputs: IOutputItemDto[]) { + if (!outputs.length) { + return null; + } + + const first = outputs[0]; + const mime = first.mime; + const sameStream = !outputs.find(op => op.mime !== mime); + + if (sameStream) { + return outputs.map(opit => opit.data.toString()).join(''); + } else { + return null; + } +} + +export function getFormatedOutputJSON(outputs: IOutputDto[]) { + if (outputs.length === 1) { + const streamOutputData = getStreamOutputData(outputs[0].outputs); + if (streamOutputData) { + return streamOutputData; + } + } + + return JSON.stringify(outputs.map(output => { + return ({ + metadata: output.metadata, + outputItems: output.outputs.map(opit => ({ + mimeType: opit.mime, + data: opit.data.toString() + })) + }); + }), undefined, '\t'); +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index 45d7774cb88..5dc1777ddb0 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -129,10 +129,15 @@ .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status span, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status span { - margin: 0 8px; + margin: 0 0 0 8px; line-height: 21px; } +.notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status span.property-description, +.notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status span.property-description { + font-style: italic; +} + .notebook-text-diff-editor { overflow: hidden; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index f380458fb38..399f893bed9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd import { isCompositeNotebookEditorInput, NotebookEditorInput, NotebookEditorInputOptions } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl'; -import { CellKind, CellToolbarLocation, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBar, CompactView, FocusIndicator, InsertToolbarLocation, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, NotebookCellEditorOptionsCustomizations, ConsolidatedRunButton, TextOutputLineLimit, GlobalToolbarShowLabel, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookWorkingCopyTypeIdentifier, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; @@ -45,7 +45,7 @@ import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/brows import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { Event } from 'vs/base/common/event'; -import { getFormatedMetadataJSON } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; +import { getFormatedMetadataJSON, getStreamOutputData } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { NotebookModelResolverServiceImpl } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookKernelService } from 'vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl'; @@ -208,7 +208,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri ) { super(); - const undoRedoPerCell = configurationService.getValue(UndoRedoPerCell); + const undoRedoPerCell = configurationService.getValue(NotebookSetting.undoRedoPerCell); this._register(undoRedoService.registerUriComparisonKeyComputer(CellUri.scheme, { getComparisonKey: (uri: URI): string => { @@ -372,22 +372,6 @@ class CellInfoContentProvider { return result; } - private _getStreamOutputData(outputs: IOutputItemDto[]) { - if (!outputs.length) { - return null; - } - - const first = outputs[0]; - const mime = first.mime; - const sameStream = !outputs.find(op => op.mime !== mime); - - if (sameStream) { - return outputs.map(opit => opit.data.toString()).join(''); - } else { - return null; - } - } - async provideOutputTextContent(resource: URI): Promise { const existing = this._modelService.getModel(resource); if (existing) { @@ -408,7 +392,7 @@ class CellInfoContentProvider { if (cell.handle === data.handle) { if (cell.outputs.length === 1) { // single output - const streamOutputData = this._getStreamOutputData(cell.outputs[0].outputs); + const streamOutputData = getStreamOutputData(cell.outputs[0].outputs); if (streamOutputData) { result = this._modelService.createModel( streamOutputData, @@ -656,7 +640,7 @@ configurationRegistry.registerConfiguration({ title: nls.localize('notebookConfigurationTitle', "Notebook"), type: 'object', properties: { - [DisplayOrderKey]: { + [NotebookSetting.displayOrder]: { description: nls.localize('notebook.displayOrder.description', "Priority list for output mime types"), type: ['array'], items: { @@ -664,7 +648,7 @@ configurationRegistry.registerConfiguration({ }, default: [] }, - [CellToolbarLocation]: { + [NotebookSetting.cellToolbarLocation]: { description: nls.localize('notebook.cellToolbarLocation.description', "Where the cell toolbar should be shown, or whether it should be hidden."), type: 'object', additionalProperties: { @@ -677,7 +661,7 @@ configurationRegistry.registerConfiguration({ }, tags: ['notebookLayout'] }, - [ShowCellStatusBar]: { + [NotebookSetting.showCellStatusBar]: { description: nls.localize('notebook.showCellStatusbar.description', "Whether the cell status bar should be shown."), type: 'string', enum: ['hidden', 'visible', 'visibleAfterExecute'], @@ -688,39 +672,39 @@ configurationRegistry.registerConfiguration({ default: 'visible', tags: ['notebookLayout'] }, - [NotebookTextDiffEditorPreview]: { + [NotebookSetting.textDiffEditorPreview]: { description: nls.localize('notebook.diff.enablePreview.description', "Whether to use the enhanced text diff editor for notebook."), type: 'boolean', default: true, tags: ['notebookLayout'] }, - [CellToolbarVisibility]: { + [NotebookSetting.cellToolbarVisibility]: { markdownDescription: nls.localize('notebook.cellToolbarVisibility.description', "Whether the cell toolbar should appear on hover or click."), type: 'string', enum: ['hover', 'click'], default: 'click', tags: ['notebookLayout'] }, - [UndoRedoPerCell]: { + [NotebookSetting.undoRedoPerCell]: { description: nls.localize('notebook.undoRedoPerCell.description', "Whether to use separate undo/redo stack for each cell."), type: 'boolean', default: true, tags: ['notebookLayout'] }, - [CompactView]: { + [NotebookSetting.compactView]: { description: nls.localize('notebook.compactView.description', "Control whether the notebook editor should be rendered in a compact form. For example, when turned on, it will decrease the left margin width."), type: 'boolean', default: true, tags: ['notebookLayout'] }, - [FocusIndicator]: { + [NotebookSetting.focusIndicator]: { description: nls.localize('notebook.focusIndicator.description', "Controls where the focus indicator is rendered, either along the cell borders or on the left gutter"), type: 'string', enum: ['border', 'gutter'], default: 'gutter', tags: ['notebookLayout'] }, - [InsertToolbarLocation]: { + [NotebookSetting.insertToolbarLocation]: { description: nls.localize('notebook.insertToolbarPosition.description', "Control where the insert cell actions should appear."), type: 'string', enum: ['betweenCells', 'notebookToolbar', 'both', 'hidden'], @@ -733,19 +717,19 @@ configurationRegistry.registerConfiguration({ default: 'both', tags: ['notebookLayout'] }, - [GlobalToolbar]: { + [NotebookSetting.globalToolbar]: { description: nls.localize('notebook.globalToolbar.description', "Control whether to render a global toolbar inside the notebook editor."), type: 'boolean', default: true, tags: ['notebookLayout'] }, - [ConsolidatedOutputButton]: { + [NotebookSetting.consolidatedOutputButton]: { description: nls.localize('notebook.consolidatedOutputButton.description', "Control whether outputs action should be rendered in the output toolbar."), type: 'boolean', default: true, tags: ['notebookLayout'] }, - [ShowFoldingControls]: { + [NotebookSetting.showFoldingControls]: { description: nls.localize('notebook.showFoldingControls.description', "Controls when the Markdown header folding arrow is shown."), type: 'string', enum: ['always', 'mouseover'], @@ -756,30 +740,36 @@ configurationRegistry.registerConfiguration({ default: 'mouseover', tags: ['notebookLayout'] }, - [DragAndDropEnabled]: { + [NotebookSetting.dragAndDropEnabled]: { description: nls.localize('notebook.dragAndDrop.description', "Control whether the notebook editor should allow moving cells through drag and drop."), type: 'boolean', default: true, tags: ['notebookLayout'] }, - [ConsolidatedRunButton]: { + [NotebookSetting.consolidatedRunButton]: { description: nls.localize('notebook.consolidatedRunButton.description', "Control whether extra actions are shown in a dropdown next to the run button."), type: 'boolean', default: false, tags: ['notebookLayout'] }, - [GlobalToolbarShowLabel]: { + [NotebookSetting.globalToolbarShowLabel]: { description: nls.localize('notebook.globalToolbarShowLabel', "Control whether the actions on the notebook toolbar should render label or not."), type: 'boolean', default: true, tags: ['notebookLayout'] }, - [TextOutputLineLimit]: { + [NotebookSetting.textOutputLineLimit]: { description: nls.localize('notebook.textOutputLineLimit', "Control how many lines of text in a text output is rendered."), type: 'number', default: 30, tags: ['notebookLayout'] }, - [NotebookCellEditorOptionsCustomizations]: editorOptionsCustomizationSchema + [NotebookSetting.markupFontSize]: { + markdownDescription: nls.localize('notebook.markup.fontSize', "Controls the font size of rendered markup in notebooks. When set to `0`, 120% of `#editor.fontSize#` is used."), + type: 'number', + default: 0, + tags: ['notebookLayout'] + }, + [NotebookSetting.cellEditorOptionsCustomizations]: editorOptionsCustomizationSchema } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index 75754a75c08..d12d4cc249b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -68,3 +68,30 @@ Code cell outputs and markdown cells are rendered in the webview, which are asyn However, we **don't** warmup the previous viewport as the cell height change of previous viewport might trigger the flickering of markdown cells in current viewport. Before we optimize this, do not do any warmup of cells before current viewport. + +## Focus Tracking + +* DOM focus tracker of notebook editor container + * focus on cell container + * focus on cell editor + * focus on cell status bar + * focus on cell/execution/output toolbar + * focus on find widget + * focus on an element in the webview/iframe + - [ ] Focus on notebook toolbar +* hasWebviewFocus + + +CSS +* `monaco-list.focus-within` for cell container/editor borders + + +* in `codecell` we update the `cell.focusMode` when rendering it and if it's not the focused element anymore, the focusMode is reverted to container + + +* cell editor focused and editor blur event happens + * if focused element is another cell, then change focusMode to container + * if focused element is find widget / notebookToolbar, don't change focusMode + * if focused element is webview/iframe, don't change focusMode + * if focused element is outside of the notebook, don't change focusMode +* cell editor focused and cell moves out of view (no blur event) / `CodeCell.dispose` diff --git a/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts index 56cf615dd92..67d223ef756 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts @@ -13,17 +13,15 @@ import { INotebookCellStatusBarItemList, INotebookCellStatusBarItemProvider } fr export class NotebookCellStatusBarService extends Disposable implements INotebookCellStatusBarService { - private _onDidChangeProviders = this._register(new Emitter()); + readonly _serviceBrand: undefined; + + private readonly _onDidChangeProviders = this._register(new Emitter()); readonly onDidChangeProviders: Event = this._onDidChangeProviders.event; - private _onDidChangeItems = this._register(new Emitter()); + private readonly _onDidChangeItems = this._register(new Emitter()); readonly onDidChangeItems: Event = this._onDidChangeItems.event; - private _providers: INotebookCellStatusBarItemProvider[] = []; - - constructor() { - super(); - } + private readonly _providers: INotebookCellStatusBarItemProvider[] = []; registerCellStatusBarItemProvider(provider: INotebookCellStatusBarItemProvider): IDisposable { this._providers.push(provider); @@ -52,6 +50,4 @@ export class NotebookCellStatusBarService extends Disposable implements INoteboo } })); } - - readonly _serviceBrand: undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts index b398ff9852d..37a5b0f439e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts @@ -25,7 +25,7 @@ export class NotebookEditorKernelManager extends Disposable { getSelectedOrSuggestedKernel(notebook: INotebookTextModel): INotebookKernel | undefined { // returns SELECTED or the ONLY available kernel const info = this._notebookKernelService.getMatchingKernel(notebook); - return info.selected ?? info.suggestions[0]; + return info.selected ?? (info.all.length === 1 ? info.all[0] : undefined); } async executeNotebookCells(notebook: INotebookTextModel, cells: Iterable): Promise { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index de441e639c2..994640eb7fa 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -412,7 +412,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._updateForNotebookConfiguration(); } - if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.insertToolbarAlignment) { + if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.markupFontSize || e.insertToolbarAlignment) { this._styleElement?.remove(); this._createLayoutStyles(); this._webview?.updateOptions(this.notebookOptions.computeWebviewOptions()); @@ -1382,13 +1382,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD // model attached this._localCellStateListeners = this.viewModel.viewCells.map(cell => this._bindCellListener(cell)); + this._lastCellWithEditorFocus = this.viewModel.viewCells.find(viewCell => this.getActiveCell() === viewCell && viewCell.focusMode === CellFocusMode.Editor) ?? null; this._localStore.add(this.viewModel.onDidChangeViewCells((e) => { if (this._isDisposed) { return; } - // update resize listener + // update cell listener e.splices.reverse().forEach(splice => { const [start, deleted, newCells] = splice; const deletedCells = this._localCellStateListeners.splice(start, deleted, ...newCells.map(cell => this._bindCellListener(cell))); @@ -1433,11 +1434,25 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD if (e.outputCollapsedChanged && cell.isOutputCollapsed && cell.cellKind === CellKind.Code) { cell.outputsViewModels.forEach(output => this.hideInset(output)); } + + if (e.focusModeChanged) { + this._validateCellFocusMode(cell); + } })); return store; } + + private _lastCellWithEditorFocus: ICellViewModel | null = null; + private _validateCellFocusMode(cell: ICellViewModel) { + if (this._lastCellWithEditorFocus && this._lastCellWithEditorFocus !== cell) { + this._lastCellWithEditorFocus.focusMode = CellFocusMode.Container; + } + + this._lastCellWithEditorFocus = cell; + } + private async _warmupWithMarkdownRenderer(viewModel: NotebookViewModel, viewState: INotebookEditorViewState | undefined) { await this._resolveWebview(); @@ -2850,7 +2865,7 @@ registerThemingParticipant((theme, collector) => { const focusedEditorBorderColorColor = theme.getColor(focusedEditorBorderColor); if (focusedEditorBorderColorColor) { - collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-focus .cell-editor-part:before { outline: solid 1px ${focusedEditorBorderColorColor}; }`); + collector.addRule(`.notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-editor-part:before { outline: solid 1px ${focusedEditorBorderColorColor}; }`); } const cellBorderColor = theme.getColor(notebookCellBorder); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index c1fd60d25a2..6f309a54847 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -30,7 +30,7 @@ import { INotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/no import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, DisplayOrderKey, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookData, NotebookEditorPriority, NotebookRendererMatch, NotebookTextDiffEditorPreview, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookData, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { updateEditorTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions'; @@ -161,7 +161,7 @@ export class NotebookProviderInfoStore extends Disposable { priority: notebookProviderInfo.exclusive ? RegisteredEditorPriority.exclusive : notebookProviderInfo.priority, }; const notebookEditorOptions = { - canHandleDiff: () => !!this._configurationService.getValue(NotebookTextDiffEditorPreview) && !this._accessibilityService.isScreenReaderOptimized(), + canHandleDiff: () => !!this._configurationService.getValue(NotebookSetting.textDiffEditorPreview) && !this._accessibilityService.isScreenReaderOptimized(), canSupportResource: (resource: URI) => resource.scheme === Schemas.untitled || resource.scheme === Schemas.vscodeNotebookCell || this._fileService.hasProvider(resource) }; const notebookEditorInputFactory: EditorInputFactoryFunction = ({ resource, options }) => { @@ -458,7 +458,7 @@ export class NotebookService extends Disposable implements INotebookService { const updateOrder = () => { this._displayOrder = new MimeTypeDisplayOrder( - this._configurationService.getValue(DisplayOrderKey) || [], + this._configurationService.getValue(NotebookSetting.displayOrder) || [], this._accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER, @@ -468,7 +468,7 @@ export class NotebookService extends Disposable implements INotebookService { updateOrder(); this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectedKeys.indexOf(DisplayOrderKey) >= 0) { + if (e.affectedKeys.indexOf(NotebookSetting.displayOrder) >= 0) { updateOrder(); } })); @@ -626,7 +626,7 @@ export class NotebookService extends Disposable implements INotebookService { } saveMimeDisplayOrder(target: ConfigurationTarget) { - this._configurationService.updateValue(DisplayOrderKey, this._displayOrder.toArray(), target); + this._configurationService.updateValue(NotebookSetting.displayOrder, this._displayOrder.toArray(), target); } getRenderers(): INotebookRendererInfo[] { 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 ea32085e655..57f448967a6 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 @@ -6,9 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; -import { dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; @@ -17,10 +15,10 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { ICellOutputViewModel, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookDelegateForOutput, IOutputTransformContribution as IOutputRendererContribution } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry'; import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper'; -import { IOutputItemDto, TextOutputLineLimit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookDelegateForOutput, IOutputTransformContribution as IOutputRendererContribution } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { IOutputItemDto, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; class JavaScriptRendererContrib extends Disposable implements IOutputRendererContribution { @@ -76,7 +74,7 @@ class StreamRendererContrib extends Disposable implements IOutputRendererContrib const text = getStringValue(item); const contentNode = DOM.$('span.output-stream'); - const lineLimit = this.configurationService.getValue(TextOutputLineLimit) ?? 30; + const lineLimit = this.configurationService.getValue(NotebookSetting.textOutputLineLimit) ?? 30; truncatedArrayOfString(notebookUri, output.cellViewModel, Math.max(lineLimit, 6), contentNode, [text], disposables, linkDetector, this.openerService, this.themeService); container.appendChild(contentNode); @@ -180,7 +178,7 @@ class PlainTextRendererContrib extends Disposable implements IOutputRendererCont const str = getStringValue(item); const contentNode = DOM.$('.output-plaintext'); - const lineLimit = this.configurationService.getValue(TextOutputLineLimit) ?? 30; + const lineLimit = this.configurationService.getValue(NotebookSetting.textOutputLineLimit) ?? 30; truncatedArrayOfString(notebookUri, output.cellViewModel, Math.max(lineLimit, 6), contentNode, [str], disposables, linkDetector, this.openerService, this.themeService); container.appendChild(contentNode); @@ -213,34 +211,6 @@ class HTMLRendererContrib extends Disposable implements IOutputRendererContribut } } -class MdRendererContrib extends Disposable implements IOutputRendererContribution { - getType() { - return RenderOutputType.Mainframe; - } - - getMimetypes() { - return [Mimes.markdown, Mimes.latex]; - } - - constructor( - public notebookEditor: INotebookDelegateForOutput, - @IInstantiationService private readonly instantiationService: IInstantiationService, - ) { - super(); - } - - render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput { - const disposable = new DisposableStore(); - const str = getStringValue(item); - const mdOutput = document.createElement('div'); - const mdRenderer = this.instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookUri) }); - mdOutput.appendChild(mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }, undefined, { gfm: true }).element); - container.appendChild(mdOutput); - disposable.add(mdRenderer); - return { type: RenderOutputType.Mainframe, disposable }; - } -} - class ImgRendererContrib extends Disposable implements IOutputRendererContribution { getType() { return RenderOutputType.Mainframe; @@ -276,7 +246,6 @@ class ImgRendererContrib extends Disposable implements IOutputRendererContributi OutputRendererRegistry.registerOutputTransform(JavaScriptRendererContrib); OutputRendererRegistry.registerOutputTransform(HTMLRendererContrib); -OutputRendererRegistry.registerOutputTransform(MdRendererContrib); OutputRendererRegistry.registerOutputTransform(ImgRendererContrib); OutputRendererRegistry.registerOutputTransform(PlainTextRendererContrib); OutputRendererRegistry.registerOutputTransform(JSErrorRendererContrib); 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 d7fc86b2e6c..c50030657da 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -51,14 +51,6 @@ export interface ICachedInset { cachedCreation: ICreationRequestMessage; } -function html(strings: TemplateStringsArray, ...values: any[]): string { - let str = ''; - strings.forEach((string, i) => { - str += string + (values[i] || ''); - }); - return str; -} - export interface INotebookWebviewMessage { message: unknown; } @@ -89,6 +81,19 @@ export interface INotebookDelegateForWebview { triggerScroll(event: IMouseWheelEvent): void; } +interface BacklayerWebviewOptions { + readonly outputNodePadding: number; + readonly outputNodeLeftPadding: number; + readonly previewNodePadding: number; + readonly markdownLeftMargin: number; + readonly leftMargin: number; + readonly rightMargin: number; + readonly runGutter: number; + readonly dragAndDropEnabled: boolean; + readonly fontSize: number; + readonly markupFontSize: number; +} + export class BackLayerWebView extends Disposable { element: HTMLElement; webview: IWebviewElement | undefined = undefined; @@ -100,7 +105,7 @@ export class BackLayerWebView extends Disposable { private readonly _onMessage = this._register(new Emitter()); private readonly _preloadsCache = new Set(); public readonly onMessage: Event = this._onMessage.event; - private _initalized?: Promise; + private _initialized?: Promise; private _disposed = false; private _currentKernel?: INotebookKernel; @@ -110,17 +115,7 @@ export class BackLayerWebView extends Disposable { public readonly notebookEditor: INotebookDelegateForWebview, public readonly id: string, public readonly documentUri: URI, - private options: { - outputNodePadding: number, - outputNodeLeftPadding: number, - previewNodePadding: number, - markdownLeftMargin: number, - leftMargin: number, - rightMargin: number, - runGutter: number, - dragAndDropEnabled: boolean, - fontSize: number - }, + private options: BacklayerWebviewOptions, private readonly rendererMessaging: IScopedRendererMessaging | undefined, @IWebviewService readonly webviewService: IWebviewService, @IOpenerService readonly openerService: IOpenerService, @@ -177,17 +172,7 @@ export class BackLayerWebView extends Disposable { })); } - updateOptions(options: { - outputNodePadding: number, - outputNodeLeftPadding: number, - previewNodePadding: number, - markdownLeftMargin: number, - leftMargin: number, - rightMargin: number, - runGutter: number, - dragAndDropEnabled: boolean, - fontSize: number - }) { + updateOptions(options: BacklayerWebviewOptions) { this.options = options; this._updateStyles(); this._updateOptions(); @@ -215,10 +200,11 @@ export class BackLayerWebView extends Disposable { 'notebook-output-width': `calc(100% - ${this.options.leftMargin + this.options.rightMargin + this.options.runGutter}px)`, 'notebook-output-node-padding': `${this.options.outputNodePadding}px`, 'notebook-run-gutter': `${this.options.runGutter}px`, - 'notebook-preivew-node-padding': `${this.options.previewNodePadding}px`, + 'notebook-preview-node-padding': `${this.options.previewNodePadding}px`, 'notebook-markdown-left-margin': `${this.options.markdownLeftMargin}px`, 'notebook-output-node-left-padding': `${this.options.outputNodeLeftPadding}px`, 'notebook-markdown-min-height': `${this.options.previewNodePadding * 2}px`, + 'notebook-markup-font-size': typeof this.options.markupFontSize === 'number' && this.options.markupFontSize > 0 ? `${this.options.markupFontSize}px` : `calc(${this.options.fontSize}px * 1.2)`, 'notebook-cell-output-font-size': `${this.options.fontSize}px`, 'notebook-cell-markup-empty-content': nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit."), 'notebook-cell-renderer-not-found-error': nls.localize({ @@ -238,7 +224,7 @@ export class BackLayerWebView extends Disposable { this.nonce); const enableCsp = this.configurationService.getValue('notebook.experimental.enableCsp'); - return html` + return /* html */` @@ -279,15 +265,17 @@ export class BackLayerWebView extends Disposable { /* markdown */ #container > div.preview { width: 100%; - padding-right: var(--notebook-preivew-node-padding); + padding-right: var(--notebook-preview-node-padding); padding-left: var(--notebook-markdown-left-margin); - padding-top: var(--notebook-preivew-node-padding); - padding-bottom: var(--notebook-preivew-node-padding); + padding-top: var(--notebook-preview-node-padding); + padding-bottom: var(--notebook-preview-node-padding); box-sizing: border-box; white-space: nowrap; overflow: hidden; white-space: initial; + + font-size: var(--notebook-markup-font-size); color: var(--theme-ui-foreground); } @@ -434,7 +422,7 @@ export class BackLayerWebView extends Disposable { let coreDependencies = ''; let resolveFunc: () => void; - this._initalized = new Promise((resolve, reject) => { + this._initialized = new Promise((resolve) => { resolveFunc = resolve; }); @@ -483,7 +471,7 @@ var requirejs = (function() { }); } - await this._initalized; + await this._initialized; } private _initialize(content: string) { @@ -835,7 +823,7 @@ var requirejs = (function() { allowScripts: true, localResourceRoots: this.localResourceRootsCache, }, undefined); - // console.log(this.localResourceRootsCache); + webview.html = content; return webview; } 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 493ee88604f..cce8bfde294 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -46,91 +46,106 @@ export class CodeCell extends Disposable { ) { super(); - const width = this.viewCell.layoutInfo.editorWidth; - const lineNum = this.viewCell.lineCount; - const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; - const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding(this.viewCell.internalMetadata); + const editorHeight = this.calculateInitEditorHeight(); + this.initializeEditor(editorHeight); + this.registerEditorOptionsListener(); + this.registerViewCellStateChange(); + this.registerFocusModeTracker(); + this.registerEditorLayoutListeners(); + this.registerDecorations(); + this.registerMouseListener(); - // patch up focusMode - if (this.viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.getActiveCell() !== this.viewCell) { - this.viewCell.focusMode = CellFocusMode.Container; + // Render Outputs + this._outputContainerRenderer = this.instantiationService.createInstance(CellOutputContainer, notebookEditor, viewCell, templateData, { limit: 500 }); + this._outputContainerRenderer.render(editorHeight); + // Need to do this after the intial renderOutput + if (this.viewCell.isOutputCollapsed === undefined && this.viewCell.isInputCollapsed === undefined) { + this.initialViewUpdateExpanded(); + this.viewCell.layoutChange({}); } + this._register(this.viewCell.onLayoutInfoRead(() => { + this._outputContainerRenderer.probeHeight(); + })); + + this.updateForCollapseState(); + } + + private calculateInitEditorHeight() { + const lineNum = this.viewCell.lineCount; + const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; + const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding(this.viewCell.internalMetadata); const editorHeight = this.viewCell.layoutInfo.editorHeight === 0 ? lineNum * lineHeight + editorPadding.top + editorPadding.bottom : this.viewCell.layoutInfo.editorHeight; + return editorHeight; + } + private initializeEditor(initEditorHeight: number) { + const width = this.viewCell.layoutInfo.editorWidth; this.layoutEditor( { width: width, - height: editorHeight + height: initEditorHeight } ); const cts = new CancellationTokenSource(); this._register({ dispose() { cts.dispose(true); } }); - raceCancellation(viewCell.resolveTextModel(), cts.token).then(model => { + raceCancellation(this.viewCell.resolveTextModel(), cts.token).then(model => { if (this._isDisposed) { return; } - if (model && templateData.editor) { - templateData.editor.setModel(model); - viewCell.attachTextEditor(templateData.editor); + if (model && this.templateData.editor) { + this.templateData.editor.setModel(model); + this.viewCell.attachTextEditor(this.templateData.editor); const focusEditorIfNeeded = () => { if ( - notebookEditor.getActiveCell() === viewCell && - viewCell.focusMode === CellFocusMode.Editor && + this.notebookEditor.getActiveCell() === this.viewCell && + this.viewCell.focusMode === CellFocusMode.Editor && (this.notebookEditor.hasEditorFocus() || document.activeElement === document.body)) // Don't steal focus from other workbench parts, but if body has focus, we can take it { - templateData.editor?.focus(); + this.templateData.editor?.focus(); } }; focusEditorIfNeeded(); - const realContentHeight = templateData.editor?.getContentHeight(); - if (realContentHeight !== undefined && realContentHeight !== editorHeight) { + const realContentHeight = this.templateData.editor?.getContentHeight(); + if (realContentHeight !== undefined && realContentHeight !== initEditorHeight) { this.onCellEditorHeightChange(realContentHeight); } focusEditorIfNeeded(); } }); + } - const updateForFocusMode = () => { - if (viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.getActiveCell() === this.viewCell) { - templateData.editor?.focus(); - } - - templateData.container.classList.toggle('cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor); - }; - this._register(viewCell.onDidChangeState((e) => { - if (e.focusModeChanged) { - updateForFocusMode(); - } - })); - updateForFocusMode(); - + private registerEditorOptionsListener() { const updateEditorOptions = () => { - const editor = templateData.editor; + const editor = this.templateData.editor; if (!editor) { return; } - const isReadonly = notebookEditor.isReadOnly; - const padding = notebookEditor.notebookOptions.computeEditorPadding(viewCell.internalMetadata); + const isReadonly = this.notebookEditor.isReadOnly; + const padding = this.notebookEditor.notebookOptions.computeEditorPadding(this.viewCell.internalMetadata); const options = editor.getOptions(); if (options.get(EditorOption.readOnly) !== isReadonly || options.get(EditorOption.padding) !== padding) { - editor.updateOptions({ readOnly: notebookEditor.isReadOnly, padding: notebookEditor.notebookOptions.computeEditorPadding(viewCell.internalMetadata) }); + editor.updateOptions({ readOnly: this.notebookEditor.isReadOnly, padding: this.notebookEditor.notebookOptions.computeEditorPadding(this.viewCell.internalMetadata) }); } }; updateEditorOptions(); - this._register(viewCell.onDidChangeState((e) => { + this._register(this.viewCell.onDidChangeState((e) => { if (e.metadataChanged || e.internalMetadataChanged) { updateEditorOptions(); } + })); + } + private registerViewCellStateChange() { + this._register(this.viewCell.onDidChangeState((e) => { if (e.inputCollapsedChanged || e.outputCollapsedChanged) { this.viewCell.pauseLayout(); const updated = this.updateForCollapseState(); @@ -141,10 +156,10 @@ export class CodeCell extends Disposable { } })); - this._register(viewCell.onDidChangeLayout((e) => { + this._register(this.viewCell.onDidChangeLayout((e) => { if (e.outerWidth !== undefined) { - const layoutInfo = templateData.editor.getLayoutInfo(); - if (layoutInfo.width !== viewCell.layoutInfo.editorWidth) { + const layoutInfo = this.templateData.editor.getLayoutInfo(); + if (layoutInfo.width !== this.viewCell.layoutInfo.editorWidth) { this.onCellWidthChange(); } } @@ -153,8 +168,10 @@ export class CodeCell extends Disposable { this.relayoutCell(); } })); + } - this._register(templateData.editor.onDidContentSizeChange((e) => { + private registerEditorLayoutListeners() { + this._register(this.templateData.editor.onDidContentSizeChange((e) => { if (e.contentHeightChanged) { if (this.viewCell.layoutInfo.editorHeight !== e.contentHeight) { this.onCellEditorHeightChange(e.contentHeight); @@ -162,24 +179,26 @@ export class CodeCell extends Disposable { } })); - this._register(templateData.editor.onDidChangeCursorSelection((e) => { + this._register(this.templateData.editor.onDidChangeCursorSelection((e) => { if (e.source === 'restoreState') { // do not reveal the cell into view if this selection change was caused by restoring editors... return; } - const primarySelection = templateData.editor.getSelection(); + const primarySelection = this.templateData.editor.getSelection(); if (primarySelection) { - this.notebookEditor.revealLineInViewAsync(viewCell, primarySelection.positionLineNumber); + this.notebookEditor.revealLineInViewAsync(this.viewCell, primarySelection.positionLineNumber); } })); + } + private registerDecorations() { // Apply decorations - this._register(viewCell.onCellDecorationsChanged((e) => { + this._register(this.viewCell.onCellDecorationsChanged((e) => { e.added.forEach(options => { if (options.className) { - templateData.rootContainer.classList.add(options.className); + this.templateData.rootContainer.classList.add(options.className); } if (options.outputClassName) { @@ -189,7 +208,7 @@ export class CodeCell extends Disposable { e.removed.forEach(options => { if (options.className) { - templateData.rootContainer.classList.remove(options.className); + this.templateData.rootContainer.classList.remove(options.className); } if (options.outputClassName) { @@ -198,69 +217,75 @@ export class CodeCell extends Disposable { }); })); - viewCell.getCellDecorations().forEach(options => { + this.viewCell.getCellDecorations().forEach(options => { if (options.className) { - templateData.rootContainer.classList.add(options.className); + this.templateData.rootContainer.classList.add(options.className); } if (options.outputClassName) { this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.outputClassName], []); } }); + } + private registerMouseListener() { // Mouse click handlers - this._register(templateData.statusBar.onDidClick(e => { + this._register(this.templateData.statusBar.onDidClick(e => { if (e.type !== ClickTargetType.ContributedCommandItem) { - const target = templateData.editor.getTargetAtClientPoint(e.event.clientX, e.event.clientY - this.notebookEditor.notebookOptions.computeEditorStatusbarHeight(viewCell.internalMetadata)); + const target = this.templateData.editor.getTargetAtClientPoint(e.event.clientX, e.event.clientY - this.notebookEditor.notebookOptions.computeEditorStatusbarHeight(this.viewCell.internalMetadata)); if (target?.position) { - templateData.editor.setPosition(target.position); - templateData.editor.focus(); + this.templateData.editor.setPosition(target.position); + this.templateData.editor.focus(); } } })); - this._register(templateData.editor.onMouseDown(e => { + this._register(this.templateData.editor.onMouseDown(e => { // prevent default on right mouse click, otherwise it will trigger unexpected focus changes // the catch is, it means we don't allow customization of right button mouse down handlers other than the built in ones. if (e.event.rightButton) { e.event.preventDefault(); } })); + } + + private registerFocusModeTracker() { + const updateEditorForFocusModeChange = () => { + if (this.viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.getActiveCell() === this.viewCell) { + this.templateData.editor?.focus(); + } + + this.templateData.container.classList.toggle('cell-editor-focus', this.viewCell.focusMode === CellFocusMode.Editor); + }; + this._register(this.viewCell.onDidChangeState((e) => { + if (e.focusModeChanged) { + updateEditorForFocusModeChange(); + } + })); + + updateEditorForFocusModeChange(); // Focus Mode - const updateFocusMode = () => { - viewCell.focusMode = - (templateData.editor.hasWidgetFocus() || (document.activeElement && this.templateData.statusBar.statusBarContainer.contains(document.activeElement))) + const updateFocusModeForEditorEvent = () => { + this.viewCell.focusMode = + (this.templateData.editor.hasWidgetFocus() || (document.activeElement && this.templateData.statusBar.statusBarContainer.contains(document.activeElement))) ? CellFocusMode.Editor : CellFocusMode.Container; }; - this._register(templateData.editor.onDidFocusEditorWidget(() => { - updateFocusMode(); + this._register(this.templateData.editor.onDidFocusEditorWidget(() => { + updateFocusModeForEditorEvent(); })); - this._register(templateData.editor.onDidBlurEditorWidget(() => { + this._register(this.templateData.editor.onDidBlurEditorWidget(() => { // this is for a special case: // users click the status bar empty space, which we will then focus the editor // so we don't want to update the focus state too eagerly, it will be updated with onDidFocusEditorWidget - if (!(document.activeElement && this.templateData.statusBar.statusBarContainer.contains(document.activeElement))) { - updateFocusMode(); + if ( + this.notebookEditor.hasEditorFocus() && + !(document.activeElement && this.templateData.statusBar.statusBarContainer.contains(document.activeElement))) { + updateFocusModeForEditorEvent(); } })); - - // Render Outputs - this._outputContainerRenderer = this.instantiationService.createInstance(CellOutputContainer, notebookEditor, viewCell, templateData, { limit: 500 }); - this._outputContainerRenderer.render(editorHeight); - // Need to do this after the intial renderOutput - if (this.viewCell.isOutputCollapsed === undefined && this.viewCell.isInputCollapsed === undefined) { - this.initialViewUpdateExpanded(); - this.viewCell.layoutChange({}); - } - - this._register(this.viewCell.onLayoutInfoRead(() => { - this._outputContainerRenderer.probeHeight(); - })); - - this.updateForCollapseState(); } private updateForCollapseState(): boolean { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index edc8222bd42..52430aabb78 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -10,7 +10,7 @@ interface BaseToWebviewMessage { readonly __vscode_notebook_message: true; } -export interface WebviewIntialized extends BaseToWebviewMessage { +export interface WebviewInitialized extends BaseToWebviewMessage { readonly type: 'initialized'; } @@ -162,22 +162,22 @@ export interface IOutputRequestMetadata { /** * Additional attributes of a cell metadata. */ - readonly custom?: { [key: string]: unknown; }; + readonly custom?: { readonly [key: string]: unknown; }; } export interface IOutputRequestDto { /** * { mime_type: value } */ - readonly data: { [key: string]: unknown; }; + readonly data: { readonly [key: string]: unknown; }; readonly metadata?: IOutputRequestMetadata; readonly outputId: string; } export type ICreationContent = - | { type: RenderOutputType.Html; htmlContent: string; } - | { type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array; metadata: unknown; mimeType: string; }; + | { readonly type: RenderOutputType.Html; readonly htmlContent: string; } + | { readonly type: RenderOutputType.Extension; readonly outputId: string; readonly valueBytes: Uint8Array; readonly metadata: unknown; readonly mimeType: string; }; export interface ICreationRequestMessage { readonly type: 'html'; @@ -187,7 +187,7 @@ export interface ICreationRequestMessage { cellTop: number; outputOffset: number; readonly left: number; - readonly requiredPreloads: ReadonlyArray; + readonly requiredPreloads: readonly IControllerPreload[]; readonly initiallyHidden?: boolean; readonly rendererId?: string | undefined; } @@ -200,10 +200,15 @@ export interface IContentWidgetTopRequest { readonly forceDisplay: boolean; } +export interface IMarkupCellScrollTops { + readonly id: string; + readonly top: number; +} + export interface IViewScrollTopRequestMessage { readonly type: 'view-scroll'; - readonly widgets: IContentWidgetTopRequest[]; - readonly markupCells: { id: string; top: number; }[]; + readonly widgets: readonly IContentWidgetTopRequest[]; + readonly markupCells: readonly IMarkupCellScrollTops[]; } export interface IScrollRequestMessage { @@ -259,14 +264,14 @@ export interface IControllerPreload { export interface IUpdateControllerPreloadsMessage { readonly type: 'preload'; - readonly resources: IControllerPreload[]; + readonly resources: readonly IControllerPreload[]; } export interface IUpdateDecorationsMessage { readonly type: 'decorations'; readonly cellId: string; - readonly addedClassNames: string[]; - readonly removedClassNames: string[]; + readonly addedClassNames: readonly string[]; + readonly removedClassNames: readonly string[]; } export interface ICustomKernelMessage extends BaseToWebviewMessage { @@ -324,13 +329,13 @@ export interface IMarkupCellInitialization { export interface IInitializeMarkupCells { readonly type: 'initializeMarkup'; - readonly cells: ReadonlyArray; + readonly cells: readonly IMarkupCellInitialization[]; } export interface INotebookStylesMessage { readonly type: 'notebookStyles'; readonly styles: { - [key: string]: string; + readonly [key: string]: string; }; } @@ -354,7 +359,7 @@ export interface ITokenizedStylesChangedMessage { readonly css: string; } -export type FromWebviewMessage = WebviewIntialized | +export type FromWebviewMessage = WebviewInitialized | IDimensionMessage | IMouseEnterMessage | IMouseLeaveMessage | diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index c3bc9163d90..df12a174b99 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -637,7 +637,7 @@ async function webviewPreloads(ctx: PreloadContext) { { let outputContainer = document.getElementById(event.data.cellId); if (!outputContainer) { - viewModel.ensureOutputCell(event.data.cellId, -100000); + viewModel.ensureOutputCell(event.data.cellId, -100000, true); outputContainer = document.getElementById(event.data.cellId); } outputContainer?.classList.add(...event.data.addedClassNames); @@ -664,8 +664,8 @@ async function webviewPreloads(ctx: PreloadContext) { } // Re-add new properties - for (const variable of Object.keys(event.data.styles)) { - documentStyle.setProperty(`--${variable}`, event.data.styles[variable]); + for (const [name, value] of Object.entries(event.data.styles)) { + documentStyle.setProperty(`--${name}`, value); } break; case 'notebookOptions': @@ -1007,7 +1007,7 @@ async function webviewPreloads(ctx: PreloadContext) { } } - public updateMarkupScrolls(markupCells: { id: string; top: number; }[]) { + public updateMarkupScrolls(markupCells: readonly webviewMessages.IMarkupCellScrollTops[]) { for (const { id, top } of markupCells) { const cell = this._markupCells.get(id); if (cell) { @@ -1032,7 +1032,7 @@ async function webviewPreloads(ctx: PreloadContext) { return; } - const cellOutput = this.ensureOutputCell(data.cellId, data.cellTop); + const cellOutput = this.ensureOutputCell(data.cellId, data.cellTop, false); const outputNode = cellOutput.createOutputElement(data.outputId, data.outputOffset, data.left); outputNode.render(data.content, preloadsAndErrors); @@ -1040,13 +1040,18 @@ async function webviewPreloads(ctx: PreloadContext) { cellOutput.element.style.visibility = data.initiallyHidden ? 'hidden' : 'visible'; } - public ensureOutputCell(cellId: string, cellTop: number): OutputCell { + public ensureOutputCell(cellId: string, cellTop: number, skipCellTopUpdateIfExist: boolean): OutputCell { let cell = this._outputCells.get(cellId); + let existed = !!cell; if (!cell) { cell = new OutputCell(cellId); this._outputCells.set(cellId, cell); } + if (existed && skipCellTopUpdateIfExist) { + return cell; + } + cell.element.style.top = cellTop + 'px'; return cell; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts index ba3c117629b..2bc631d67e3 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts @@ -24,11 +24,9 @@ function rangesEqual(a: ICellRange[], b: ICellRange[]) { // Handle first, then we migrate to ICellRange competely // Challenge is List View talks about `element`, which needs extra work to convert to ICellRange as we support Folding and Cell Move export class NotebookCellSelectionCollection extends Disposable { + private readonly _onDidChangeSelection = this._register(new Emitter()); get onDidChangeSelection(): Event { return this._onDidChangeSelection.event; } - constructor() { - super(); - } private _primary: ICellRange | null = null; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 8e1f4b086e7..2a32670df98 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -23,7 +23,7 @@ import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controll import { INotebookEditorDelegate, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; -import { GlobalToolbarShowLabel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; @@ -114,7 +114,7 @@ export class NotebookEditorToolbar extends Disposable { this._register(this._notebookGlobalActionsMenu); this._useGlobalToolbar = this.notebookOptions.getLayoutConfiguration().globalToolbar; - this._renderLabel = this.configurationService.getValue(GlobalToolbarShowLabel); + this._renderLabel = this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel); const context = { ui: true, @@ -184,8 +184,8 @@ export class NotebookEditorToolbar extends Disposable { })); this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(GlobalToolbarShowLabel)) { - this._renderLabel = this.configurationService.getValue(GlobalToolbarShowLabel); + if (e.affectsConfiguration(NotebookSetting.globalToolbarShowLabel)) { + this._renderLabel = this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel); const oldElement = this._notebookLeftToolbar.getElement(); oldElement.parentElement?.removeChild(oldElement); this._notebookLeftToolbar.dispose(); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index badebb06286..c495315f175 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -579,7 +579,6 @@ const _mimeTypeInfo = new Map([ ['image/svg+xml', { supportedByCore: true }], ['application/json', { alwaysSecure: true, supportedByCore: true }], [Mimes.latex, { alwaysSecure: true, supportedByCore: true }], - [Mimes.markdown, { alwaysSecure: true, supportedByCore: true }], [Mimes.text, { alwaysSecure: true, supportedByCore: true }], ['text/html', { supportedByCore: true }], ['text/x-javascript', { alwaysSecure: true, supportedByCore: true }], // secure because rendered as text, not executed @@ -902,26 +901,30 @@ export interface INotebookCellStatusBarItemList { dispose?(): void; } -export const DisplayOrderKey = 'notebook.displayOrder'; -export const CellToolbarLocation = 'notebook.cellToolbarLocation'; -export const CellToolbarVisibility = 'notebook.cellToolbarVisibility'; export type ShowCellStatusBarType = 'hidden' | 'visible' | 'visibleAfterExecute'; -export const ShowCellStatusBar = 'notebook.showCellStatusBar'; -export const NotebookTextDiffEditorPreview = 'notebook.diff.enablePreview'; -export const ExperimentalInsertToolbarAlignment = 'notebook.experimental.insertToolbarAlignment'; -export const CompactView = 'notebook.compactView'; -export const FocusIndicator = 'notebook.cellFocusIndicator'; -export const InsertToolbarLocation = 'notebook.insertToolbarLocation'; -export const GlobalToolbar = 'notebook.globalToolbar'; -export const UndoRedoPerCell = 'notebook.undoRedoPerCell'; -export const ConsolidatedOutputButton = 'notebook.consolidatedOutputButton'; -export const ShowFoldingControls = 'notebook.showFoldingControls'; -export const DragAndDropEnabled = 'notebook.dragAndDropEnabled'; -export const NotebookCellEditorOptionsCustomizations = 'notebook.editorOptionsCustomizations'; -export const ConsolidatedRunButton = 'notebook.consolidatedRunButton'; -export const OpenGettingStarted = 'notebook.experimental.openGettingStarted'; -export const TextOutputLineLimit = 'notebook.output.textLineLimit'; -export const GlobalToolbarShowLabel = 'notebook.globalToolbarShowLabel'; + +export const NotebookSetting = { + displayOrder: 'notebook.displayOrder', + cellToolbarLocation: 'notebook.cellToolbarLocation', + cellToolbarVisibility: 'notebook.cellToolbarVisibility', + showCellStatusBar: 'notebook.showCellStatusBar', + textDiffEditorPreview: 'notebook.diff.enablePreview', + experimentalInsertToolbarAlignment: 'notebook.experimental.insertToolbarAlignment', + compactView: 'notebook.compactView', + focusIndicator: 'notebook.cellFocusIndicator', + insertToolbarLocation: 'notebook.insertToolbarLocation', + globalToolbar: 'notebook.globalToolbar', + undoRedoPerCell: 'notebook.undoRedoPerCell', + consolidatedOutputButton: 'notebook.consolidatedOutputButton', + showFoldingControls: 'notebook.showFoldingControls', + dragAndDropEnabled: 'notebook.dragAndDropEnabled', + cellEditorOptionsCustomizations: 'notebook.editorOptionsCustomizations', + consolidatedRunButton: 'notebook.consolidatedRunButton', + openGettingStarted: 'notebook.experimental.openGettingStarted', + textOutputLineLimit: 'notebook.output.textLineLimit', + globalToolbarShowLabel: 'notebook.globalToolbarShowLabel', + markupFontSize: 'notebook.markup.fontSize', +} as const; export const enum CellStatusbarAlignment { Left = 1, diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index 238c38653a2..2205ff78222 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as glob from 'vs/base/common/glob'; -import { GroupIdentifier, ISaveOptions, IMoveResult, IRevertOptions, EditorInputCapabilities, Verbosity, IUntypedEditorInput, isResourceDiffEditorInput, isResourceSideBySideEditorInput } from 'vs/workbench/common/editor'; +import { GroupIdentifier, ISaveOptions, IMoveResult, IRevertOptions, EditorInputCapabilities, Verbosity, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; @@ -13,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { IDisposable, IReference } from 'vs/base/common/lifecycle'; -import { CellEditType, CellUri, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ILabelService } from 'vs/platform/label/common/label'; import { Schemas } from 'vs/base/common/network'; import { mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; @@ -285,12 +285,6 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { if (otherInput instanceof NotebookEditorInput) { return this.viewType === otherInput.viewType && isEqual(this.resource, otherInput.resource); } - if (isResourceDiffEditorInput(otherInput) || isResourceSideBySideEditorInput(otherInput)) { - return false; - } - if (otherInput.resource && otherInput.resource.scheme === Schemas.vscodeNotebookCell) { - return isEqual(this.resource, CellUri.parse(otherInput.resource)?.notebook); - } return false; } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index 387fabb158e..040cedd0bd6 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -6,7 +6,7 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { CellToolbarLocation, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, ConsolidatedRunButton, DragAndDropEnabled, ExperimentalInsertToolbarAlignment, FocusIndicator, GlobalToolbar, InsertToolbarLocation, NotebookCellEditorOptionsCustomizations, NotebookCellInternalMetadata, ShowCellStatusBar, ShowCellStatusBarType, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookCellInternalMetadata, NotebookSetting, ShowCellStatusBarType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const SCROLLABLE_ELEMENT_PADDING_TOP = 18; @@ -59,30 +59,32 @@ export interface NotebookLayoutConfiguration { showFoldingControls: 'always' | 'mouseover'; dragAndDropEnabled: boolean; fontSize: number; + markupFontSize: number; focusIndicatorLeftMargin: number; editorOptionsCustomizations: any | undefined; } export interface NotebookOptionsChangeEvent { - cellStatusBarVisibility?: boolean; - cellToolbarLocation?: boolean; - cellToolbarInteraction?: boolean; - editorTopPadding?: boolean; - compactView?: boolean; - focusIndicator?: boolean; - insertToolbarPosition?: boolean; - insertToolbarAlignment?: boolean; - globalToolbar?: boolean; - showFoldingControls?: boolean; - consolidatedOutputButton?: boolean; - consolidatedRunButton?: boolean; - dragAndDropEnabled?: boolean; - fontSize?: boolean; - editorOptionsCustomizations?: boolean; - cellBreakpointMargin?: boolean; + readonly cellStatusBarVisibility?: boolean; + readonly cellToolbarLocation?: boolean; + readonly cellToolbarInteraction?: boolean; + readonly editorTopPadding?: boolean; + readonly compactView?: boolean; + readonly focusIndicator?: boolean; + readonly insertToolbarPosition?: boolean; + readonly insertToolbarAlignment?: boolean; + readonly globalToolbar?: boolean; + readonly showFoldingControls?: boolean; + readonly consolidatedOutputButton?: boolean; + readonly consolidatedRunButton?: boolean; + readonly dragAndDropEnabled?: boolean; + readonly fontSize?: boolean; + readonly markupFontSize?: boolean; + readonly editorOptionsCustomizations?: boolean; + readonly cellBreakpointMargin?: boolean; } -const defaultConfigConstants = { +const defaultConfigConstants = Object.freeze({ codeCellLeftMargin: 28, cellRunGutter: 32, markdownCellTopMargin: 8, @@ -90,9 +92,9 @@ const defaultConfigConstants = { markdownCellLeftMargin: 0, markdownCellGutter: 32, focusIndicatorLeftMargin: 4 -}; +}); -const compactConfigConstants = { +const compactConfigConstants = Object.freeze({ codeCellLeftMargin: 8, cellRunGutter: 36, markdownCellTopMargin: 6, @@ -100,7 +102,7 @@ const compactConfigConstants = { markdownCellLeftMargin: 8, markdownCellGutter: 36, focusIndicatorLeftMargin: 4 -}; +}); export class NotebookOptions extends Disposable { private _layoutConfiguration: NotebookLayoutConfiguration; @@ -109,21 +111,22 @@ export class NotebookOptions extends Disposable { constructor(private readonly configurationService: IConfigurationService, private readonly overrides?: { cellToolbarInteraction: string, globalToolbar: boolean }) { super(); - const showCellStatusBar = this.configurationService.getValue(ShowCellStatusBar); - const globalToolbar = overrides?.globalToolbar ?? this.configurationService.getValue(GlobalToolbar) ?? true; - const consolidatedOutputButton = this.configurationService.getValue(ConsolidatedOutputButton) ?? true; - const consolidatedRunButton = this.configurationService.getValue(ConsolidatedRunButton) ?? false; - const dragAndDropEnabled = this.configurationService.getValue(DragAndDropEnabled) ?? true; - const cellToolbarLocation = this.configurationService.getValue(CellToolbarLocation) ?? { 'default': 'right' }; - const cellToolbarInteraction = overrides?.cellToolbarInteraction ?? this.configurationService.getValue(CellToolbarVisibility); - const compactView = this.configurationService.getValue(CompactView) ?? true; + const showCellStatusBar = this.configurationService.getValue(NotebookSetting.showCellStatusBar); + const globalToolbar = overrides?.globalToolbar ?? this.configurationService.getValue(NotebookSetting.globalToolbar) ?? true; + const consolidatedOutputButton = this.configurationService.getValue(NotebookSetting.consolidatedOutputButton) ?? true; + const consolidatedRunButton = this.configurationService.getValue(NotebookSetting.consolidatedRunButton) ?? false; + const dragAndDropEnabled = this.configurationService.getValue(NotebookSetting.dragAndDropEnabled) ?? true; + const cellToolbarLocation = this.configurationService.getValue(NotebookSetting.cellToolbarLocation) ?? { 'default': 'right' }; + const cellToolbarInteraction = overrides?.cellToolbarInteraction ?? this.configurationService.getValue(NotebookSetting.cellToolbarVisibility); + const compactView = this.configurationService.getValue(NotebookSetting.compactView) ?? true; const focusIndicator = this._computeFocusIndicatorOption(); const insertToolbarPosition = this._computeInsertToolbarPositionOption(); const insertToolbarAlignment = this._computeInsertToolbarAlignmentOption(); const showFoldingControls = this._computeShowFoldingControlsOption(); // const { bottomToolbarGap, bottomToolbarHeight } = this._computeBottomToolbarDimensions(compactView, insertToolbarPosition, insertToolbarAlignment); const fontSize = this.configurationService.getValue('editor.fontSize'); - const editorOptionsCustomizations = this.configurationService.getValue(NotebookCellEditorOptionsCustomizations); + const markupFontSize = this.configurationService.getValue(NotebookSetting.markupFontSize); + const editorOptionsCustomizations = this.configurationService.getValue(NotebookSetting.cellEditorOptionsCustomizations); this._layoutConfiguration = { ...(compactView ? compactConfigConstants : defaultConfigConstants), @@ -153,6 +156,7 @@ export class NotebookOptions extends Disposable { insertToolbarAlignment, showFoldingControls, fontSize, + markupFontSize, editorOptionsCustomizations, }; @@ -169,20 +173,21 @@ export class NotebookOptions extends Disposable { } private _updateConfiguration(e: IConfigurationChangeEvent) { - const cellStatusBarVisibility = e.affectsConfiguration(ShowCellStatusBar); - const cellToolbarLocation = e.affectsConfiguration(CellToolbarLocation); - const cellToolbarInteraction = e.affectsConfiguration(CellToolbarVisibility); - const compactView = e.affectsConfiguration(CompactView); - const focusIndicator = e.affectsConfiguration(FocusIndicator); - const insertToolbarPosition = e.affectsConfiguration(InsertToolbarLocation); - const insertToolbarAlignment = e.affectsConfiguration(ExperimentalInsertToolbarAlignment); - const globalToolbar = e.affectsConfiguration(GlobalToolbar); - const consolidatedOutputButton = e.affectsConfiguration(ConsolidatedOutputButton); - const consolidatedRunButton = e.affectsConfiguration(ConsolidatedRunButton); - const showFoldingControls = e.affectsConfiguration(ShowFoldingControls); - const dragAndDropEnabled = e.affectsConfiguration(DragAndDropEnabled); + const cellStatusBarVisibility = e.affectsConfiguration(NotebookSetting.showCellStatusBar); + const cellToolbarLocation = e.affectsConfiguration(NotebookSetting.cellToolbarLocation); + const cellToolbarInteraction = e.affectsConfiguration(NotebookSetting.cellToolbarVisibility); + const compactView = e.affectsConfiguration(NotebookSetting.compactView); + const focusIndicator = e.affectsConfiguration(NotebookSetting.focusIndicator); + const insertToolbarPosition = e.affectsConfiguration(NotebookSetting.insertToolbarLocation); + const insertToolbarAlignment = e.affectsConfiguration(NotebookSetting.experimentalInsertToolbarAlignment); + const globalToolbar = e.affectsConfiguration(NotebookSetting.globalToolbar); + const consolidatedOutputButton = e.affectsConfiguration(NotebookSetting.consolidatedOutputButton); + const consolidatedRunButton = e.affectsConfiguration(NotebookSetting.consolidatedRunButton); + const showFoldingControls = e.affectsConfiguration(NotebookSetting.showFoldingControls); + const dragAndDropEnabled = e.affectsConfiguration(NotebookSetting.dragAndDropEnabled); const fontSize = e.affectsConfiguration('editor.fontSize'); - const editorOptionsCustomizations = e.affectsConfiguration(NotebookCellEditorOptionsCustomizations); + const markupFontSize = e.affectsConfiguration(NotebookSetting.markupFontSize); + const editorOptionsCustomizations = e.affectsConfiguration(NotebookSetting.cellEditorOptionsCustomizations); if ( !cellStatusBarVisibility @@ -198,6 +203,7 @@ export class NotebookOptions extends Disposable { && !showFoldingControls && !dragAndDropEnabled && !fontSize + && !markupFontSize && !editorOptionsCustomizations) { return; } @@ -205,15 +211,15 @@ export class NotebookOptions extends Disposable { let configuration = Object.assign({}, this._layoutConfiguration); if (cellStatusBarVisibility) { - configuration.showCellStatusBar = this.configurationService.getValue(ShowCellStatusBar); + configuration.showCellStatusBar = this.configurationService.getValue(NotebookSetting.showCellStatusBar); } if (cellToolbarLocation) { - configuration.cellToolbarLocation = this.configurationService.getValue(CellToolbarLocation) ?? { 'default': 'right' }; + configuration.cellToolbarLocation = this.configurationService.getValue(NotebookSetting.cellToolbarLocation) ?? { 'default': 'right' }; } if (cellToolbarInteraction && !this.overrides?.cellToolbarInteraction) { - configuration.cellToolbarInteraction = this.configurationService.getValue(CellToolbarVisibility); + configuration.cellToolbarInteraction = this.configurationService.getValue(NotebookSetting.cellToolbarVisibility); } if (focusIndicator) { @@ -221,7 +227,7 @@ export class NotebookOptions extends Disposable { } if (compactView) { - const compactViewValue = this.configurationService.getValue(CompactView) ?? true; + const compactViewValue = this.configurationService.getValue(NotebookSetting.compactView) ?? true; configuration = Object.assign(configuration, { ...(compactViewValue ? compactConfigConstants : defaultConfigConstants), }); @@ -237,15 +243,15 @@ export class NotebookOptions extends Disposable { } if (globalToolbar && this.overrides?.globalToolbar === undefined) { - configuration.globalToolbar = this.configurationService.getValue(GlobalToolbar) ?? true; + configuration.globalToolbar = this.configurationService.getValue(NotebookSetting.globalToolbar) ?? true; } if (consolidatedOutputButton) { - configuration.consolidatedOutputButton = this.configurationService.getValue(ConsolidatedOutputButton) ?? true; + configuration.consolidatedOutputButton = this.configurationService.getValue(NotebookSetting.consolidatedOutputButton) ?? true; } if (consolidatedRunButton) { - configuration.consolidatedRunButton = this.configurationService.getValue(ConsolidatedRunButton) ?? true; + configuration.consolidatedRunButton = this.configurationService.getValue(NotebookSetting.consolidatedRunButton) ?? true; } if (showFoldingControls) { @@ -253,15 +259,19 @@ export class NotebookOptions extends Disposable { } if (dragAndDropEnabled) { - configuration.dragAndDropEnabled = this.configurationService.getValue(DragAndDropEnabled) ?? true; + configuration.dragAndDropEnabled = this.configurationService.getValue(NotebookSetting.dragAndDropEnabled) ?? true; } if (fontSize) { configuration.fontSize = this.configurationService.getValue('editor.fontSize'); } + if (markupFontSize) { + configuration.markupFontSize = this.configurationService.getValue(NotebookSetting.markupFontSize); + } + if (editorOptionsCustomizations) { - configuration.editorOptionsCustomizations = this.configurationService.getValue(NotebookCellEditorOptionsCustomizations); + configuration.editorOptionsCustomizations = this.configurationService.getValue(NotebookSetting.cellEditorOptionsCustomizations); } this._layoutConfiguration = Object.freeze(configuration); @@ -281,24 +291,25 @@ export class NotebookOptions extends Disposable { consolidatedRunButton, dragAndDropEnabled, fontSize, + markupFontSize, editorOptionsCustomizations }); } private _computeInsertToolbarPositionOption() { - return this.configurationService.getValue<'betweenCells' | 'notebookToolbar' | 'both' | 'hidden'>(InsertToolbarLocation) ?? 'both'; + return this.configurationService.getValue<'betweenCells' | 'notebookToolbar' | 'both' | 'hidden'>(NotebookSetting.insertToolbarLocation) ?? 'both'; } private _computeInsertToolbarAlignmentOption() { - return this.configurationService.getValue<'left' | 'center'>(ExperimentalInsertToolbarAlignment) ?? 'center'; + return this.configurationService.getValue<'left' | 'center'>(NotebookSetting.experimentalInsertToolbarAlignment) ?? 'center'; } private _computeShowFoldingControlsOption() { - return this.configurationService.getValue<'always' | 'mouseover'>(ShowFoldingControls) ?? 'mouseover'; + return this.configurationService.getValue<'always' | 'mouseover'>(NotebookSetting.showFoldingControls) ?? 'mouseover'; } private _computeFocusIndicatorOption() { - return this.configurationService.getValue<'border' | 'gutter'>(FocusIndicator) ?? 'gutter'; + return this.configurationService.getValue<'border' | 'gutter'>(NotebookSetting.focusIndicator) ?? 'gutter'; } getLayoutConfiguration(): NotebookLayoutConfiguration { @@ -456,7 +467,8 @@ export class NotebookOptions extends Disposable { rightMargin: this._layoutConfiguration.cellRightMargin, runGutter: this._layoutConfiguration.cellRunGutter, dragAndDropEnabled: this._layoutConfiguration.dragAndDropEnabled, - fontSize: this._layoutConfiguration.fontSize + fontSize: this._layoutConfiguration.fontSize, + markupFontSize: this._layoutConfiguration.markupFontSize, }; } @@ -470,7 +482,8 @@ export class NotebookOptions extends Disposable { rightMargin: 0, runGutter: 0, dragAndDropEnabled: false, - fontSize: this._layoutConfiguration.fontSize + fontSize: this._layoutConfiguration.fontSize, + markupFontSize: this._layoutConfiguration.markupFontSize, }; } diff --git a/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts b/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts index 4881945cbf4..cab9ddc108d 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts @@ -374,7 +374,7 @@ suite('NotebookCommon', () => { assert.strictEqual(diffViewModels.viewModels[0].type, 'unchanged'); assert.strictEqual(diffViewModels.viewModels[0].checkIfOutputsModified(), false); assert.strictEqual(diffViewModels.viewModels[1].type, 'modified'); - assert.strictEqual(diffViewModels.viewModels[1].checkIfOutputsModified(), true); + assert.deepStrictEqual(diffViewModels.viewModels[1].checkIfOutputsModified(), { reason: undefined }); }); }); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index a011b3b1200..87ed13ecff2 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -910,11 +910,11 @@ class CommandColumnRenderer implements ITableRenderer { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 44b34029af1..f12bf278726 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -122,7 +122,7 @@ export class SettingsEditor2 extends EditorPane { return false; } return type === SettingValueType.Enum || - type === SettingValueType.StringOrEnumArray || + type === SettingValueType.Array || type === SettingValueType.BooleanObject || type === SettingValueType.Object || type === SettingValueType.Complex || diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index c4676688baf..88f090a63ac 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -302,6 +302,46 @@ function createObjectValueSuggester(element: SettingsTreeSettingElement): IObjec }; } +function isNonNullableNumericType(type: unknown): type is 'number' | 'integer' { + return type === 'number' || type === 'integer'; +} + +function parseNumericObjectValues(dataElement: SettingsTreeSettingElement, v: Record): Record { + const newRecord: Record = {}; + for (const key in v) { + // Set to true/false once we're sure of the answer + let keyMatchesNumericProperty: boolean | undefined; + const patternProperties = dataElement.setting.objectPatternProperties; + const properties = dataElement.setting.objectProperties; + const additionalProperties = dataElement.setting.objectAdditionalProperties; + + // Match the current record key against the properties of the object + if (properties) { + for (const propKey in properties) { + if (propKey === key) { + keyMatchesNumericProperty = isNonNullableNumericType(properties[propKey].type); + break; + } + } + } + if (keyMatchesNumericProperty === undefined && patternProperties) { + for (const patternKey in patternProperties) { + if (key.match(patternKey)) { + keyMatchesNumericProperty = isNonNullableNumericType(patternProperties[patternKey].type); + break; + } + } + } + if (keyMatchesNumericProperty === undefined && additionalProperties && typeof additionalProperties !== 'boolean') { + if (isNonNullableNumericType(additionalProperties.type)) { + keyMatchesNumericProperty = true; + } + } + newRecord[key] = keyMatchesNumericProperty ? Number(v[key]) : v[key]; + } + return newRecord; +} + function getListDisplayValue(element: SettingsTreeSettingElement): IListDataItem[] { if (!element.value || !isArray(element.value)) { return []; @@ -1101,8 +1141,7 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr common.toDispose.add( listWidget.onDidChangeList(e => { const newList = this.computeNewList(template, e); - this.onDidChangeList(template, newList); - if (newList !== null && template.onChange) { + if (template.onChange) { template.onChange(newList); } }) @@ -1111,18 +1150,6 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr return template; } - private onDidChangeList(template: ISettingListItemTemplate, newList: string[] | undefined | null): void { - if (!template.context || newList === null) { - return; - } - - this._onDidChangeSetting.fire({ - key: template.context.setting.key, - value: newList, - type: template.context.valueType - }); - } - private computeNewList(template: ISettingListItemTemplate, e: ISettingListChangeEvent): string[] | undefined { if (template.context) { let newValue: string[] = []; @@ -1179,7 +1206,7 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr super.renderSettingElement(element, index, templateData); } - protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingListItemTemplate, onChange: (value: string[] | undefined) => void): void { + protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingListItemTemplate, onChange: (value: string[] | number[] | undefined) => void): void { const value = getListDisplayValue(dataElement); const keySuggester = dataElement.setting.enum ? createArraySuggester(dataElement) : undefined; template.listWidget.setValue(value, { @@ -1192,9 +1219,16 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr template.listWidget.cancelEdit(); })); - template.onChange = (v) => { - onChange(v); - renderArrayValidations(dataElement, template, v, false); + template.onChange = (v: string[] | undefined) => { + if (v && !renderArrayValidations(dataElement, template, v, false)) { + const itemType = dataElement.setting.arrayItemType; + const arrToSave = isNonNullableNumericType(itemType) ? v.map(a => +a) : v; + onChange(arrToSave); + } else { + // Save the setting unparsed and containing the errors. + // renderArrayValidations will render relevant error messages. + onChange(v); + } }; renderArrayValidations(dataElement, template, value.map(v => v.value.data.toString()), true); @@ -1335,8 +1369,14 @@ export class SettingObjectRenderer extends AbstractSettingObjectRenderer impleme })); template.onChange = (v: Record | undefined) => { - onChange(v); - renderArrayValidations(dataElement, template, v, false); + if (v && !renderArrayValidations(dataElement, template, v, false)) { + const parsedRecord = parseNumericObjectValues(dataElement, v); + onChange(parsedRecord); + } else { + // Save the setting unparsed and containing the errors. + // renderArrayValidations will render relevant error messages. + onChange(v); + } }; renderArrayValidations(dataElement, template, dataElement.value, true); } @@ -2000,12 +2040,15 @@ function renderValidations(dataElement: SettingsTreeSettingElement, template: IS return false; } +/** + * Validate and render any error message for arrays. Returns true if the value is invalid. + */ function renderArrayValidations( dataElement: SettingsTreeSettingElement, template: ISettingListItemTemplate | ISettingObjectItemTemplate, value: string[] | Record | undefined, calledOnStartup: boolean -) { +): boolean { template.containerElement.classList.add('invalid-input'); if (dataElement.setting.validator) { const errMsg = dataElement.setting.validator(value); @@ -2015,12 +2058,13 @@ function renderArrayValidations( const validationError = localize('validationError', "Validation Error."); template.containerElement.setAttribute('aria-label', [dataElement.setting.key, validationError, errMsg].join(' ')); if (!calledOnStartup) { ariaAlert(validationError + ' ' + errMsg); } - return; + return true; } else { template.containerElement.setAttribute('aria-label', dataElement.setting.key); template.containerElement.classList.remove('invalid-input'); } } + return false; } function cleanRenderedMarkdown(element: Node): void { @@ -2154,7 +2198,7 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate deleteActionTooltip: localize('removeItem', "Remove Item"), editActionTooltip: localize('editItem', "Edit Item"), addButtonLabel: localize('addItem', "Add Item"), - inputPlaceholder: localize('itemInputPlaceholder', "String Item..."), + inputPlaceholder: localize('itemInputPlaceholder', "Item..."), siblingInputPlaceholder: localize('listSiblingInputPlaceholder', "Sibling..."), }; } diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 5acad42f25c..dff2881e612 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -15,13 +15,13 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { FilterViewPaneContainer } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { AutomaticPortForwarding, ForwardedPortsView, PortRestore, VIEWLET_ID } from 'vs/workbench/contrib/remote/browser/remoteExplorer'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewDescriptor, IViewsRegistry, Extensions, ViewContainerLocation, IViewContainersRegistry, IViewDescriptorService } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IExtensionDescription, isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -498,7 +498,7 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements } private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser, helpInformation: HelpInformation[]) { - if (!isProposedApiEnabled(extension.description)) { + if (!isProposedApiEnabled(extension.description, undefined)) { return; } diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index add862a216a..a6fa4499a8d 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -771,9 +771,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this._recentlyUsedTasks; } - private getFolderFromTaskKey(key: string): string | undefined { - const keyValue: { folder: string | undefined } = JSON.parse(key); - return keyValue.folder; + private getFolderFromTaskKey(key: string): { folder: string | undefined, isWorkspaceFile: boolean | undefined } { + const keyValue: { folder: string | undefined, id: string | undefined } = JSON.parse(key); + return { + folder: keyValue.folder, isWorkspaceFile: keyValue.id?.endsWith(TaskSourceKind.WorkspaceFile) + }; } public async readRecentTasks(): Promise<(Task | ConfiguringTask)[]> { @@ -782,40 +784,55 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer folderMap[folder.uri.toString()] = folder; }); const folderToTasksMap: Map = new Map(); + const workspaceToTaskMap: Map = new Map(); const recentlyUsedTasks = this.getRecentlyUsedTasks(); const tasks: (Task | ConfiguringTask)[] = []; - for (const entry of recentlyUsedTasks.entries()) { - const key = entry[0]; - const task = JSON.parse(entry[1]); - const folder = this.getFolderFromTaskKey(key); - if (folder && !folderToTasksMap.has(folder)) { - folderToTasksMap.set(folder, []); + + function addTaskToMap(map: Map, folder: string | undefined, task: any) { + if (folder && !map.has(folder)) { + map.set(folder, []); } if (folder && (folderMap[folder] || (folder === USER_TASKS_GROUP_KEY)) && task) { - folderToTasksMap.get(folder).push(task); + map.get(folder).push(task); } } + + for (const entry of recentlyUsedTasks.entries()) { + const key = entry[0]; + const task = JSON.parse(entry[1]); + const folderInfo = this.getFolderFromTaskKey(key); + addTaskToMap(folderInfo.isWorkspaceFile ? workspaceToTaskMap : folderToTasksMap, folderInfo.folder, task); + } + const readTasksMap: Map = new Map(); - for (const key of folderToTasksMap.keys()) { - let custom: CustomTask[] = []; - let customized: IStringDictionary = Object.create(null); - await this.computeTasksForSingleConfig(folderMap[key] ?? await this.getAFolder(), { - version: '2.0.0', - tasks: folderToTasksMap.get(key) - }, TaskRunSource.System, custom, customized, folderMap[key] ? TaskConfig.TaskConfigSource.TasksJson : TaskConfig.TaskConfigSource.User, true); - custom.forEach(task => { - const taskKey = task.getRecentlyUsedKey(); - if (taskKey) { - readTasksMap.set(taskKey, task); - } - }); - for (const configuration in customized) { - const taskKey = customized[configuration].getRecentlyUsedKey(); - if (taskKey) { - readTasksMap.set(taskKey, customized[configuration]); + async function readTasks(that: AbstractTaskService, map: Map, isWorkspaceFile: boolean) { + for (const key of map.keys()) { + let custom: CustomTask[] = []; + let customized: IStringDictionary = Object.create(null); + const taskConfigSource = (folderMap[key] + ? (isWorkspaceFile + ? TaskConfig.TaskConfigSource.WorkspaceFile : TaskConfig.TaskConfigSource.TasksJson) + : TaskConfig.TaskConfigSource.User); + await that.computeTasksForSingleConfig(folderMap[key] ?? await that.getAFolder(), { + version: '2.0.0', + tasks: map.get(key) + }, TaskRunSource.System, custom, customized, taskConfigSource, true); + custom.forEach(task => { + const taskKey = task.getRecentlyUsedKey(); + if (taskKey) { + readTasksMap.set(taskKey, task); + } + }); + for (const configuration in customized) { + const taskKey = customized[configuration].getRecentlyUsedKey(); + if (taskKey) { + readTasksMap.set(taskKey, customized[configuration]); + } } } } + await readTasks(this, folderToTasksMap, false); + await readTasks(this, workspaceToTaskMap, true); for (const key of recentlyUsedTasks.keys()) { if (readTasksMap.has(key)) { diff --git a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts index 41e68267fbf..ced0fd8cf8f 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts @@ -13,6 +13,7 @@ import { ITaskService, Task } from 'vs/workbench/contrib/tasks/common/taskServic import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalStatus } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; interface TerminalData { terminal: ITerminalInstance; @@ -21,7 +22,7 @@ interface TerminalData { } const TASK_TERMINAL_STATUS_ID = 'task_terminal_status'; -const ACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: new Codicon('loading~spin', Codicon.loading), severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.active', "Task is running") }; +const ACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: spinningLoading, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.active', "Task is running") }; const SUCCEEDED_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.check, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.succeeded', "Task succeeded") }; const SUCCEEDED_INACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.check, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.succeededInactive', "Task succeeded and waiting...") }; const FAILED_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.error, severity: Severity.Error, tooltip: nls.localize('taskTerminalStatus.errors', "Task has errors") }; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 4d4726e6371..3f12b29d53c 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1448,8 +1448,16 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } this.collectMatcherVariables(variables, task.configurationProperties.problemMatchers); - if (task.command.runtime === RuntimeType.CustomExecution && CustomTask.is(task)) { - this.collectDefinitionVariables(variables, task._source.config.element); + if (task.command.runtime === RuntimeType.CustomExecution && (CustomTask.is(task) || ContributedTask.is(task))) { + let definition: any; + if (CustomTask.is(task)) { + definition = task._source.config.element; + } else { + definition = Objects.deepClone(task.defines); + delete definition._key; + delete definition.type; + } + this.collectDefinitionVariables(variables, definition); } } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index d76761635fd..9c42f1dfa4c 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -17,7 +17,7 @@ import type { Terminal, IViewportRange, ILinkProvider } from 'xterm'; import { Schemas } from 'vs/base/common/network'; import { posix, win32 } from 'vs/base/common/path'; import { ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { OperatingSystem, isMacintosh, OS, isWindows } from 'vs/base/common/platform'; +import { OperatingSystem, isMacintosh, OS } from 'vs/base/common/platform'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { TerminalProtocolLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider'; import { TerminalValidatedLocalLinkProvider, lineAndColumnClause, unixLocalLinkClause, winLocalLinkClause, winDrivePrefix, winLineAndColumnMatchIndex, unixLineAndColumnMatchIndex, lineAndColumnClauseGroupCount } from 'vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider'; @@ -213,7 +213,7 @@ export class TerminalLinkManager extends DisposableStore { if (uri.scheme === Schemas.file) { // Just using fsPath here is unsafe: https://github.com/microsoft/vscode/issues/109076 const fsPath = uri.fsPath; - this._handleLocalLink(((this._osPath.sep === posix.sep) && isWindows) ? fsPath.replace(/\\/g, posix.sep) : fsPath); + this._handleLocalLink(((this._osPath.sep === posix.sep) && this._processManager.os === OperatingSystem.Windows) ? fsPath.replace(/\\/g, posix.sep) : fsPath); return; } diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index d32a431a437..6ddd5af7baf 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -337,7 +337,7 @@ class RemoteTerminalBackend extends Disposable implements ITerminalBackend { return this._remoteTerminalChannel?.getWslPath(original) || original; } - setTerminalLayoutInfo(layout: ITerminalsLayoutInfoById): Promise { + async setTerminalLayoutInfo(layout?: ITerminalsLayoutInfoById): Promise { if (!this._remoteTerminalChannel) { throw new Error(`Cannot call setActiveInstanceId when there is no remote`); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 1fd36dbf73c..7f2d006ee3f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, TerminalIcon, TitleEventSource, TerminalShellType, IExtensionTerminalProfile, TerminalLocation, ProcessPropertyType, ProcessCapability, IProcessPropertyMap } from 'vs/platform/terminal/common/terminal'; -import { ICommandTracker, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalBackend, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ICommandTracker, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalBackend, ITerminalProcessExtHostProxy, IRegisterContributedProfileArgs } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as XTermTerminal } from 'xterm'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; @@ -19,6 +19,7 @@ import { IEditableData } from 'vs/workbench/common/views'; import { DeserializedTerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorSerializer'; import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; +import { IKeyMods } from 'vs/platform/quickinput/common/quickInput'; export const ITerminalService = createDecorator('terminalService'); export const ITerminalEditorService = createDecorator('terminalEditorService'); @@ -73,6 +74,11 @@ export const enum Direction { Down = 3 } +export interface IQuickPickTerminalObject { + config: IRegisterContributedProfileArgs | ITerminalProfile | { profile: IExtensionTerminalProfile, options: { icon?: string, color?: string } } | undefined, + keyMods: IKeyMods | undefined +} + export interface ITerminalGroup { activeInstance: ITerminalInstance | undefined; terminalInstances: ITerminalInstance[]; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index af7e864387b..6bca2bc4769 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -414,7 +414,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { this._terminalInstances.splice(index, 0, instance); if (this._splitPaneContainer) { this._splitPaneContainer.remove(instance); - this._splitPaneContainer.split(instance, sourceIndex < index ? index - 1 : index); + this._splitPaneContainer.split(instance, index); } this._onInstancesChanged.fire(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 8e1db030c63..08ffd2a1e3a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -615,7 +615,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { lineDataEventAddon.setOperatingSystem(this._processManager.os); } if (this._processManager.os === OperatingSystem.Windows) { - xterm.raw.setOption('windowsMode', processTraits.requiresWindowsMode || false); + xterm.raw.options.windowsMode = processTraits.requiresWindowsMode || false; } this._linkManager = this._instantiationService.createInstance(TerminalLinkManager, xterm.raw, this._processManager!); this._areLinksReady = true; @@ -1088,7 +1088,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); this._processManager.onEnvironmentVariableInfoChanged(e => this._onEnvironmentVariableInfoChanged(e)); this._processManager.onPtyDisconnect(() => { - this._safeSetOption('disableStdin', true); + if (this.xterm) { + this.xterm.raw.options.disableStdin = true; + } this.statusList.add({ id: TerminalStatus.Disconnected, severity: Severity.Error, @@ -1097,7 +1099,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); }); this._processManager.onPtyReconnect(() => { - this._safeSetOption('disableStdin', false); + if (this.xterm) { + this.xterm.raw.options.disableStdin = false; + } this.statusList.remove(TerminalStatus.Disconnected); }); } @@ -1231,7 +1235,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit)); } // Disable all input if the terminal is exiting and listen for next keypress - xterm.raw.setOption('disableStdin', true); + xterm.raw.options.disableStdin = true; if (xterm.raw.textarea) { this._attachPressAnyKeyToCloseListener(xterm.raw); } @@ -1313,7 +1317,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Clean up waitOnExit state if (this._isExiting && this._shellLaunchConfig.waitOnExit) { - this.xterm.raw.setOption('disableStdin', false); + this.xterm.raw.options.disableStdin = false; this._isExiting = false; } } @@ -1397,7 +1401,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._navigationModeAddon?.dispose(); this._navigationModeAddon = undefined; } - this.xterm!.raw.setOption('screenReaderMode', isEnabled); + this.xterm!.raw.options.screenReaderMode = isEnabled; } private _setCommandsToSkipShell(commands: string[]): void { @@ -1407,16 +1411,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }).concat(commands); } - private _safeSetOption(key: string, value: any): void { - if (!this.xterm) { - return; - } - - if (this.xterm.raw.getOption(key) !== value) { - this.xterm.raw.setOption(key, value); - } - } - layout(dimension: dom.Dimension): void { this._lastLayoutDimensions = dimension; if (this.disableLayout) { @@ -1456,12 +1450,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (this._isVisible && this._layoutSettingsChanged) { const font = this.xterm.getFont(); const config = this._configHelper.config; - this._safeSetOption('letterSpacing', font.letterSpacing); - this._safeSetOption('lineHeight', font.lineHeight); - this._safeSetOption('fontSize', font.fontSize); - this._safeSetOption('fontFamily', font.fontFamily); - this._safeSetOption('fontWeight', config.fontWeight); - this._safeSetOption('fontWeightBold', config.fontWeightBold); + this.xterm.raw.options.letterSpacing = font.letterSpacing; + this.xterm.raw.options.lineHeight = font.lineHeight; + this.xterm.raw.options.fontSize = font.fontSize; + this.xterm.raw.options.fontFamily = font.fontFamily; + this.xterm.raw.options.fontWeight = config.fontWeight; + this.xterm.raw.options.fontWeightBold = config.fontWeightBold; // Any of the above setting changes could have changed the dimensions of the // terminal, re-evaluate now. @@ -1787,8 +1781,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { async toggleEscapeSequenceLogging(): Promise { const xterm = await this._xtermReadyPromise; - const isDebug = xterm.raw.getOption('logLevel') === 'debug'; - xterm.raw.setOption('logLevel', isDebug ? 'info' : 'debug'); + xterm.raw.options.logLevel = xterm.raw.options.logLevel === 'debug' ? 'info' : 'debug'; } async getInitialCwd(): Promise { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts new file mode 100644 index 00000000000..e7dac04a1c1 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts @@ -0,0 +1,240 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { iconRegistry, Codicon } from 'vs/base/common/codicons'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IQuickInputService, IKeyMods, IPickOptions, IQuickPickSeparator, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { IExtensionTerminalProfile, ITerminalProfile, ITerminalProfileObject, TerminalSettingPrefix } from 'vs/platform/terminal/common/terminal'; +import { getUriClasses, getColorClass, getColorStyleElement } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; +import { configureTerminalProfileIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; +import * as nls from 'vs/nls'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IQuickPickTerminalObject } from 'vs/workbench/contrib/terminal/browser/terminal'; + + +type DefaultProfileName = string; +export class TerminalProfileQuickpick { + constructor( + @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @IThemeService private readonly _themeService: IThemeService + ) { } + + async showAndGetResult(type: 'setDefault' | 'createInstance'): Promise { + const platformKey = await this._terminalProfileService.getPlatformKey(); + const profilesKey = TerminalSettingPrefix.Profiles + platformKey; + const result = await this._createAndShow(type); + const defaultProfileKey = `${TerminalSettingPrefix.DefaultProfile}${platformKey}`; + if (!result) { + return; + } + if (type === 'setDefault') { + if ('command' in result.profile) { + return; // Should never happen + } else if ('id' in result.profile) { + // extension contributed profile + await this._configurationService.updateValue(defaultProfileKey, result.profile.title, ConfigurationTarget.USER); + return { + config: { + extensionIdentifier: result.profile.extensionIdentifier, + id: result.profile.id, + title: result.profile.title, + options: { + color: result.profile.color, + icon: result.profile.icon + } + }, + keyMods: result.keyMods + }; + } + + // Add the profile to settings if necessary + if ('isAutoDetected' in result.profile) { + const profilesConfig = await this._configurationService.getValue(profilesKey); + if (typeof profilesConfig === 'object') { + const newProfile: ITerminalProfileObject = { + path: result.profile.path + }; + if (result.profile.args) { + newProfile.args = result.profile.args; + } + (profilesConfig as { [key: string]: ITerminalProfileObject })[result.profile.profileName] = newProfile; + } + await this._configurationService.updateValue(profilesKey, profilesConfig, ConfigurationTarget.USER); + } + // Set the default profile + await this._configurationService.updateValue(defaultProfileKey, result.profileName, ConfigurationTarget.USER); + } else if (type === 'createInstance') { + if ('id' in result.profile) { + return { + config: { + extensionIdentifier: result.profile.extensionIdentifier, + id: result.profile.id, + title: result.profile.title, + options: { + icon: result.profile.icon, + color: result.profile.color, + } + }, + keyMods: result.keyMods + }; + } else { + return { config: result.profile, keyMods: result.keyMods }; + } + } + // for tests + return 'profileName' in result.profile ? result.profile.profileName : result.profile.title; + } + + private async _createAndShow(type: 'setDefault' | 'createInstance'): Promise { + const platformKey = await this._terminalProfileService.getPlatformKey(); + const profiles = this._terminalProfileService.availableProfiles; + const profilesKey = TerminalSettingPrefix.Profiles + platformKey; + const defaultProfileName = this._terminalProfileService.getDefaultProfileName(); + let keyMods: IKeyMods | undefined; + const options: IPickOptions = { + placeHolder: type === 'createInstance' ? nls.localize('terminal.integrated.selectProfileToCreate', "Select the terminal profile to create") : nls.localize('terminal.integrated.chooseDefaultProfile', "Select your default terminal profile"), + onDidTriggerItemButton: async (context) => { + if ('command' in context.item.profile) { + return; + } + if ('id' in context.item.profile) { + return; + } + const configProfiles: { [key: string]: any } = this._configurationService.getValue(TerminalSettingPrefix.Profiles + platformKey); + const existingProfiles = !!configProfiles ? Object.keys(configProfiles) : []; + const name = await this._quickInputService.input({ + prompt: nls.localize('enterTerminalProfileName', "Enter terminal profile name"), + value: context.item.profile.profileName, + validateInput: async input => { + if (existingProfiles.includes(input)) { + return nls.localize('terminalProfileAlreadyExists', "A terminal profile already exists with that name"); + } + return undefined; + } + }); + if (!name) { + return; + } + const newConfigValue: { [key: string]: ITerminalProfileObject } = { ...configProfiles } ?? {}; + newConfigValue[name] = { + path: context.item.profile.path, + args: context.item.profile.args + }; + await this._configurationService.updateValue(profilesKey, newConfigValue, ConfigurationTarget.USER); + }, + onKeyMods: mods => keyMods = mods + }; + + // Build quick pick items + const quickPickItems: (IProfileQuickPickItem | IQuickPickSeparator)[] = []; + const configProfiles = profiles.filter(e => !e.isAutoDetected); + const autoDetectedProfiles = profiles.filter(e => e.isAutoDetected); + + if (configProfiles.length > 0) { + quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles', "profiles") }); + quickPickItems.push(...this._sortProfileQuickPickItems(configProfiles.map(e => this._createProfileQuickPickItem(e)), defaultProfileName!)); + } + + quickPickItems.push({ type: 'separator', label: nls.localize('ICreateContributedTerminalProfileOptions', "contributed") }); + const contributedProfiles: IProfileQuickPickItem[] = []; + for (const contributed of this._terminalProfileService.contributedProfiles) { + if (typeof contributed.icon === 'string' && contributed.icon.startsWith('$(')) { + contributed.icon = contributed.icon.substring(2, contributed.icon.length - 1); + } + const icon = contributed.icon && typeof contributed.icon === 'string' ? (iconRegistry.get(contributed.icon) || Codicon.terminal) : Codicon.terminal; + const uriClasses = getUriClasses(contributed, this._themeService.getColorTheme().type, true); + const colorClass = getColorClass(contributed); + const iconClasses = []; + if (uriClasses) { + iconClasses.push(...uriClasses); + } + if (colorClass) { + iconClasses.push(colorClass); + } + contributedProfiles.push({ + label: `$(${icon.id}) ${contributed.title}`, + profile: { + extensionIdentifier: contributed.extensionIdentifier, + title: contributed.title, + icon: contributed.icon, + id: contributed.id, + color: contributed.color + }, + profileName: contributed.title, + iconClasses + }); + } + + if (contributedProfiles.length > 0) { + quickPickItems.push(...this._sortProfileQuickPickItems(contributedProfiles, defaultProfileName!)); + } + + if (autoDetectedProfiles.length > 0) { + quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles.detected', "detected") }); + quickPickItems.push(...this._sortProfileQuickPickItems(autoDetectedProfiles.map(e => this._createProfileQuickPickItem(e)), defaultProfileName!)); + } + const styleElement = getColorStyleElement(this._themeService.getColorTheme()); + document.body.appendChild(styleElement); + + const result = await this._quickInputService.pick(quickPickItems, options); + document.body.removeChild(styleElement); + if (!result) { + return undefined; + } + if (keyMods) { + result.keyMods = keyMods; + } + return result; + } + + private _createProfileQuickPickItem(profile: ITerminalProfile): IProfileQuickPickItem { + const buttons: IQuickInputButton[] = [{ + iconClass: ThemeIcon.asClassName(configureTerminalProfileIcon), + tooltip: nls.localize('createQuickLaunchProfile', "Configure Terminal Profile") + }]; + const icon = (profile.icon && ThemeIcon.isThemeIcon(profile.icon)) ? profile.icon : Codicon.terminal; + const label = `$(${icon.id}) ${profile.profileName}`; + const colorClass = getColorClass(profile); + const iconClasses = []; + if (colorClass) { + iconClasses.push(colorClass); + } + + if (profile.args) { + if (typeof profile.args === 'string') { + return { label, description: `${profile.path} ${profile.args}`, profile, profileName: profile.profileName, buttons, iconClasses }; + } + const argsString = profile.args.map(e => { + if (e.includes(' ')) { + return `"${e.replace('/"/g', '\\"')}"`; + } + return e; + }).join(' '); + return { label, description: `${profile.path} ${argsString}`, profile, profileName: profile.profileName, buttons, iconClasses }; + } + return { label, description: profile.path, profile, profileName: profile.profileName, buttons, iconClasses }; + } + + private _sortProfileQuickPickItems(items: IProfileQuickPickItem[], defaultProfileName: string) { + return items.sort((a, b) => { + if (b.profileName === defaultProfileName) { + return 1; + } + if (a.profileName === defaultProfileName) { + return -1; + } + return a.profileName.localeCompare(b.profileName); + }); + } +} + +export interface IProfileQuickPickItem extends IQuickPickItem { + profile: ITerminalProfile | IExtensionTerminalProfile; + profileName: string; + keyMods?: IKeyMods | undefined; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts index 7e30984367a..00f3aa099d6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts @@ -11,12 +11,12 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { isMacintosh, isWeb, isWindows, OperatingSystem, OS } from 'vs/base/common/platform'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ITerminalProfile, IExtensionTerminalProfile, TerminalSettingPrefix, TerminalSettingId, ICreateContributedTerminalProfileOptions, ITerminalProfileObject, IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; +import { ITerminalProfile, IExtensionTerminalProfile, TerminalSettingPrefix, TerminalSettingId, ITerminalProfileObject, IShellLaunchConfig } from 'vs/platform/terminal/common/terminal'; import { registerTerminalDefaultProfileConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration'; import { terminalIconsEqual, terminalProfileArgsMatch } from 'vs/platform/terminal/common/terminalProfiles'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { refreshTerminalActions } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { ITerminalProfileProvider, ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IRegisterContributedProfileArgs, ITerminalProfileProvider, ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -61,7 +61,7 @@ export class TerminalProfileService implements ITerminalProfileService { this._extensionService.onDidChangeExtensions(() => this.refreshAvailableProfiles()); this._configurationService.onDidChangeConfiguration(async e => { - const platformKey = await this._getPlatformKey(); + const platformKey = await this.getPlatformKey(); if (e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + platformKey) || e.affectsConfiguration(TerminalSettingPrefix.Profiles + platformKey) || e.affectsConfiguration(TerminalSettingId.UseWslProfiles)) { @@ -112,7 +112,7 @@ export class TerminalProfileService implements ITerminalProfileService { } private async _updateContributedProfiles(): Promise { - const platformKey = await this._getPlatformKey(); + const platformKey = await this.getPlatformKey(); const excludedContributedProfiles: string[] = []; const configProfiles: { [key: string]: any } = this._configurationService.getValue(TerminalSettingPrefix.Profiles + platformKey); for (const [profileName, value] of Object.entries(configProfiles)) { @@ -136,7 +136,7 @@ export class TerminalProfileService implements ITerminalProfileService { if (!primaryBackend) { return this._availableProfiles || []; } - const platform = await this._getPlatformKey(); + const platform = await this.getPlatformKey(); this._defaultProfileName = this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${platform}`) ?? undefined; return primaryBackend.getProfiles(this._configurationService.getValue(`${TerminalSettingPrefix.Profiles}${platform}`), this._defaultProfileName, includeDetectedProfiles); } @@ -151,7 +151,7 @@ export class TerminalProfileService implements ITerminalProfileService { refreshTerminalActions(profiles); } - private async _getPlatformKey(): Promise { + async getPlatformKey(): Promise { const env = await this._remoteAgentService.getEnvironment(); if (env) { return env.os === OperatingSystem.Windows ? 'windows' : (env.os === OperatingSystem.Macintosh ? 'osx' : 'linux'); @@ -169,19 +169,19 @@ export class TerminalProfileService implements ITerminalProfileService { return toDisposable(() => this._profileProviders.delete(id)); } - async registerContributedProfile(extensionIdentifier: string, id: string, title: string, options: ICreateContributedTerminalProfileOptions): Promise { - const platformKey = await this._getPlatformKey(); + async registerContributedProfile(args: IRegisterContributedProfileArgs): Promise { + const platformKey = await this.getPlatformKey(); const profilesConfig = await this._configurationService.getValue(`${TerminalSettingPrefix.Profiles}${platformKey}`); if (typeof profilesConfig === 'object') { const newProfile: IExtensionTerminalProfile = { - extensionIdentifier: extensionIdentifier, - icon: options.icon, - id, - title: title, - color: options.color + extensionIdentifier: args.extensionIdentifier, + icon: args.options.icon, + id: args.id, + title: args.title, + color: args.options.color }; - (profilesConfig as { [key: string]: ITerminalProfileObject })[title] = newProfile; + (profilesConfig as { [key: string]: ITerminalProfileObject })[args.title] = newProfile; } await this._configurationService.updateValue(`${TerminalSettingPrefix.Profiles}${platformKey}`, profilesConfig, ConfigurationTarget.USER); return; @@ -191,7 +191,7 @@ export class TerminalProfileService implements ITerminalProfileService { // prevents recursion with the MainThreadTerminalService call to create terminal // and defers to the provided launch config when an executable is provided if (shellLaunchConfig && !shellLaunchConfig.extHostTerminalId && !('executable' in shellLaunchConfig)) { - const key = await this._getPlatformKey(); + const key = await this.getPlatformKey(); const defaultProfileName = this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${key}`); const contributedDefaultProfile = this.contributedProfiles.find(p => p.title === defaultProfileName); return contributedDefaultProfile; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 05016499524..ef444259b8a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -10,18 +10,16 @@ import { debounce } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { isMacintosh, isWeb, isWindows, OperatingSystem } from 'vs/base/common/platform'; +import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import * as nls from 'vs/nls'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IKeyMods, IPickOptions, IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ICreateContributedTerminalProfileOptions, IExtensionTerminalProfile, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalProfileObject, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalLocation, TerminalLocationString, TerminalSettingPrefix } from 'vs/platform/terminal/common/terminal'; +import { ICreateContributedTerminalProfileOptions, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalLocation, TerminalLocationString } from 'vs/platform/terminal/common/terminal'; import { iconForeground } from 'vs/platform/theme/common/colorRegistry'; import { IconDefinition } from 'vs/platform/theme/common/iconRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; @@ -31,8 +29,7 @@ import { IEditableData, IViewsService } from 'vs/workbench/common/views'; import { ICreateTerminalOptions, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalExternalLinkProvider, ITerminalFindHost, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalEditor } from 'vs/workbench/contrib/terminal/browser/terminalEditor'; -import { getColorClass, getColorStyleContent, getColorStyleElement, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; -import { configureTerminalProfileIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; +import { getColorStyleContent, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { getInstanceFromResource, getTerminalUri, parseTerminalUri } from 'vs/workbench/contrib/terminal/browser/terminalUri'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalBackend, ITerminalProcessExtHostProxy, ITerminalProfileService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -46,6 +43,8 @@ import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/ed import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { TerminalProfileQuickpick } from 'vs/workbench/contrib/terminal/browser/terminalProfileQuickpick'; +import { IKeyMods } from 'vs/base/parts/quickinput/common/quickInput'; export class TerminalService implements ITerminalService { declare _serviceBrand: undefined; @@ -66,7 +65,7 @@ export class TerminalService implements ITerminalService { private _configHelper: TerminalConfigHelper; private _remoteTerminalsInitPromise: Promise | undefined; private _localTerminalsInitPromise: Promise | undefined; - private _connectionState: TerminalConnectionState; + private _connectionState: TerminalConnectionState = TerminalConnectionState.Connecting; private _nativeDelegate?: ITerminalServiceNativeDelegate; private _shutdownWindowCount?: number; @@ -144,8 +143,6 @@ export class TerminalService implements ITerminalService { @IDialogService private _dialogService: IDialogService, @IInstantiationService private _instantiationService: IInstantiationService, @IRemoteAgentService private _remoteAgentService: IRemoteAgentService, - @IQuickInputService private _quickInputService: IQuickInputService, - @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @@ -154,7 +151,6 @@ export class TerminalService implements ITerminalService { @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IEditorResolverService editorResolverService: IEditorResolverService, @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, - @IThemeService private readonly _themeService: IThemeService, @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, @IExtensionService private readonly _extensionService: IExtensionService, @INotificationService private readonly _notificationService: INotificationService @@ -236,29 +232,67 @@ export class TerminalService implements ITerminalService { } }); - const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; - - // Connect to the extension host if it's there, set the connection state to connected when - // it's done. This should happen even when there is no extension host. - this._connectionState = TerminalConnectionState.Connecting; + // Create async as the class depends on `this` + timeout(0).then(() => this._instantiationService.createInstance(TerminalEditorStyle, document.head)); + } + async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise { + const quickPick = this._instantiationService.createInstance(TerminalProfileQuickpick); + const result = await quickPick.showAndGetResult(type); + if (!result) { + return; + } + if (typeof result === 'string') { + return; + } + let keyMods: IKeyMods | undefined = result.keyMods; + if (type === 'createInstance') { + const activeInstance = this.getDefaultInstanceHost().activeInstance; + let instance; - const isPersistentRemote = !!this._environmentService.remoteAuthority && enableTerminalReconnection; + if (result.config && 'id' in result?.config) { + await this.createContributedTerminalProfile(result.config.extensionIdentifier, result.config.id, { + icon: result.config.options?.icon, + color: result.config.options?.color, + location: !!(keyMods?.alt && activeInstance) ? { splitActiveTerminal: true } : this.defaultLocation + }); + return; + } else if (result.config && 'profileName' in result.config) { + if (keyMods?.alt && activeInstance) { + // create split, only valid if there's an active instance + instance = await this.createTerminal({ location: { parentTerminal: activeInstance }, config: result.config }); + } else { + instance = await this.createTerminal({ location: this.defaultLocation, config: result.config, cwd }); + } + } - if (isPersistentRemote) { - this._remoteTerminalsInitPromise = this._reconnectToRemoteTerminals(); - } else if (enableTerminalReconnection) { - this._localTerminalsInitPromise = this._reconnectToLocalTerminals(); - } else { - this._connectionState = TerminalConnectionState.Connected; + if (instance && this.defaultLocation !== TerminalLocation.Editor) { + this._terminalGroupService.showPanel(true); + this.setActiveInstance(instance); + return instance; + } } - - // Create async as the class depends on `this` - timeout(0).then(() => this._instantiationService.createInstance(TerminalEditorStyle, document.head)); + return undefined; } handleNewRegisteredBackend(backend: ITerminalBackend) { if (backend.remoteAuthority === this._environmentService.remoteAuthority) { this._primaryBackend = backend; + const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; + + // Connect to the extension host if it's there, set the connection state to connected when + // it's done. This should happen even when there is no extension host. + this._connectionState = TerminalConnectionState.Connecting; + + const isPersistentRemote = !!this._environmentService.remoteAuthority && enableTerminalReconnection; + + if (isPersistentRemote) { + this._remoteTerminalsInitPromise = this._reconnectToRemoteTerminals(); + } else if (enableTerminalReconnection) { + this._localTerminalsInitPromise = this._reconnectToLocalTerminals(); + } else { + this._connectionState = TerminalConnectionState.Connected; + } + backend.onDidRequestDetach(async (e) => { const instanceToDetach = this.getInstanceFromResource(getTerminalUri(e.workspaceId, e.instanceId)); if (instanceToDetach) { @@ -848,172 +882,6 @@ export class TerminalService implements ITerminalService { return !res.confirmed; } - private async _getPlatformKey(): Promise { - const env = await this._remoteAgentService.getEnvironment(); - if (env) { - return env.os === OperatingSystem.Windows ? 'windows' : (env.os === OperatingSystem.Macintosh ? 'osx' : 'linux'); - } - return isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); - } - - async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise { - let keyMods: IKeyMods | undefined; - const profiles = this._terminalProfileService.availableProfiles; - const platformKey = await this._getPlatformKey(); - const profilesKey = `${TerminalSettingPrefix.Profiles}${platformKey}`; - const defaultProfileKey = `${TerminalSettingPrefix.DefaultProfile}${platformKey}`; - const defaultProfileName = this._configurationService.getValue(defaultProfileKey); - - const options: IPickOptions = { - placeHolder: type === 'createInstance' ? nls.localize('terminal.integrated.selectProfileToCreate', "Select the terminal profile to create") : nls.localize('terminal.integrated.chooseDefaultProfile', "Select your default terminal profile"), - onDidTriggerItemButton: async (context) => { - if ('command' in context.item.profile) { - return; - } - if ('id' in context.item.profile) { - return; - } - const configProfiles = this._configurationService.getValue<{ [key: string]: ITerminalProfileObject }>(profilesKey); - const existingProfiles = configProfiles ? Object.keys(configProfiles) : []; - const name = await this._quickInputService.input({ - prompt: nls.localize('enterTerminalProfileName', "Enter terminal profile name"), - value: context.item.profile.profileName, - validateInput: async input => { - if (existingProfiles.includes(input)) { - return nls.localize('terminalProfileAlreadyExists', "A terminal profile already exists with that name"); - } - return undefined; - } - }); - if (!name) { - return; - } - const newConfigValue: { [key: string]: ITerminalProfileObject } = { ...configProfiles } ?? {}; - newConfigValue[name] = { - path: context.item.profile.path, - args: context.item.profile.args - }; - await this._configurationService.updateValue(profilesKey, newConfigValue, ConfigurationTarget.USER); - }, - onKeyMods: mods => keyMods = mods - }; - - // Build quick pick items - const quickPickItems: (IProfileQuickPickItem | IQuickPickSeparator)[] = []; - const configProfiles = profiles.filter(e => !e.isAutoDetected); - const autoDetectedProfiles = profiles.filter(e => e.isAutoDetected); - - if (configProfiles.length > 0) { - quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles', "profiles") }); - quickPickItems.push(...this._sortProfileQuickPickItems(configProfiles.map(e => this._createProfileQuickPickItem(e)), defaultProfileName)); - } - - quickPickItems.push({ type: 'separator', label: nls.localize('ICreateContributedTerminalProfileOptions', "contributed") }); - const contributedProfiles: IProfileQuickPickItem[] = []; - for (const contributed of this._terminalProfileService.contributedProfiles) { - if (typeof contributed.icon === 'string' && contributed.icon.startsWith('$(')) { - contributed.icon = contributed.icon.substring(2, contributed.icon.length - 1); - } - const icon = contributed.icon && typeof contributed.icon === 'string' ? (iconRegistry.get(contributed.icon) || Codicon.terminal) : Codicon.terminal; - const uriClasses = getUriClasses(contributed, this._themeService.getColorTheme().type, true); - const colorClass = getColorClass(contributed); - const iconClasses = []; - if (uriClasses) { - iconClasses.push(...uriClasses); - } - if (colorClass) { - iconClasses.push(colorClass); - } - contributedProfiles.push({ - label: `$(${icon.id}) ${contributed.title}`, - profile: { - extensionIdentifier: contributed.extensionIdentifier, - title: contributed.title, - icon: contributed.icon, - id: contributed.id, - color: contributed.color - }, - profileName: contributed.title, - iconClasses - }); - } - - if (contributedProfiles.length > 0) { - quickPickItems.push(...this._sortProfileQuickPickItems(contributedProfiles, defaultProfileName)); - } - - if (autoDetectedProfiles.length > 0) { - quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles.detected', "detected") }); - quickPickItems.push(...this._sortProfileQuickPickItems(autoDetectedProfiles.map(e => this._createProfileQuickPickItem(e)), defaultProfileName)); - } - const styleElement = getColorStyleElement(this._themeService.getColorTheme()); - document.body.appendChild(styleElement); - - const value = await this._quickInputService.pick(quickPickItems, options); - document.body.removeChild(styleElement); - if (!value) { - return; - } - if (type === 'createInstance') { - const activeInstance = this.getDefaultInstanceHost().activeInstance; - let instance; - - if ('id' in value.profile) { - await this.createContributedTerminalProfile(value.profile.extensionIdentifier, value.profile.id, { - icon: value.profile.icon, - color: value.profile.color, - location: !!(keyMods?.alt && activeInstance) ? { splitActiveTerminal: true } : this.defaultLocation - }); - return; - } else { - if (keyMods?.alt && activeInstance) { - // create split, only valid if there's an active instance - instance = await this.createTerminal({ location: { parentTerminal: activeInstance }, config: value.profile }); - } else { - instance = await this.createTerminal({ location: this.defaultLocation, config: value.profile, cwd }); - } - } - - if (instance && this.defaultLocation !== TerminalLocation.Editor) { - this._terminalGroupService.showPanel(true); - this.setActiveInstance(instance); - return instance; - } - } else { // setDefault - if ('command' in value.profile) { - return; // Should never happen - } else if ('id' in value.profile) { - // extension contributed profile - await this._configurationService.updateValue(defaultProfileKey, value.profile.title, ConfigurationTarget.USER); - - this._terminalProfileService.registerContributedProfile(value.profile.extensionIdentifier, value.profile.id, value.profile.title, { - color: value.profile.color, - icon: value.profile.icon - }); - return; - } - } - - // Add the profile to settings if necessary - if (value.profile.isAutoDetected) { - const profilesConfig = await this._configurationService.getValue(profilesKey); - if (typeof profilesConfig === 'object') { - const newProfile: ITerminalProfileObject = { - path: value.profile.path - }; - if (value.profile.args) { - newProfile.args = value.profile.args; - } - (profilesConfig as { [key: string]: ITerminalProfileObject })[value.profile.profileName] = newProfile; - } - await this._configurationService.updateValue(profilesKey, profilesConfig, ConfigurationTarget.USER); - } - // Set the default profile - await this._configurationService.updateValue(defaultProfileKey, value.profile.profileName, ConfigurationTarget.USER); - return undefined; - } - - getDefaultInstanceHost(): ITerminalInstanceHost { if (this.defaultLocation === TerminalLocation.Editor) { return this._terminalEditorService; @@ -1042,46 +910,6 @@ export class TerminalService implements ITerminalService { return instance?.target === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService; } - private _createProfileQuickPickItem(profile: ITerminalProfile): IProfileQuickPickItem { - const buttons: IQuickInputButton[] = [{ - iconClass: ThemeIcon.asClassName(configureTerminalProfileIcon), - tooltip: nls.localize('createQuickLaunchProfile', "Configure Terminal Profile") - }]; - const icon = (profile.icon && ThemeIcon.isThemeIcon(profile.icon)) ? profile.icon : Codicon.terminal; - const label = `$(${icon.id}) ${profile.profileName}`; - const colorClass = getColorClass(profile); - const iconClasses = []; - if (colorClass) { - iconClasses.push(colorClass); - } - - if (profile.args) { - if (typeof profile.args === 'string') { - return { label, description: `${profile.path} ${profile.args}`, profile, profileName: profile.profileName, buttons, iconClasses }; - } - const argsString = profile.args.map(e => { - if (e.includes(' ')) { - return `"${e.replace('/"/g', '\\"')}"`; - } - return e; - }).join(' '); - return { label, description: `${profile.path} ${argsString}`, profile, profileName: profile.profileName, buttons, iconClasses }; - } - return { label, description: profile.path, profile, profileName: profile.profileName, buttons, iconClasses }; - } - - private _sortProfileQuickPickItems(items: IProfileQuickPickItem[], defaultProfileName: string) { - return items.sort((a, b) => { - if (b.profileName === defaultProfileName) { - return 1; - } - if (a.profileName === defaultProfileName) { - return -1; - } - return a.profileName.localeCompare(b.profileName); - }); - } - private _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { const profile = shellLaunchConfigOrProfile; @@ -1297,11 +1125,6 @@ export class TerminalService implements ITerminalService { } } -interface IProfileQuickPickItem extends IQuickPickItem { - profile: ITerminalProfile | IExtensionTerminalProfile; - profileName: string; -} - class TerminalEditorStyle extends Themable { private _styleElement: HTMLElement; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts b/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts index 36c163de1aa..b514c5c027a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts @@ -3,13 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Codicon, iconRegistry } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; +import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IHoverAction } from 'vs/workbench/services/hover/browser/hover'; /** @@ -34,7 +36,7 @@ export interface ITerminalStatus { * An icon representing the status, if this is not specified it will not show up on the terminal * tab and will use the generic `info` icon when hovering. */ - icon?: Codicon; + icon?: ThemeIcon; /** * What to show for this status in the terminal's hover. */ @@ -141,23 +143,21 @@ export class TerminalStatusList extends Disposable implements ITerminalStatusLis } private _applyAnimationSetting(status: ITerminalStatus): ITerminalStatus { - if (!status.icon?.id.endsWith('~spin') || this._configurationService.getValue(TerminalSettingId.TabsEnableAnimation)) { + if (!status.icon || ThemeIcon.getModifier(status.icon) !== 'spin' || this._configurationService.getValue(TerminalSettingId.TabsEnableAnimation)) { return status; } - let id = status.icon.id.split('~')[0]; + let icon; // Loading without animation is just a curved line that doesn't mean anything - if (id === 'loading') { - id = 'play'; - } - const codicon = iconRegistry.get(id); - if (!codicon) { - return status; + if (status.icon.id === spinningLoading.id) { + icon = Codicon.play; + } else { + icon = ThemeIcon.modify(status.icon, undefined); } // Clone the status when changing the icon so that setting changes are applied without a // reload being needed return { ...status, - icon: codicon + icon }; } } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 213eea3cde1..3b5aa91c7fe 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -157,27 +157,27 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { updateConfig(): void { const config = this._configHelper.config; - this._safeSetOption('altClickMovesCursor', config.altClickMovesCursor); + this.raw.options.altClickMovesCursor = config.altClickMovesCursor; this._setCursorBlink(config.cursorBlinking); this._setCursorStyle(config.cursorStyle); this._setCursorWidth(config.cursorWidth); - this._safeSetOption('scrollback', config.scrollback); - this._safeSetOption('drawBoldTextInBrightColors', config.drawBoldTextInBrightColors); - this._safeSetOption('minimumContrastRatio', config.minimumContrastRatio); - this._safeSetOption('fastScrollSensitivity', config.fastScrollSensitivity); - this._safeSetOption('scrollSensitivity', config.mouseWheelScrollSensitivity); - this._safeSetOption('macOptionIsMeta', config.macOptionIsMeta); + this.raw.options.scrollback = config.scrollback; + this.raw.options.drawBoldTextInBrightColors = config.drawBoldTextInBrightColors; + this.raw.options.minimumContrastRatio = config.minimumContrastRatio; + this.raw.options.fastScrollSensitivity = config.fastScrollSensitivity; + this.raw.options.scrollSensitivity = config.mouseWheelScrollSensitivity; + this.raw.options.macOptionIsMeta = config.macOptionIsMeta; const editorOptions = this._configurationService.getValue('editor'); - this._safeSetOption('altClickMovesCursor', config.altClickMovesCursor && editorOptions.multiCursorModifier === 'alt'); - this._safeSetOption('macOptionClickForcesSelection', config.macOptionClickForcesSelection); - this._safeSetOption('rightClickSelectsWord', config.rightClickBehavior === 'selectWord'); - this._safeSetOption('wordSeparator', config.wordSeparators); - this._safeSetOption('customGlyphs', config.customGlyphs); + this.raw.options.altClickMovesCursor = config.altClickMovesCursor && editorOptions.multiCursorModifier === 'alt'; + this.raw.options.macOptionClickForcesSelection = config.macOptionClickForcesSelection; + this.raw.options.rightClickSelectsWord = config.rightClickBehavior === 'selectWord'; + this.raw.options.wordSeparator = config.wordSeparators; + this.raw.options.customGlyphs = config.customGlyphs; if ((!isSafari && config.gpuAcceleration === 'auto' && XtermTerminal._suggestedRendererType === undefined) || config.gpuAcceleration === 'on') { this._enableWebglRenderer(); } else { this._disposeOfWebglRenderer(); - this._safeSetOption('rendererType', this._getBuiltInXtermRenderer(config.gpuAcceleration, XtermTerminal._suggestedRendererType)); + this.raw.options.rendererType = this._getBuiltInXtermRenderer(config.gpuAcceleration, XtermTerminal._suggestedRendererType); } } @@ -282,30 +282,23 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this.raw.clear(); } - private _safeSetOption(key: string, value: any): void { - if (this.raw.getOption(key) !== value) { - this.raw.setOption(key, value); - } - } - private _setCursorBlink(blink: boolean): void { - if (this.raw.getOption('cursorBlink') !== blink) { - this.raw.setOption('cursorBlink', blink); + if (this.raw.options.cursorBlink !== blink) { + this.raw.options.cursorBlink = blink; this.raw.refresh(0, this.raw.rows - 1); } } - private _setCursorStyle(style: string): void { - if (this.raw.getOption('cursorStyle') !== style) { + private _setCursorStyle(style: 'block' | 'underline' | 'bar' | 'line'): void { + if (this.raw.options.cursorStyle !== style) { // 'line' is used instead of bar in VS Code to be consistent with editor.cursorStyle - const xtermOption = style === 'line' ? 'bar' : style; - this.raw.setOption('cursorStyle', xtermOption); + this.raw.options.cursorStyle = (style === 'line') ? 'bar' : style; } } private _setCursorWidth(width: number): void { - if (this.raw.getOption('cursorWidth') !== width) { - this.raw.setOption('cursorWidth', width); + if (this.raw.options.cursorWidth !== width) { + this.raw.options.cursorWidth = width; } } @@ -328,7 +321,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._webglAddon.onContextLoss(() => { this._logService.info(`Webgl lost context, disposing of webgl renderer`); this._disposeOfWebglRenderer(); - this._safeSetOption('rendererType', 'dom'); + this.raw.options.rendererType = 'dom'; }); } catch (e) { this._logService.warn(`Webgl could not be loaded. Falling back to the canvas renderer type.`, e); @@ -337,7 +330,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { if (!neverMeasureRenderTime && this._configHelper.config.gpuAcceleration !== 'off') { this._measureRenderTime(); } - this._safeSetOption('rendererType', 'canvas'); + this.raw.options.rendererType = 'canvas'; XtermTerminal._suggestedRendererType = 'canvas'; this._disposeOfWebglRenderer(); } diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts index 6812a399682..6bc36af3107 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts @@ -261,11 +261,11 @@ export class RemoteTerminalChannelClient { return this._channel.call('$getWslPath', [original]); } - setTerminalLayoutInfo(layout: ITerminalsLayoutInfoById): Promise { + setTerminalLayoutInfo(layout?: ITerminalsLayoutInfoById): Promise { const workspace = this._workspaceContextService.getWorkspace(); const args: ISetTerminalLayoutInfoArgs = { workspaceId: workspace.id, - tabs: layout.tabs + tabs: layout ? layout.tabs : [] }; return this._channel.call('$setTerminalLayoutInfo', args); } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index bf99ce95676..35bdcce9826 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -57,17 +57,22 @@ export interface ITerminalProfileResolverService { createProfileFromShellAndShellArgs(shell?: unknown, shellArgs?: unknown): Promise; } +export interface IRegisterContributedProfileArgs { + extensionIdentifier: string, id: string, title: string, options: ICreateContributedTerminalProfileOptions; +} + export const ITerminalProfileService = createDecorator('terminalProfileService'); export interface ITerminalProfileService { readonly _serviceBrand: undefined; readonly availableProfiles: ITerminalProfile[]; readonly contributedProfiles: IExtensionTerminalProfile[]; readonly profilesReady: Promise; + getPlatformKey(): Promise; refreshAvailableProfiles(): void; getDefaultProfileName(): string | undefined; onDidChangeAvailableProfiles: Event; getContributedDefaultProfile(shellLaunchConfig: IShellLaunchConfig): Promise; - registerContributedProfile(extensionIdentifier: string, id: string, title: string, options: ICreateContributedTerminalProfileOptions): Promise; + registerContributedProfile(args: IRegisterContributedProfileArgs): Promise; getContributedProfileProvider(extensionIdentifier: string, id: string): ITerminalProfileProvider | undefined; registerTerminalProfileProvider(extensionIdentifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable; } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.test.ts index e5e070b430e..7a92132511c 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { ITerminalConfiguration, ITerminalBackend } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfiguration, ITerminalBackend, ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices'; import { TerminalProfileService } from 'vs/workbench/contrib/terminal/browser/terminalProfileService'; @@ -19,12 +19,16 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Codicon } from 'vs/base/common/codicons'; import { deepStrictEqual } from 'assert'; import { Emitter } from 'vs/base/common/event'; +import { IProfileQuickPickItem, TerminalProfileQuickpick } from 'vs/workbench/contrib/terminal/browser/terminalProfileQuickpick'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { IPickOptions, IQuickInputService, Omit, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { CancellationToken } from 'vs/base/common/cancellation'; -class TestTerminalProfileService extends TerminalProfileService { +class TestTerminalProfileService extends TerminalProfileService implements Partial{ hasRefreshedProfiles: Promise | undefined; override refreshAvailableProfiles(): void { this.hasRefreshedProfiles = this._refreshAvailableProfilesNow(); @@ -38,6 +42,46 @@ class TestTerminalProfileService extends TerminalProfileService { } } +class MockTerminalProfileService implements Partial{ + hasRefreshedProfiles: Promise | undefined; + _defaultProfileName: string | undefined; + availableProfiles?: ITerminalProfile[] | undefined = []; + contributedProfiles?: IExtensionTerminalProfile[] | undefined = []; + async getPlatformKey(): Promise { + return 'linux'; + } + getDefaultProfileName(): string | undefined { + return this._defaultProfileName; + } + setProfiles(profiles: ITerminalProfile[], contributed: IExtensionTerminalProfile[]): void { + this.availableProfiles = profiles; + this.contributedProfiles = contributed; + } + setDefaultProfileName(name: string): void { + this._defaultProfileName = name; + } +} + + +class MockQuickInputService implements Partial { + _pick: IProfileQuickPickItem = powershellPick; + pick(picks: QuickPickInput[] | Promise[]>, options?: IPickOptions & { canPickMany: true; }, token?: CancellationToken): Promise; + pick(picks: QuickPickInput[] | Promise[]>, options?: IPickOptions & { canPickMany: false; }, token?: CancellationToken): Promise; + pick(picks: QuickPickInput[] | Promise[]>, options?: Omit, 'canPickMany'>, token?: CancellationToken): Promise; + async pick(picks: any, options?: any, token?: any): Promise { + Promise.resolve(picks); + return this._pick; + } + + setPick(pick: IProfileQuickPickItem) { + this._pick = pick; + } +} + +class TestTerminalProfileQuickpick extends TerminalProfileQuickpick { + +} + class TestTerminalExtensionService extends TestExtensionService { readonly _onDidChangeExtensions = new Emitter(); } @@ -96,7 +140,8 @@ let jsdebugProfile = { id: 'extension.js-debug.debugTerminal', title: 'JavaScript Debug Terminal' }; - +let powershellPick = { label: 'Powershell', profile: powershellProfile, profileName: powershellProfile.profileName }; +let jsdebugPick = { label: 'Javascript Debug Terminal', profile: jsdebugProfile, profileName: jsdebugProfile.title }; suite('TerminalProfileService', () => { let configurationService: TestConfigurationService; @@ -115,6 +160,7 @@ suite('TerminalProfileService', () => { environmentService = { configuration: {}, remoteAuthority: undefined } as IWorkbenchEnvironmentService; instantiationService = new TestInstantiationService(); + let themeService = new TestThemeService(); let terminalContributionService = new TestTerminalContributionService(); let contextKeyService = new MockContextKeyService(); @@ -125,6 +171,7 @@ suite('TerminalProfileService', () => { instantiationService.stub(ITerminalContributionService, terminalContributionService); instantiationService.stub(ITerminalInstanceService, terminalInstanceService); instantiationService.stub(IWorkbenchEnvironmentService, environmentService); + instantiationService.stub(IThemeService, themeService); terminalProfileService = instantiationService.createInstance(TestTerminalProfileService); @@ -283,4 +330,62 @@ suite('TerminalProfileService', () => { deepStrictEqual(terminalProfileService.availableProfiles, [powershellProfile]); deepStrictEqual(terminalProfileService.contributedProfiles, [jsdebugProfile]); }); + suite('Profiles Quickpick', () => { + let quickInputService: MockQuickInputService; + let mockTerminalProfileService: MockTerminalProfileService; + let terminalProfileQuickpick: TestTerminalProfileQuickpick; + setup(async () => { + quickInputService = new MockQuickInputService(); + mockTerminalProfileService = new MockTerminalProfileService(); + instantiationService.stub(IQuickInputService, quickInputService); + instantiationService.stub(ITerminalProfileService, mockTerminalProfileService); + terminalProfileQuickpick = instantiationService.createInstance(TestTerminalProfileQuickpick); + }); + test('setDefault', async () => { + powershellProfile.isDefault = false; + mockTerminalProfileService.setProfiles([powershellProfile], [jsdebugProfile]); + mockTerminalProfileService.setDefaultProfileName(jsdebugProfile.title); + const result = await terminalProfileQuickpick.showAndGetResult('setDefault'); + deepStrictEqual(result, powershellProfile.profileName); + }); + test('setDefault to contributed', async () => { + mockTerminalProfileService.setDefaultProfileName(powershellProfile.profileName); + quickInputService.setPick(jsdebugPick); + const result = await terminalProfileQuickpick.showAndGetResult('setDefault'); + const expected = { + config: { + extensionIdentifier: jsdebugProfile.extensionIdentifier, + id: jsdebugProfile.id, + options: { color: undefined, icon: 'debug' }, + title: jsdebugProfile.title, + }, + keyMods: undefined + }; + deepStrictEqual(result, expected); + }); + + test('createInstance', async () => { + mockTerminalProfileService.setDefaultProfileName(powershellProfile.profileName); + const pick = { ...powershellPick, keyMods: { alt: true, ctrlCmd: false } }; + quickInputService.setPick(pick); + const result = await terminalProfileQuickpick.showAndGetResult('createInstance'); + deepStrictEqual(result, { config: powershellProfile, keyMods: { alt: true, ctrlCmd: false } }); + }); + + test('createInstance with contributed', async () => { + const pick = { ...jsdebugPick, keyMods: { alt: true, ctrlCmd: false } }; + quickInputService.setPick(pick); + const result = await terminalProfileQuickpick.showAndGetResult('createInstance'); + const expected = { + config: { + extensionIdentifier: jsdebugProfile.extensionIdentifier, + id: jsdebugProfile.id, + options: { color: undefined, icon: 'debug' }, + title: jsdebugProfile.title, + }, + keyMods: { alt: true, ctrlCmd: false } + }; + deepStrictEqual(result, expected); + }); + }); }); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts index 7519894453b..f8388e94e19 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts @@ -7,6 +7,8 @@ import { deepStrictEqual, strictEqual } from 'assert'; import { Codicon } from 'vs/base/common/codicons'; import Severity from 'vs/base/common/severity'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ITerminalStatus, TerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; function statusesEqual(list: TerminalStatusList, expected: [string, Severity][]) { @@ -114,17 +116,17 @@ suite('Workbench - TerminalStatusList', () => { test('add should remove animation', () => { statusesEqual(list, []); - list.add({ id: 'info', severity: Severity.Info, icon: new Codicon('loading~spin', Codicon.loading) }); + list.add({ id: 'info', severity: Severity.Info, icon: spinningLoading }); statusesEqual(list, [ ['info', Severity.Info] ]); - strictEqual(list.statuses[0].icon!.id, 'play', 'loading~spin should be converted to play'); - list.add({ id: 'warning', severity: Severity.Warning, icon: new Codicon('zap~spin', Codicon.zap) }); + strictEqual(list.statuses[0].icon!.id, Codicon.play.id, 'loading~spin should be converted to play'); + list.add({ id: 'warning', severity: Severity.Warning, icon: ThemeIcon.modify(Codicon.zap, 'spin') }); statusesEqual(list, [ ['info', Severity.Info], ['warning', Severity.Warning] ]); - strictEqual(list.statuses[1].icon!.id, 'zap', 'zap~spin should have animation removed only'); + strictEqual(list.statuses[1].icon!.id, Codicon.zap.id, 'zap~spin should have animation removed only'); }); test('remove', () => { diff --git a/src/vs/workbench/contrib/testing/browser/icons.ts b/src/vs/workbench/contrib/testing/browser/icons.ts index ad087ca959b..6928994dfdf 100644 --- a/src/vs/workbench/contrib/testing/browser/icons.ts +++ b/src/vs/workbench/contrib/testing/browser/icons.ts @@ -5,7 +5,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { localize } from 'vs/nls'; -import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import { registerIcon, spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { testingColorRunAction, testStatesToIconColors } from 'vs/workbench/contrib/testing/browser/theme'; import { TestResultState } from 'vs/workbench/contrib/testing/common/testCollection'; @@ -31,7 +31,7 @@ export const testingStatesToIcons = new Map([ [TestResultState.Failed, registerIcon('testing-failed-icon', Codicon.error, localize('testingFailedIcon', 'Icon shown for tests that failed.'))], [TestResultState.Passed, registerIcon('testing-passed-icon', Codicon.pass, localize('testingPassedIcon', 'Icon shown for tests that passed.'))], [TestResultState.Queued, registerIcon('testing-queued-icon', Codicon.history, localize('testingQueuedIcon', 'Icon shown for tests that are queued.'))], - [TestResultState.Running, ThemeIcon.modify(Codicon.loading, 'spin')], + [TestResultState.Running, spinningLoading], [TestResultState.Skipped, registerIcon('testing-skipped-icon', Codicon.debugStepOver, localize('testingSkippedIcon', 'Icon shown for tests that are skipped.'))], [TestResultState.Unset, registerIcon('testing-unset-icon', Codicon.circleOutline, localize('testingUnsetIcon', 'Icon shown for tests that are in an unset state.'))], ]); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 26efb1ed53e..32e53a531b8 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -16,10 +16,18 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMapping'; /** - * Set when the find widget in a webview is visible. + * Set when the find widget in a webview in a webview is visible. */ export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey('webviewFindWidgetVisible', false); + +/** + * Set when the find widget in a webview is focused. + */ export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED = new RawContextKey('webviewFindWidgetFocused', false); + +/** + * Set when the find widget in a webview is enabled in a webview + */ export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_ENABLED = new RawContextKey('webviewFindWidgetEnabled', false); export const IWebviewService = createDecorator('webviewService'); @@ -72,27 +80,58 @@ export const enum WebviewContentPurpose { WebviewView = 'webviewView', } -export type WebviewStyles = { [key: string]: string | number; }; +export type WebviewStyles = { readonly [key: string]: string | number; }; export interface WebviewOptions { - // The purpose of the webview; this is (currently) only used for filtering in js-debug + /** + * The purpose of the webview; this is (currently) only used for filtering in js-debug + */ readonly purpose?: WebviewContentPurpose; readonly customClasses?: string; readonly enableFindWidget?: boolean; readonly tryRestoreScrollPosition?: boolean; readonly retainContextWhenHidden?: boolean; - transformCssVariables?(styles: Readonly): Readonly; + transformCssVariables?(styles: WebviewStyles): WebviewStyles; } +/** + * + */ export interface WebviewContentOptions { + /** + * Should the webview allow `acquireVsCodeApi` to be called multiple times? Defaults to false. + */ readonly allowMultipleAPIAcquire?: boolean; + + /** + * Should scripts be enabled in the webview? Defaults to false. + */ readonly allowScripts?: boolean; + + /** + * Should forms be enabled in the webview? Defaults to the value of {@link allowScripts}. + */ readonly allowForms?: boolean; - readonly localResourceRoots?: ReadonlyArray; - readonly portMapping?: ReadonlyArray; + + /** + * Set of root paths from which the webview can load local resources. + */ + readonly localResourceRoots?: readonly URI[]; + + /** + * Set of localhost port mappings to apply inside the webview. + */ + readonly portMapping?: readonly IWebviewPortMapping[]; + + /** + * Are command uris enabled in the webview? Defaults to false. + */ readonly enableCommandUris?: boolean; } +/** + * Check if two {@link WebviewContentOptions} are equal. + */ export function areWebviewContentOptionsEqual(a: WebviewContentOptions, b: WebviewContentOptions): boolean { return ( a.allowMultipleAPIAcquire === b.allowMultipleAPIAcquire @@ -110,8 +149,8 @@ export interface WebviewExtensionDescription { } export interface IDataLinkClickEvent { - dataURL: string; - downloadName?: string; + readonly dataURL: string; + readonly downloadName?: string; } export interface WebviewMessageReceivedEvent { @@ -137,7 +176,7 @@ export interface IWebview extends IDisposable { readonly onDidDispose: Event; readonly onDidClickLink: Event; - readonly onDidScroll: Event<{ scrollYPercentage: number }>; + readonly onDidScroll: Event<{ readonly scrollYPercentage: number }>; readonly onDidWheel: Event; readonly onDidUpdateState: Event; readonly onDidReload: Event; diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index a9ef766660e..41f202b54d4 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -11,7 +11,7 @@ import { IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/exte import { ViewsWelcomeExtensionPoint, ViewWelcome, ViewIdentifierMap } from './viewsWelcomeExtensionPoint'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ViewContainerExtensions, IViewContentDescriptor, IViewsRegistry } from 'vs/workbench/common/views'; -import { isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); @@ -73,7 +73,7 @@ function parseGroupAndOrder(welcome: ViewWelcome, contribution: IExtensionPointU let group: string | undefined; let order: number | undefined; if (welcome.group) { - if (!isProposedApiEnabled(contribution.description)) { + if (!isProposedApiEnabled(contribution.description, 'contribViewsWelcome')) { contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled.", contribution.description.identifier.value)); return { group, order }; } diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css index 26d4d8a30db..85b0163d359 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -717,6 +717,8 @@ .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlide .openAWalkthrough>button, .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlide .showOnStartup { text-align: center; + display: flex; + justify-content: center; } .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlide .getting-started-checkbox { diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcome/gettingStarted/common/gettingStartedContent.ts index 62c5ac5b7fb..2218e71c907 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/common/gettingStartedContent.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -import { OpenGettingStarted } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const setupIcon = registerIcon('getting-started-setup', Codicon.zap, localize('getting-started-setup-icon', "Icon used for the setup category of welcome page")); @@ -483,7 +483,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ description: '', icon: setupIcon, isFeatured: false, - when: `config.${OpenGettingStarted} && userHasOpenedNotebook`, + when: `config.${NotebookSetting.openGettingStarted} && userHasOpenedNotebook`, content: { type: 'steps', steps: [ diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts index 066d0b21620..0786d610505 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -11,7 +11,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table'; import { Action, IAction } from 'vs/base/common/actions'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Codicon, registerCodicon } from 'vs/base/common/codicons'; +import { Codicon } from 'vs/base/common/codicons'; import { debounce } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -44,7 +44,6 @@ import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { ChoiceAction } from 'vs/workbench/common/notifications'; import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors'; import { IExtensionsWorkbenchService, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; -import { settingsEditIcon, settingsRemoveIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -55,12 +54,15 @@ import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbe import { posix } from 'vs/base/common/path'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IProductService } from 'vs/platform/product/common/productService'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -export const shieldIcon = registerCodicon('workspace-trust-icon', Codicon.shield); +export const shieldIcon = registerIcon('workspace-trust-banner', Codicon.shield, localize('shieldIcon', 'Icon for workspace trust ion the banner.')); -const checkListIcon = registerCodicon('workspace-trusted-check-icon', Codicon.check); -const xListIcon = registerCodicon('workspace-trusted-x-icon', Codicon.x); -const folderPickerIcon = registerCodicon('folder-picker', Codicon.folder); +const checkListIcon = registerIcon('workspace-trust-editor-check', Codicon.check, localize('checkListIcon', 'Icon for the checkmark in the workspace trust editor.')); +const xListIcon = registerIcon('workspace-trust-editor-cross', Codicon.x, localize('xListIcon', 'Icon for the cross in the workspace trust editor.')); +const folderPickerIcon = registerIcon('workspace-trust-editor-folder-picker', Codicon.folder, localize('folderPickerIcon', 'Icon for the pick folder icon in the workspace trust editor.')); +const editIcon = registerIcon('workspace-trust-editor-edit-folder', Codicon.edit, localize('editIcon', 'Icon for the edit folder icon in the workspace trust editor.')); +const removeIcon = registerIcon('workspace-trust-editor-remove-folder', Codicon.edit, localize('removeIcon', 'Icon for the remove folder icon in the workspace trust editor.')); interface ITrustedUriItem { parentOfWorkspaceItem: boolean; @@ -418,7 +420,7 @@ class TrustedUriActionsColumnRenderer implements ITableRenderer{ - class: ThemeIcon.asClassName(settingsEditIcon), + class: ThemeIcon.asClassName(editIcon), enabled: true, id: 'editTrustedUri', tooltip: localize('editTrustedUri', "Edit Path"), @@ -442,7 +444,7 @@ class TrustedUriActionsColumnRenderer implements ITableRenderer{ - class: ThemeIcon.asClassName(settingsRemoveIcon), + class: ThemeIcon.asClassName(removeIcon), enabled: true, id: 'deleteTrustedUri', tooltip: localize('deleteTrustedUri', "Delete Path"), @@ -771,7 +773,7 @@ export class WorkspaceTrustEditor extends EditorPane { } private getHeaderTitleIconClassNames(trusted: boolean): string[] { - return shieldIcon.classNamesArray; + return ThemeIcon.asClassNameArray(shieldIcon); } private getFeaturesHeaderText(trusted: boolean): [string, string] { @@ -955,7 +957,7 @@ export class WorkspaceTrustEditor extends EditorPane { localize('trustedSettings', "All workspace settings are applied"), localize('trustedExtensions', "All extensions are enabled") ]; - this.renderLimitationsListElement(this.trustedContainer, trustedContainerItems, checkListIcon.classNamesArray); + this.renderLimitationsListElement(this.trustedContainer, trustedContainerItems, ThemeIcon.asClassNameArray(checkListIcon)); // Restricted Mode features const [untrustedTitle, untrustedSubTitle] = this.getFeaturesHeaderText(false); @@ -973,7 +975,7 @@ export class WorkspaceTrustEditor extends EditorPane { fixBadLocalizedLinks(numSettings ? localize({ key: 'untrustedSettings', comment: ['Please ensure the markdown link syntax is not broken up with whitespace [text block](link block)'] }, "[{0} workspace settings]({1}) are not applied", numSettings, 'command:settings.filterUntrusted') : localize('no untrustedSettings', "Workspace settings requiring trust are not applied")), fixBadLocalizedLinks(localize({ key: 'untrustedExtensions', comment: ['Please ensure the markdown link syntax is not broken up with whitespace [text block](link block)'] }, "[{0} extensions]({1}) are disabled or have limited functionality", numExtensions, `command:${LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID}`)) ]; - this.renderLimitationsListElement(this.untrustedContainer, untrustedContainerItems, xListIcon.classNamesArray); + this.renderLimitationsListElement(this.untrustedContainer, untrustedContainerItems, ThemeIcon.asClassNameArray(xListIcon)); if (this.workspaceTrustManagementService.isWorkspaceTrusted()) { if (this.workspaceTrustManagementService.canSetWorkspaceTrust()) { diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 6e3bdfe3cef..70eb8b65d51 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -25,9 +25,9 @@ import { getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Codicon } from 'vs/base/common/codicons'; import { NativeMenubarControl } from 'vs/workbench/electron-sandbox/parts/titlebar/menubarControl'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export class TitlebarPart extends BrowserTitleBarPart { - private windowControls: HTMLElement | undefined; private maxRestoreControl: HTMLElement | undefined; private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; @@ -53,6 +53,7 @@ export class TitlebarPart extends BrowserTitleBarPart { @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IInstantiationService instantiationService: IInstantiationService, + @IKeybindingService keybindingService: IKeybindingService, @IThemeService themeService: IThemeService, @ILabelService labelService: ILabelService, @IStorageService storageService: IStorageService, @@ -63,7 +64,7 @@ export class TitlebarPart extends BrowserTitleBarPart { @IProductService productService: IProductService, @INativeHostService private readonly nativeHostService: INativeHostService ) { - super(contextMenuService, configurationService, editorService, environmentService, contextService, instantiationService, themeService, labelService, storageService, layoutService, menuService, contextKeyService, hostService, productService); + super(contextMenuService, configurationService, editorService, environmentService, contextService, instantiationService, keybindingService, themeService, labelService, storageService, layoutService, menuService, contextKeyService, hostService, productService); this.environmentService = environmentService; } @@ -187,9 +188,7 @@ export class TitlebarPart extends BrowserTitleBarPart { this.dragRegion = prepend(this.element, $('div.titlebar-drag-region')); // Window Controls (Native Windows/Linux) - if (!isMacintosh) { - this.windowControls = append(this.element, $('div.window-controls-container')); - + if (!isMacintosh && this.windowControls) { // Minimize const minimizeIcon = append(this.windowControls, $('div.window-icon.window-minimize' + Codicon.chromeMinimize.cssSelector)); this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => { diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 4587a06904c..150e2192154 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { onUnexpectedError } from 'vs/base/common/errors'; import { equals } from 'vs/base/common/objects'; import { EventType, EventHelper, addDisposableListener, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; -import { Separator } from 'vs/base/common/actions'; +import { Separator, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors, IResourceDiffEditorInput, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -155,11 +155,7 @@ export class NativeWindow extends Disposable { try { await this.commandService.executeCommand(request.id, ...args); - type CommandExecutedClassifcation = { - id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - from: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - this.telemetryService.publicLog2<{ id: String, from: String }, CommandExecutedClassifcation>('commandExecuted', { id: request.id, from: request.from }); + this.telemetryService.publicLog2('workbenchActionExecuted', { id: request.id, from: request.from }); } catch (error) { this.notificationService.error(error); } diff --git a/src/vs/workbench/services/banner/browser/bannerService.ts b/src/vs/workbench/services/banner/browser/bannerService.ts index 74e79141fcf..9bd7ca53d60 100644 --- a/src/vs/workbench/services/banner/browser/bannerService.ts +++ b/src/vs/workbench/services/banner/browser/bannerService.ts @@ -3,16 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Codicon } from 'vs/base/common/codicons'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILinkDescriptor } from 'vs/platform/opener/browser/link'; - +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export interface IBannerItem { readonly id: string; - readonly icon: Codicon | URI | undefined; + readonly icon: ThemeIcon | URI | undefined; readonly message: string | MarkdownString; readonly actions?: ILinkDescriptor[]; readonly ariaLabel?: string; diff --git a/src/vs/workbench/services/editor/common/editorGroupFinder.ts b/src/vs/workbench/services/editor/common/editorGroupFinder.ts index d722d2137d9..a53f1a3e1ee 100644 --- a/src/vs/workbench/services/editor/common/editorGroupFinder.ts +++ b/src/vs/workbench/services/editor/common/editorGroupFinder.ts @@ -15,7 +15,7 @@ import { PreferredGroup, SIDE_GROUP } from 'vs/workbench/services/editor/common/ /** * Finds the target `IEditorGroup` given the instructions provided * that is best for the editor and matches the preferred group if - * posisble. + * possible. */ export function findGroup(accessor: ServicesAccessor, editor: IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): [IEditorGroup, EditorActivation | undefined]; export function findGroup(accessor: ServicesAccessor, editor: EditorInputWithOptions, preferredGroup: PreferredGroup | undefined): [IEditorGroup, EditorActivation | undefined]; diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index 5445b7ff1d2..99cdd078bf1 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -196,6 +196,36 @@ suite('EditorService', () => { visibleEditorChangeListener.dispose(); }); + test('openEditor() - same input does not cancel previous one - https://github.com/microsoft/vscode/issues/136684', async () => { + const [, service] = await createEditorService(); + + let input = new TestFileEditorInput(URI.parse('my://resource-basics'), TEST_EDITOR_INPUT_ID); + + let editorP1 = service.openEditor(input, { pinned: true }); + let editorP2 = service.openEditor(input, { pinned: true }); + + let editor1 = await editorP1; + assert.strictEqual(editor1?.input, input); + + let editor2 = await editorP2; + assert.strictEqual(editor2?.input, input); + + assert.ok(editor2.group); + await editor2.group.closeAllEditors(); + + input = new TestFileEditorInput(URI.parse('my://resource-basics'), TEST_EDITOR_INPUT_ID); + let inputSame = new TestFileEditorInput(URI.parse('my://resource-basics'), TEST_EDITOR_INPUT_ID); + + editorP1 = service.openEditor(input, { pinned: true }); + editorP2 = service.openEditor(inputSame, { pinned: true }); + + editor1 = await editorP1; + assert.strictEqual(editor1?.input, input); + + editor2 = await editorP2; + assert.strictEqual(editor2?.input, input); + }); + test('openEditor() - locked groups', async () => { disposables.add(registerTestFileEditor()); diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 5a571ed0f1f..71764d5a156 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -34,6 +34,9 @@ import { URI } from 'vs/base/common/uri'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; import { dedupExtensions } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ApiProposalName, allApiProposals } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; +import { forEach } from 'vs/base/common/collections'; +import { ILogService } from 'vs/platform/log/common/log'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -201,7 +204,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._installedExtensionsReady = new Barrier(); this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; this._extensionsMessages = new Map(); - this._proposedApiController = new ProposedApiController(this._environmentService, this._productService); + this._proposedApiController = _instantiationService.createInstance(ProposedApiController); this._extensionHostManagers = []; this._extensionHostActiveExtensions = new Map(); @@ -777,7 +780,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx protected _checkEnableProposedApi(extensions: IExtensionDescription[]): void { for (let extension of extensions) { - this._proposedApiController.updateEnableProposedApi(extension); + this._proposedApiController.updateEnabledApiProposals(extension); } } @@ -1116,52 +1119,100 @@ class ExtensionRunningLocationClassifier { class ProposedApiController { - private readonly enableProposedApiFor: string[]; - private readonly enableProposedApiForAll: boolean; - private readonly productAllowProposedApi: Set; + private readonly _envEnablesProposedApiForAll: boolean; + private readonly _envEnabledExtensions: Set; + private readonly _productEnabledExtensions: Map; constructor( + @ILogService private readonly _logService: ILogService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @IProductService productService: IProductService ) { - // Make enabled proposed API be lowercase for case insensitive comparison - this.enableProposedApiFor = (_environmentService.extensionEnabledProposedApi || []).map(id => id.toLowerCase()); - this.enableProposedApiForAll = + this._envEnabledExtensions = new Set((_environmentService.extensionEnabledProposedApi ?? []).map(id => ExtensionIdentifier.toKey(id))); + + this._envEnablesProposedApiForAll = !_environmentService.isBuilt || // always allow proposed API when running out of sources (_environmentService.isExtensionDevelopment && productService.quality !== 'stable') || // do not allow proposed API against stable builds when developing an extension - (this.enableProposedApiFor.length === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID + (this._envEnabledExtensions.size === 0 && Array.isArray(_environmentService.extensionEnabledProposedApi)); // always allow proposed API if --enable-proposed-api is provided without extension ID + + this._productEnabledExtensions = new Map(); - this.productAllowProposedApi = new Set(); + // todo@jrieken this is deprecated and will be removed + // OLD world - extensions that are listed in `extensionAllowedProposedApi` get all proposals enabled if (isNonEmptyArray(productService.extensionAllowedProposedApi)) { - productService.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); + for (let id of productService.extensionAllowedProposedApi) { + const key = ExtensionIdentifier.toKey(id); + this._productEnabledExtensions.set(key, Object.keys(allApiProposals)); + } + } + + // NEW world - product.json spells out what proposals each extension can use + if (productService.extensionEnabledApiProposals) { + forEach(productService.extensionEnabledApiProposals, entry => { + const proposalNames = entry.value.filter(name => { + if (!allApiProposals[name]) { + _logService.warn(`Extension '${key} wants API proposal '${name}' but that proposal DOES NOT EXIST.`); + return false; + } + return true; + }); + const key = ExtensionIdentifier.toKey(entry.key); + if (this._productEnabledExtensions.has(key)) { + _logService.warn(`Extension '${key} appears in BOTH 'product.json#extensionAllowedProposedApi' and 'extensionEnabledApiProposals'. The latter is more restrictive and will override the former.`); + } + this._productEnabledExtensions.set(key, proposalNames); + }); } } - public updateEnableProposedApi(extension: IExtensionDescription): void { - if (this._allowProposedApiFromProduct(extension.identifier)) { - // fast lane -> proposed api is available to all extensions - // that are listed in product.json-files - extension.enableProposedApi = true; + updateEnabledApiProposals(_extension: IExtensionDescription): void { + + // this is a trick to make the extension description writeable... + type Writeable = { -readonly [P in keyof T]: Writeable }; + const extension = >_extension; + + const key = ExtensionIdentifier.toKey(_extension.identifier); + + if (this._productEnabledExtensions.has(key)) { + // NOTE that proposals that are listed in product.json override whatever is declared in the extension + // itself. This is needed for us to know what proposals are used "in the wild". Merging product.json-proposals + // and extension-proposals would break that. - } else if (extension.enableProposedApi && !extension.isBuiltin) { - if ( - !this.enableProposedApiForAll && - this.enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0 - ) { - extension.enableProposedApi = false; - console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); - - } else if (this._environmentService.isBuilt) { - // proposed api is available when developing or when an extension was explicitly - // spelled out via a command line argument - console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`); + const productEnabledProposals = this._productEnabledExtensions.get(key)!; + + // check for difference between product.json-declaration and package.json-declaration + const productSet = new Set(productEnabledProposals); + const extensionSet = new Set(extension.enabledApiProposals); + const diff = new Set([...extensionSet].filter(a => !productSet.has(a))); + if (diff.size > 0) { + this._logService.critical(`Extension '${key}' appears in product.json but enables LESS API proposals than the extension wants.\npackage.json (LOOSES): ${[...extensionSet].join(', ')}\nproduct.json (WINS): ${[...productSet].join(', ')}`); + + if (this._environmentService.isExtensionDevelopment) { + this._logService.critical(`Proceeding with EXTRA proposals (${[...diff].join(', ')}) because extension is in development mode. Still, this EXTENSION WILL BE BROKEN unless product.json is updated.`); + productEnabledProposals.push(...diff); + } } + + extension.enabledApiProposals = productEnabledProposals; + + // todo@jrieken REMOVE, legacy flag is turned on + extension.enableProposedApi = true; + return; + } + + if (this._envEnablesProposedApiForAll || this._envEnabledExtensions.has(key)) { + // proposed API usage is not restricted and allowed just like the extension + // has declared it + return; } - } - private _allowProposedApiFromProduct(id: ExtensionIdentifier): boolean { - return this.productAllowProposedApi.has(ExtensionIdentifier.toKey(id)); + if (!extension.isBuiltin && (extension.enableProposedApi || isNonEmptyArray(extension.enabledApiProposals))) { + // restrictive: extension cannot use proposed API in this context and its declaration is nulled + this._logService.critical(`Extension '${extension.identifier.value} CANNOT USE these API proposals '${extension.enabledApiProposals?.join(', ') ?? '*'}'. You MUST start in extension development mode or use the --enable-proposed-api command line flag`); + extension.enabledApiProposals = []; + extension.enableProposedApi = false; + } } } diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index 8d6afdbfdad..288d3451fb1 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -137,19 +137,17 @@ export class ExtensionHostMain { const extensionsDeactivated = this._extensionService.deactivateAll(); - // Give extensions 1 second to wrap up any async dispose, then exit in at most 4 seconds - setTimeout(() => { - Promise.race([timeout(4000), extensionsDeactivated]).finally(() => { - if (this._hostUtils.pid) { - this._logService.info(`Extension host with pid ${this._hostUtils.pid} exiting with code 0`); - } else { - this._logService.info(`Extension host exiting with code 0`); - } - this._logService.flush(); - this._logService.dispose(); - this._hostUtils.exit(0); - }); - }, 1000); + // Give extensions at most 5 seconds to wrap up any async deactivate, then exit + Promise.race([timeout(5000), extensionsDeactivated]).finally(() => { + if (this._hostUtils.pid) { + this._logService.info(`Extension host with pid ${this._hostUtils.pid} exiting with code 0`); + } else { + this._logService.info(`Extension host exiting with code 0`); + } + this._logService.flush(); + this._logService.dispose(); + this._hostUtils.exit(0); + }); } private static _transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index ce65f021533..02ab47e2c30 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -12,6 +12,7 @@ import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { ApiProposalName } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; export const nullExtensionDescription = Object.freeze({ identifier: new ExtensionIdentifier('nullExtensionDescription'), @@ -133,6 +134,22 @@ export interface IExtensionHost { dispose(): void; } +export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName | undefined): boolean { + if (!proposal) { + return Boolean(extension.enableProposedApi); + } + if (extension.enabledApiProposals?.includes(proposal)) { + return true; + } + return Boolean(extension.enableProposedApi); +} + +export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName | undefined): void { + if (!isProposedApiEnabled(extension, proposal)) { + throw new Error(`[${extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`); + } +} + /** * Extension id or one of the four known program states. diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts new file mode 100644 index 00000000000..974d2a3bdab --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. + +export const allApiProposals = Object.freeze({ + authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', + contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts', + customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', + diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', + documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', + editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts', + extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts', + externalUriOpener: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts', + fileSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts', + findTextInFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts', + fsChunks: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fsChunks.d.ts', + inlayHints: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlayHints.d.ts', + inlineCompletions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts', + languageStatus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatus.d.ts', + notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', + notebookConcatTextDocument: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts', + notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', + notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', + notebookDebugOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts', + notebookDeprecated: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts', + notebookEditor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditor.d.ts', + notebookEditorDecorationType: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts', + notebookEditorEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts', + notebookLiveShare: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts', + notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', + notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', + portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', + quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', + resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', + scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.d.ts', + scmSelectedProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts', + scmValidation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmValidation.d.ts', + tabs: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tabs.d.ts', + taskPresentationGroup: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts', + terminalDataWriteEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts', + terminalDimensions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts', + terminalLocation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalLocation.d.ts', + terminalNameChangeEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts', + testCoverage: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testCoverage.d.ts', + testObserver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts', + textDocumentNotebook: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts', + textSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts', + timeline: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.timeline.d.ts', + tokenInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tokenInformation.d.ts', + treeViewDragAndDrop: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts', + treeViewReveal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts', + workspaceTrust: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts' +}); +export type ApiProposalName = keyof typeof allApiProposals; diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 9c3c09efa59..a32b31f59a3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -12,6 +12,8 @@ import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/c import { Registry } from 'vs/platform/registry/common/platform'; import { IMessage } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier, IExtensionDescription, EXTENSION_CATEGORIES, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { allApiProposals } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; +import { values } from 'vs/base/common/collections'; const schemaRegistry = Registry.as(Extensions.JSONContribution); @@ -221,6 +223,20 @@ export const schema: IJSONSchema = { type: 'boolean', description: nls.localize('vscode.extension.preview', 'Sets the extension to be flagged as a Preview in the Marketplace.'), }, + enableProposedApi: { + type: 'boolean', + deprecationMessage: nls.localize('vscode.extension.enableProposedApi.deprecated', 'Use `enabledApiProposals` instead.'), + }, + enabledApiProposals: { + markdownDescription: nls.localize('vscode.extension.enabledApiProposals', 'Enable API proposals to try them out. Only valid **during development**. Extensions **cannot be published** with this property. For more details visit: https://code.visualstudio.com/api/advanced-topics/using-proposed-api'), + type: 'array', + uniqueItems: true, + items: { + type: 'string', + enum: Object.keys(allApiProposals), + markdownEnumDescriptions: values(allApiProposals) + } + }, activationEvents: { description: nls.localize('vscode.extension.activationEvents', 'Activation events for the VS Code extension.'), type: 'array', diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 29b74dbc6a4..777123794b1 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -362,6 +362,8 @@ export class LocalProcessExtensionHost implements IExtensionHost { let startupTimeoutHandle: any; if (!this._environmentService.isBuilt && !this._environmentService.remoteAuthority || this._isExtensionDevHost) { startupTimeoutHandle = setTimeout(() => { + this._logService.error(`[LocalProcessExtensionHost]: Extension host did not start in 10 seconds (debugBrk: ${this._isExtensionDevDebugBrk})`); + const msg = this._isExtensionDevDebugBrk ? nls.localize('extensionHost.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") : nls.localize('extensionHost.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); diff --git a/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts index 3464798a9ac..9bdffcd67fd 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts @@ -3,7 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; +import { registerMainProcessRemoteService, registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter'; -registerSharedProcessRemoteService(IExtensionHostStarter, ipcExtensionHostStarterChannelName, { supportsDelayedInstantiation: true }); +const location = 'main' as 'main' | 'shared'; + +if (location === 'main') { + registerMainProcessRemoteService(IExtensionHostStarter, ipcExtensionHostStarterChannelName, { supportsDelayedInstantiation: true }); +} else { + registerSharedProcessRemoteService(IExtensionHostStarter, ipcExtensionHostStarterChannelName, { supportsDelayedInstantiation: true }); +} diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 43a42e6001c..ac2ea9c022c 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -13,8 +13,9 @@ import { MainThreadTelemetryShape, IInitData } from 'vs/workbench/api/common/ext import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; -import { IExtensionDescription, isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { LogLevel, createHttpPatch, ProxyResolveEvent, createProxyResolver, createTlsPatch, ProxySupportSetting } from 'vscode-proxy-agent'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export function connectProxyResolver( extHostWorkspace: IExtHostWorkspaceProvider, @@ -129,7 +130,7 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku } if (!cache[request]) { let mod = modules.default; - if (ext && isProposedApiEnabled(ext)) { + if (ext && isProposedApiEnabled(ext, undefined)) { mod = (modules as any)[(ext).proxySupport] || modules.onRequest; } cache[request] = { ...mod }; // Copy to work around #93167. diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 7bae6f53e9e..23fff4a5550 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom'; import { printKeyboardEvent, printStandardKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Emitter, Event } from 'vs/base/common/event'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { KeyCode, KeyMod, ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod, ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { Keybinding, ResolvedKeybinding, SimpleKeybinding, ScanCodeBinding } from 'vs/base/common/keybindings'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { OS, OperatingSystem, isMacintosh } from 'vs/base/common/platform'; @@ -51,6 +51,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { dirname } from 'vs/base/common/resources'; import { getAllUnboundCommands } from 'vs/workbench/services/keybinding/browser/unboundCommands'; +import { UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; interface ContributedKeyBinding { command: string; @@ -338,11 +339,82 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { updateSchema(flatten(this._contributions.map(x => x.getSchemaAdditions()))); } + private _printUserBinding(parts: (SimpleKeybinding | ScanCodeBinding)[]): string { + return UserSettingsLabelProvider.toLabel(OS, parts, (part) => { + if (part instanceof SimpleKeybinding) { + return KeyCodeUtils.toString(part.keyCode); + } + return ScanCodeUtils.toString(part.scanCode); + }) || '[null]'; + } + + private _printResolvedKeybinding(resolvedKeybinding: ResolvedKeybinding): string { + return resolvedKeybinding.getDispatchParts().map(x => x || '[null]').join(' '); + } + + private _printResolvedKeybindings(output:string[], input: string, resolvedKeybindings: ResolvedKeybinding[]): void { + const padLength = 35; + const firstRow = `${input.padStart(padLength, ' ')} => `; + if (resolvedKeybindings.length === 0) { + // no binding found + output.push(`${firstRow}${'[NO BINDING]'.padStart(padLength, ' ')}`); + return; + } + + const firstRowIndentation = firstRow.length; + let isFirst = true; + for (const resolvedKeybinding of resolvedKeybindings) { + if (isFirst) { + output.push(`${firstRow}${this._printResolvedKeybinding(resolvedKeybinding).padStart(padLength, ' ')}`); + } else { + output.push(`${' '.repeat(firstRowIndentation)}${this._printResolvedKeybinding(resolvedKeybinding).padStart(padLength, ' ')}`); + } + } + } + + private _dumpResolveKeybindingDebugInfo(): string { + + const seenBindings = new Set(); + const result: string[] = []; + + result.push(`Default Resolved Keybindings (unique only):`); + for (const item of KeybindingsRegistry.getDefaultKeybindings()) { + if (!item.keybinding || item.keybinding.length === 0) { + continue; + } + const input = this._printUserBinding(item.keybinding); + if (seenBindings.has(input)) { + continue; + } + seenBindings.add(input); + const resolvedKeybindings = this._keyboardMapper.resolveUserBinding(item.keybinding); + this._printResolvedKeybindings(result, input, resolvedKeybindings); + } + + result.push(`User Resolved Keybindings (unique only):`); + for (const _item of this.userKeybindings.keybindings) { + const item = KeybindingIO.readUserKeybindingItem(_item); + if (!item.parts || item.parts.length === 0) { + continue; + } + const input = _item.key; + if (seenBindings.has(input)) { + continue; + } + seenBindings.add(input); + const resolvedKeybindings = this._keyboardMapper.resolveUserBinding(item.parts); + this._printResolvedKeybindings(result, input, resolvedKeybindings); + } + + return result.join('\n'); + } + public _dumpDebugInfo(): string { const layoutInfo = JSON.stringify(this.keyboardLayoutService.getCurrentKeyboardLayout(), null, '\t'); const mapperInfo = this._keyboardMapper.dumpDebugInfo(); + const resolvedKeybindings = this._dumpResolveKeybindingDebugInfo(); const rawMapping = JSON.stringify(this.keyboardLayoutService.getRawKeyboardMapping(), null, '\t'); - return `Layout info:\n${layoutInfo}\n${mapperInfo}\n\nRaw mapping:\n${rawMapping}`; + return `Layout info:\n${layoutInfo}\n\n${resolvedKeybindings}\n\n${mapperInfo}\n\nRaw mapping:\n${rawMapping}`; } public _dumpDebugInfoJSON(): string { diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 54112c8b342..509be770cf7 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -21,7 +21,7 @@ import { match } from 'vs/base/common/glob'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'resourceLabelFormatters', @@ -84,7 +84,7 @@ class ResourceLabelFormattersHandler implements IWorkbenchContribution { constructor(@ILabelService labelService: ILabelService) { resourceLabelFormattersExtPoint.setHandler((extensions, delta) => { delta.added.forEach(added => added.value.forEach(formatter => { - if (!isProposedApiEnabled(added.description) && formatter.formatting.workspaceTooltip) { + if (!isProposedApiEnabled(added.description, undefined) && formatter.formatting.workspaceTooltip) { // workspaceTooltip is only proposed formatter.formatting.workspaceTooltip = undefined; } diff --git a/src/vs/workbench/services/log/electron-sandbox/logService.ts b/src/vs/workbench/services/log/electron-sandbox/logService.ts index ac35a244992..c0d9de791fb 100644 --- a/src/vs/workbench/services/log/electron-sandbox/logService.ts +++ b/src/vs/workbench/services/log/electron-sandbox/logService.ts @@ -14,17 +14,20 @@ export class NativeLogService extends LogService { const disposables = new DisposableStore(); - // Extension development test CLI: forward everything to main side const loggers: ILogger[] = []; + + // Always log to file + loggers.push(disposables.add(loggerService.createLogger(environmentService.logFile, { name }))); + + // Extension development test CLI: forward everything to main side if (environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI) { loggers.push(loggerService.createConsoleMainLogger()); } - // Normal logger: spdylog and console + // Normal mode: Log to console else { loggers.push( disposables.add(new ConsoleLogger(logLevel)), - disposables.add(loggerService.createLogger(environmentService.logFile, { name })) ); } diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index f2b6d8c39eb..bad2c3faee7 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -28,7 +28,7 @@ export enum SettingValueType { Integer = 'integer', Number = 'number', Boolean = 'boolean', - StringOrEnumArray = 'string-or-enum-array', + Array = 'array', Exclude = 'exclude', Complex = 'complex', NullableInteger = 'nullable-integer', diff --git a/src/vs/workbench/services/preferences/common/preferencesValidation.ts b/src/vs/workbench/services/preferences/common/preferencesValidation.ts index c991e059885..b722a3cee43 100644 --- a/src/vs/workbench/services/preferences/common/preferencesValidation.ts +++ b/src/vs/workbench/services/preferences/common/preferencesValidation.ts @@ -26,15 +26,15 @@ export function createValidator(prop: IConfigurationPropertySchema): (value: any const numericValidations = getNumericValidators(prop); const stringValidations = getStringValidators(prop); - const stringArrayValidator = getArrayOfStringValidator(prop); + const arrayValidator = getArrayValidator(prop); const objectValidator = getObjectValidator(prop); return value => { if (isNullable && isNullOrEmpty(value)) { return ''; } const errors: string[] = []; - if (stringArrayValidator) { - const err = stringArrayValidator(value); + if (arrayValidator) { + const err = arrayValidator(value); if (err) { errors.push(err); } @@ -52,7 +52,7 @@ export function createValidator(prop: IConfigurationPropertySchema): (value: any } if (isNumeric) { - if (isNullOrEmpty(value) || isNaN(+value)) { + if (isNullOrEmpty(value) || typeof value === 'boolean' || Array.isArray(value) || isNaN(+value)) { errors.push(nls.localize('validations.expectedNumeric', "Value must be a number.")); } else { errors.push(...numericValidations.filter(validator => !validator.isValid(+value)).map(validator => validator.message)); @@ -205,7 +205,6 @@ function getNumericValidators(prop: IConfigurationPropertySchema): Validator value > exclusiveMin!), message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin) }, - { enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum), isValid: ((value: number) => value <= prop.maximum!), @@ -229,10 +228,10 @@ function getNumericValidators(prop: IConfigurationPropertySchema): Validator validation.enabled); } -function getArrayOfStringValidator(prop: IConfigurationPropertySchema): ((value: any) => (string | null)) | null { - if (prop.type === 'array' && prop.items && !isArray(prop.items) && prop.items.type === 'string') { +function getArrayValidator(prop: IConfigurationPropertySchema): ((value: any) => (string | null)) | null { + if (prop.type === 'array' && prop.items && !isArray(prop.items)) { const propItems = prop.items; - if (propItems && !isArray(propItems) && propItems.type === 'string') { + if (propItems && !isArray(propItems.type)) { const withQuotes = (s: string) => `'` + s + `'`; return value => { if (!value) { @@ -241,58 +240,72 @@ function getArrayOfStringValidator(prop: IConfigurationPropertySchema): ((value: let message = ''; - if (!isStringArray(value)) { - message += nls.localize('validations.stringArrayIncorrectType', 'Incorrect type. Expected a string array.'); + if (!isArray(value)) { + message += nls.localize('validations.arrayIncorrectType', 'Incorrect type. Expected an array.'); message += '\n'; return message; } - const stringArrayValue = value; - + const arrayValue = value as unknown[]; if (prop.uniqueItems) { - if (new Set(stringArrayValue).size < stringArrayValue.length) { + if (new Set(arrayValue).size < arrayValue.length) { message += nls.localize('validations.stringArrayUniqueItems', 'Array has duplicate items'); message += '\n'; } } - if (prop.minItems && stringArrayValue.length < prop.minItems) { + if (prop.minItems && arrayValue.length < prop.minItems) { message += nls.localize('validations.stringArrayMinItem', 'Array must have at least {0} items', prop.minItems); message += '\n'; } - if (prop.maxItems && stringArrayValue.length > prop.maxItems) { + if (prop.maxItems && arrayValue.length > prop.maxItems) { message += nls.localize('validations.stringArrayMaxItem', 'Array must have at most {0} items', prop.maxItems); message += '\n'; } - if (typeof propItems.pattern === 'string') { - const patternRegex = new RegExp(propItems.pattern); - stringArrayValue.forEach(v => { - if (!patternRegex.test(v)) { - message += - propItems.patternErrorMessage || - nls.localize( - 'validations.stringArrayItemPattern', - 'Value {0} must match regex {1}.', + if (propItems.type === 'string') { + if (!isStringArray(arrayValue)) { + message += nls.localize('validations.stringArrayIncorrectType', 'Incorrect type. Expected a string array.'); + message += '\n'; + return message; + } + + if (typeof propItems.pattern === 'string') { + const patternRegex = new RegExp(propItems.pattern); + arrayValue.forEach(v => { + if (!patternRegex.test(v)) { + message += + propItems.patternErrorMessage || + nls.localize( + 'validations.stringArrayItemPattern', + 'Value {0} must match regex {1}.', + withQuotes(v), + withQuotes(propItems.pattern!) + ); + } + }); + } + + const propItemsEnum = propItems.enum; + if (propItemsEnum) { + arrayValue.forEach(v => { + if (propItemsEnum.indexOf(v) === -1) { + message += nls.localize( + 'validations.stringArrayItemEnum', + 'Value {0} is not one of {1}', withQuotes(v), - withQuotes(propItems.pattern!) + '[' + propItemsEnum.map(withQuotes).join(', ') + ']' ); - } - }); - } - - const propItemsEnum = propItems.enum; - if (propItemsEnum) { - stringArrayValue.forEach(v => { - if (propItemsEnum.indexOf(v) === -1) { - message += nls.localize( - 'validations.stringArrayItemEnum', - 'Value {0} is not one of {1}', - withQuotes(v), - '[' + propItemsEnum.map(withQuotes).join(', ') + ']' - ); - message += '\n'; + message += '\n'; + } + }); + } + } else if (propItems.type === 'integer' || propItems.type === 'number') { + arrayValue.forEach(v => { + const errorMessage = getErrorsForSchema(propItems, v); + if (errorMessage) { + message += `${v}: ${errorMessage}\n`; } }); } diff --git a/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts b/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts index 0ee46bad8e7..a849aa2f780 100644 --- a/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts +++ b/src/vs/workbench/services/preferences/test/common/preferencesValidation.test.ts @@ -279,6 +279,31 @@ suite('Preferences Validation', () => { } }); + test('numerical objects work', () => { + { + const obj = new Tester({ type: 'object', properties: { 'b': { type: 'number' } } }); + obj.accepts({ 'b': 2.5 }); + obj.accepts({ 'b': -2.5 }); + obj.accepts({ 'b': 0 }); + obj.accepts({ 'b': '0.12' }); + obj.rejects({ 'b': 'abc' }); + obj.rejects({ 'b': [] }); + obj.rejects({ 'b': false }); + obj.rejects({ 'b': null }); + obj.rejects({ 'b': undefined }); + } + { + const obj = new Tester({ type: 'object', properties: { 'b': { type: 'integer', minimum: 2, maximum: 5.5 } } }); + obj.accepts({ 'b': 2 }); + obj.accepts({ 'b': 3 }); + obj.accepts({ 'b': '3.0' }); + obj.accepts({ 'b': 5 }); + obj.rejects({ 'b': 1 }); + obj.rejects({ 'b': 6 }); + obj.rejects({ 'b': 5.5 }); + } + }); + test('patterns work', () => { { const urls = new Tester({ pattern: '^(hello)*$', type: 'string' }); @@ -312,7 +337,7 @@ suite('Preferences Validation', () => { this.validator = createValidator(settings)!; } - public accepts(input: string[]) { + public accepts(input: unknown[]) { assert.strictEqual(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to accept \`${JSON.stringify(input)}\`. Got ${this.validator(input)}.`); } @@ -365,6 +390,39 @@ suite('Preferences Validation', () => { } }); + test('array of numbers', () => { + // We accept parseable strings since the view handles strings + { + const arr = new ArrayTester({ type: 'array', items: { type: 'number' } }); + arr.accepts([]); + arr.accepts([2]); + arr.accepts([2, 3]); + arr.accepts(['2', '3']); + arr.accepts([6.6, '3', 7]); + arr.rejects(76); + arr.rejects(7.6); + arr.rejects([6, 'a', 7]); + } + { + const arr = new ArrayTester({ type: 'array', items: { type: 'integer', minimum: -2, maximum: 3 }, maxItems: 4 }); + arr.accepts([]); + arr.accepts([-2, 3]); + arr.accepts([2, 3]); + arr.accepts(['2', '3']); + arr.accepts(['-2', '0', '3']); + arr.accepts(['-2', 0.0, '3']); + arr.rejects(2); + arr.rejects(76); + arr.rejects([6, '3', 7]); + arr.rejects([2, 'a', 3]); + arr.rejects([-2, 4]); + arr.rejects([-1.2, 2.1]); + arr.rejects([-3, 3]); + arr.rejects([-3, 4]); + arr.rejects([2, 2, 2, 2, 2]); + } + }); + test('min-max and enum', () => { const arr = new ArrayTester({ type: 'array', items: { type: 'string', enum: ['a', 'b'] }, minItems: 1, maxItems: 2 }); diff --git a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts index 6ff7c1c2706..2806378bc82 100644 --- a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts +++ b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts @@ -38,8 +38,6 @@ export class SharedProcessService extends Disposable implements ISharedProcessSe // as a result. As such, make sure we await the `Restored` // phase before making a connection attempt, but also add a // timeout to be safe against possible deadlocks. - // TODO@sandbox revisit this when the shared process connection - // is more cruicial. await Promise.race([this.restoredBarrier.wait(), timeout(2000)]); // Acquire a message port connected to the shared process diff --git a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts index 152345e43f9..addcda618e8 100644 --- a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts @@ -10,7 +10,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { CSSIcon } from 'vs/base/common/codicons'; import { fontIdRegex } from 'vs/workbench/services/themes/common/productIconThemeSchema'; import * as resources from 'vs/base/common/resources'; -import { IExtensionDescription, isProposedApiEnabled } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; interface IIconExtensionPoint { id: string; @@ -125,7 +126,7 @@ export class IconExtensionPoint { const extensionValue = extension.value; const collector = extension.collector; - if (!isProposedApiEnabled(extension.description)) { + if (!isProposedApiEnabled(extension.description, undefined)) { collector.error(nls.localize('invalid.icons.proposedAPI', "'configuration.icons is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } @@ -179,7 +180,7 @@ export class IconFontExtensionPoint { const extensionValue = extension.value; const collector = extension.collector; - if (!isProposedApiEnabled(extension.description)) { + if (!isProposedApiEnabled(extension.description, undefined)) { collector.error(nls.localize('invalid.iconFonts.proposedAPI', "'configuration.iconFonts is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } diff --git a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts index 7b3c74f0457..c07beb56cab 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts @@ -107,7 +107,54 @@ suite('MainThreadDiagnostics', function () { await timeout(0); assert.strictEqual(markerService.read().length, 2); assert.strictEqual(changedData.length, 1); + assert.strictEqual(changedData[0].length, 1); assert.strictEqual(changedData[0][0][1][0].message, 'forgein_owner'); }); }); + + test('onDidChangeDiagnostics different behavior when "extensionKind" ui running on remote workspace #136955', function () { + return runWithFakedTimers({}, async () => { + + const markerData: IMarkerData = { + code: '666', + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1, + severity: 1, + source: 'me', + message: 'message' + }; + const target = URI.file('a'); + markerService.changeOne('bar', target, [markerData]); + + const changedData: [UriComponents, IMarkerData[]][][] = []; + + let diag = new MainThreadDiagnostics( + new class implements IExtHostContext { + remoteAuthority = ''; + extensionHostKind = ExtensionHostKind.LocalProcess; + assertRegistered() { } + set(v: any): any { return null; } + getProxy(): any { + return { + $acceptMarkersChange(data: [UriComponents, IMarkerData[]][]) { + changedData.push(data); + } + }; + } + drain(): any { return null; } + }, + markerService, + new class extends mock() { + override asCanonicalUri(uri: URI) { return uri; } + } + ); + + diag.$clear('bar'); + await timeout(0); + assert.strictEqual(markerService.read().length, 0); + assert.strictEqual(changedData.length, 1); + }); + }); }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 2e696179c8c..d908a25531e 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -662,6 +662,12 @@ export class TestSideBarPart implements IPaneCompositePart { onDidViewletOpenEmitter = new Emitter(); onDidViewletCloseEmitter = new Emitter(); + element: HTMLElement = undefined!; + minimumWidth = 0; + maximumWidth = 0; + minimumHeight = 0; + maximumHeight = 0; + onDidChange = Event.None; onDidPaneCompositeOpen = this.onDidViewletOpenEmitter.event; onDidPaneCompositeClose = this.onDidViewletCloseEmitter.event; @@ -675,11 +681,18 @@ export class TestSideBarPart implements IPaneCompositePart { hideActivePaneComposite(): void { } getLastActivePaneCompositeId(): string { return undefined!; } dispose() { } + layout(width: number, height: number, top: number, left: number): void { } } export class TestPanelPart implements IPaneCompositePart, IPaneCompositeSelectorPart { declare readonly _serviceBrand: undefined; + element: HTMLElement = undefined!; + minimumWidth = 0; + maximumWidth = 0; + minimumHeight = 0; + maximumHeight = 0; + onDidChange = Event.None; onDidPaneCompositeOpen = new Emitter().event; onDidPaneCompositeClose = new Emitter().event; @@ -695,6 +708,7 @@ export class TestPanelPart implements IPaneCompositePart, IPaneCompositeSelector getProgressIndicator(id: string) { return null!; } hideActivePaneComposite(): void { } getLastActivePaneCompositeId(): string { return undefined!; } + layout(width: number, height: number, top: number, left: number): void { } } export class TestViewsService implements IViewsService { diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index c8fa9fdd65f..1db8caf392a 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -58,7 +58,6 @@ import 'vs/workbench/electron-browser/desktop.main'; import 'vs/workbench/services/extensions/electron-browser/extensionService'; -import 'vs/platform/extensions/node/extensionHostStarter'; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/src/vs/workbench/workbench.desktop.sandbox.main.ts b/src/vs/workbench/workbench.desktop.sandbox.main.ts index 9279adf4e22..d7c7c3928ae 100644 --- a/src/vs/workbench/workbench.desktop.sandbox.main.ts +++ b/src/vs/workbench/workbench.desktop.sandbox.main.ts @@ -33,7 +33,6 @@ import 'vs/workbench/electron-sandbox/desktop.main'; //#region --- workbench services -import 'vs/workbench/services/extensions/electron-sandbox/extensionHostStarter'; //#endregion diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index 9f61b4e3720..b24066e7a34 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -52,6 +52,7 @@ import 'vs/workbench/services/credentials/electron-sandbox/credentialsService'; import 'vs/workbench/services/encryption/electron-sandbox/encryptionService'; import 'vs/workbench/services/localizations/electron-sandbox/localizationsService'; import 'vs/workbench/services/telemetry/electron-sandbox/telemetryService'; +import 'vs/workbench/services/extensions/electron-sandbox/extensionHostStarter'; import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService'; import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionTipsService'; import 'vs/workbench/services/userDataSync/electron-sandbox/userDataSyncMachinesService'; diff --git a/src/vs/vscode.d.ts b/src/vscode-dts/vscode.d.ts similarity index 99% rename from src/vs/vscode.d.ts rename to src/vscode-dts/vscode.d.ts index 02f02c68f19..84b27f81c62 100644 --- a/src/vs/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -818,7 +818,7 @@ declare module 'vscode' { /** * The optional ThemeColor of the icon. The color is currently only used in {@link TreeItem}. */ - readonly color?: ThemeColor; + readonly color?: ThemeColor | undefined; /** * Creates a reference to a theme icon. @@ -1107,13 +1107,13 @@ declare module 'vscode' { /** * The selections in this text editor. The primary selection is always at index 0. */ - selections: Selection[]; + selections: readonly Selection[]; /** * The current visible ranges in the editor (vertically). * This accounts only for vertical scrolling, and not for horizontal scrolling. */ - readonly visibleRanges: Range[]; + readonly visibleRanges: readonly Range[]; /** * Text editor options. @@ -1672,6 +1672,12 @@ declare module 'vscode' { * Always show this item. */ alwaysShow?: boolean; + + /** + * Optional buttons that will be rendered on this particular item. These buttons will trigger + * an {@link QuickPickItemButtonEvent} when clicked. + */ + buttons?: readonly QuickInputButton[]; } /** @@ -2705,7 +2711,7 @@ declare module 'vscode' { /* * If specified the expression overrides the extracted expression. */ - readonly expression?: string; + readonly expression?: string | undefined; /** * Creates a new evaluatable expression object. @@ -2772,7 +2778,7 @@ declare module 'vscode' { /** * If specified the name of the variable to look up. */ - readonly variableName?: string; + readonly variableName?: string | undefined; /** * How to perform the lookup. */ @@ -2801,7 +2807,7 @@ declare module 'vscode' { /** * If specified the expression overrides the extracted expression. */ - readonly expression?: string; + readonly expression?: string | undefined; /** * Creates a new InlineValueEvaluatableExpression object. * @@ -4901,7 +4907,7 @@ declare module 'vscode' { * An optional word pattern that describes valid contents for the given ranges. * If no pattern is provided, the language configuration's word pattern will be used. */ - readonly wordPattern?: RegExp; + readonly wordPattern: RegExp | undefined; } /** @@ -5656,6 +5662,13 @@ declare module 'vscode' { */ appendLine(value: string): void; + /** + * Replaces all output from the channel with the given value. + * + * @param value A string, falsy values will not be printed. + */ + replace(value: string): void; + /** * Removes all output from the channel. */ @@ -6564,7 +6577,7 @@ declare module 'vscode' { * Whether the task that is part of this group is the default for the group. * This property cannot be set through API, and is controlled by a user's task configurations. */ - readonly isDefault?: boolean; + readonly isDefault: boolean | undefined; /** * The ID of the task group. Is one of TaskGroup.Clean.id, TaskGroup.Build.id, TaskGroup.Rebuild.id, or TaskGroup.Test.id. @@ -6900,7 +6913,7 @@ declare module 'vscode' { /** * The task's scope. */ - readonly scope?: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder; + readonly scope: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder | undefined; /** * The task's name @@ -8643,7 +8656,7 @@ declare module 'vscode' { /** * The currently visible editors or an empty array. */ - export let visibleTextEditors: TextEditor[]; + export let visibleTextEditors: readonly TextEditor[]; /** * An {@link Event} which fires when the {@link window.activeTextEditor active editor} @@ -8656,7 +8669,7 @@ declare module 'vscode' { * An {@link Event} which fires when the array of {@link window.visibleTextEditors visible editors} * has changed. */ - export const onDidChangeVisibleTextEditors: Event; + export const onDidChangeVisibleTextEditors: Event; /** * An {@link Event} which fires when the selection in an editor has changed. @@ -9341,7 +9354,7 @@ declare module 'vscode' { /** * Selected elements. */ - readonly selection: T[]; + readonly selection: readonly T[]; } @@ -9375,7 +9388,7 @@ declare module 'vscode' { /** * Currently selected elements. */ - readonly selection: T[]; + readonly selection: readonly T[]; /** * Event that is fired when the {@link TreeView.selection selection} has changed @@ -10192,6 +10205,12 @@ declare module 'vscode' { */ readonly onDidTriggerButton: Event; + /** + * An event signaling when a button in a particular {@link QuickPickItem} was triggered. + * This event does not fire for buttons in the title bar. + */ + readonly onDidTriggerItemButton: Event>; + /** * Items to pick from. This can be read and updated by the extension. */ @@ -10212,6 +10231,11 @@ declare module 'vscode' { */ matchOnDetail: boolean; + /* + * An optional flag to maintain the scroll position of the quick pick when the quick pick items are updated. Defaults to false. + */ + keepScrollPosition?: boolean; + /** * Active items. This can be read and updated by the extension. */ @@ -10323,6 +10347,21 @@ declare module 'vscode' { private constructor(); } + /** + * An event signaling when a button in a particular {@link QuickPickItem} was triggered. + * This event does not fire for buttons in the title bar. + */ + export interface QuickPickItemButtonEvent { + /** + * The button that was clicked. + */ + readonly button: QuickInputButton; + /** + * The item that the button belongs to. + */ + readonly item: T; + } + /** * An event describing an individual change in the text of a {@link TextDocument document}. */ @@ -13039,7 +13078,7 @@ declare module 'vscode' { /** * The host. */ - readonly host?: string; + readonly host?: string | undefined; /** * Create a description for a debug adapter running as a socket based server. @@ -13212,15 +13251,15 @@ declare module 'vscode' { /** * An optional expression for conditional breakpoints. */ - readonly condition?: string; + readonly condition?: string | undefined; /** * An optional expression that controls how many hits of the breakpoint are ignored. */ - readonly hitCondition?: string; + readonly hitCondition?: string | undefined; /** * An optional message that gets logged when this breakpoint is hit. Embedded expressions within {} are interpolated by the debug adapter. */ - readonly logMessage?: string; + readonly logMessage?: string | undefined; protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); } @@ -13348,7 +13387,7 @@ declare module 'vscode' { /** * List of breakpoints. */ - export let breakpoints: Breakpoint[]; + export let breakpoints: readonly Breakpoint[]; /** * An {@link Event} which fires when the {@link debug.activeDebugSession active debug session} @@ -13848,6 +13887,17 @@ declare module 'vscode' { * Options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. */ export interface AuthenticationGetSessionOptions { + /** + * Whether the existing user session preference should be cleared. + * + * For authentication providers that support being signed into multiple accounts at once, the user will be + * prompted to select an account to use when {@link authentication.getSession getSession} is called. This preference + * is remembered until {@link authentication.getSession getSession} is called with this flag. + * + * Defaults to false. + */ + clearSessionPreference?: boolean; + /** * Whether login should be performed if there is no matching session. * @@ -13859,19 +13909,23 @@ declare module 'vscode' { * will also result in an immediate modal dialog, and false will add a numbered badge to the accounts icon. * * Defaults to false. + * + * Note: you cannot use this option with {@link silent}. */ createIfNone?: boolean; + /** - * Whether the existing user session preference should be cleared. + * Whether we should show the indication to sign in in the Accounts menu. * - * For authentication providers that support being signed into multiple accounts at once, the user will be - * prompted to select an account to use when {@link authentication.getSession getSession} is called. This preference - * is remembered until {@link authentication.getSession getSession} is called with this flag. + * If false, the user will be shown a badge on the Accounts menu with an option to sign in for the extension. + * If true, no indication will be shown. * * Defaults to false. + * + * Note: you cannot use this option with any other options that prompt the user like {@link createIfNone}. */ - clearSessionPreference?: boolean; + silent?: boolean; } /** @@ -14268,7 +14322,7 @@ declare module 'vscode' { * The process of running tests should resolve the children of any test * items who have not yet been resolved. */ - readonly include: TestItem[] | undefined; + readonly include: readonly TestItem[] | undefined; /** * An array of tests the user has marked as excluded from the test included @@ -14277,7 +14331,7 @@ declare module 'vscode' { * May be omitted if no exclusions were requested. Test controllers should * not run excluded tests or any children of excluded tests. */ - readonly exclude: TestItem[] | undefined; + readonly exclude: readonly TestItem[] | undefined; /** * The profile used for this request. This will always be defined diff --git a/src/vscode-dts/vscode.proposed.authSession.d.ts b/src/vscode-dts/vscode.proposed.authSession.d.ts new file mode 100644 index 00000000000..a3e593d4224 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.authSession.d.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + /** + * More options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. + */ + export interface AuthenticationGetSessionOptions { + /** + * Whether we should attempt to reauthenticate even if there is already a session available. + * + * If true, a modal dialog will be shown asking the user to sign in again. This is mostly used for scenarios + * where the token needs to be re minted because it has lost some authorization. + * + * Defaults to false. + */ + forceNewSession?: boolean | { detail: string }; + } + + export namespace authentication { + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @param providerId The id of the provider to use + * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider + * @param options The {@link AuthenticationGetSessionOptions} to use + * @returns A thenable that resolves to an authentication session + */ + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | { detail: string } }): Thenable; + export function hasSession(providerId: string, scopes: readonly string[]): Thenable; + } +} diff --git a/extensions/typescript-language-features/src/typings/ref.d.ts b/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts similarity index 82% rename from extensions/typescript-language-features/src/typings/ref.d.ts rename to src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts index 43a9cadf250..035b8f1f8b5 100644 --- a/extensions/typescript-language-features/src/typings/ref.d.ts +++ b/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts @@ -3,4 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// +// empty place holder declaration for the `viewsWelcome`-contribution point diff --git a/src/vscode-dts/vscode.proposed.customEditorMove.d.ts b/src/vscode-dts/vscode.proposed.customEditorMove.d.ts new file mode 100644 index 00000000000..f988913ea61 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.customEditorMove.d.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/86146 + + // TODO: Also for custom editor + + export interface CustomTextEditorProvider { + + /** + * Handle when the underlying resource for a custom editor is renamed. + * + * This allows the webview for the editor be preserved throughout the rename. If this method is not implemented, + * the editor will destroy the previous custom editor and create a replacement one. + * + * @param newDocument New text document to use for the custom editor. + * @param existingWebviewPanel Webview panel for the custom editor. + * @param token A cancellation token that indicates the result is no longer needed. + * + * @return Thenable indicating that the webview editor has been moved. + */ + // eslint-disable-next-line vscode-dts-provider-naming + moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel, token: CancellationToken): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.diffCommand.d.ts b/src/vscode-dts/vscode.proposed.diffCommand.d.ts new file mode 100644 index 00000000000..84f4328e077 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.diffCommand.d.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/84899 + + /** + * The contiguous set of modified lines in a diff. + */ + export interface LineChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; + } + + export namespace commands { + + /** + * Registers a diff information command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Diff information commands are different from ordinary {@link commands.registerCommand commands} as + * they only execute when there is an active diff editor when the command is called, and the diff + * information has been computed. Also, the command handler of an editor command has access to + * the diff information. + * + * @param command A unique identifier for the command. + * @param callback A command handler function with access to the {@link LineChange diff information}. + * @param thisArg The `this` context used when invoking the handler function. + * @return Disposable which unregisters this command on disposal. + */ + export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; + } +} diff --git a/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts b/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts similarity index 72% rename from extensions/vscode-custom-editor-tests/src/typings/ref.d.ts rename to src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts index bf67b19225d..7d033796355 100644 --- a/extensions/vscode-custom-editor-tests/src/typings/ref.d.ts +++ b/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts @@ -3,6 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// +declare module 'vscode' { + // todo@jrieken add issue reference + + export interface DocumentFilter { + readonly exclusive?: boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.editorInsets.d.ts b/src/vscode-dts/vscode.proposed.editorInsets.d.ts new file mode 100644 index 00000000000..c13b4208d42 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.editorInsets.d.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// https://github.com/microsoft/vscode/issues/85682 + +declare module 'vscode' { + + export interface WebviewEditorInset { + readonly editor: TextEditor; + readonly line: number; + readonly height: number; + readonly webview: Webview; + readonly onDidDispose: Event; + dispose(): void; + } + + export namespace window { + export function createWebviewTextEditorInset(editor: TextEditor, line: number, height: number, options?: WebviewOptions): WebviewEditorInset; + } +} diff --git a/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts b/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts new file mode 100644 index 00000000000..356587ebc95 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.extensionRuntime.d.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. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/104436 + + export enum ExtensionRuntime { + /** + * The extension is running in a NodeJS extension host. Runtime access to NodeJS APIs is available. + */ + Node = 1, + /** + * The extension is running in a Webworker extension host. Runtime access is limited to Webworker APIs. + */ + Webworker = 2 + } + + export interface ExtensionContext { + readonly extensionRuntime: ExtensionRuntime; + } +} diff --git a/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts b/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts new file mode 100644 index 00000000000..2b5432404f7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/109277 + + /** + * Details if an `ExternalUriOpener` can open a uri. + * + * The priority is also used to rank multiple openers against each other and determine + * if an opener should be selected automatically or if the user should be prompted to + * select an opener. + * + * The editor will try to use the best available opener, as sorted by `ExternalUriOpenerPriority`. + * If there are multiple potential "best" openers for a URI, then the user will be prompted + * to select an opener. + */ + export enum ExternalUriOpenerPriority { + /** + * The opener is disabled and will never be shown to users. + * + * Note that the opener can still be used if the user specifically + * configures it in their settings. + */ + None = 0, + + /** + * The opener can open the uri but will not cause a prompt on its own + * since the editor always contributes a built-in `Default` opener. + */ + Option = 1, + + /** + * The opener can open the uri. + * + * The editor's built-in opener has `Default` priority. This means that any additional `Default` + * openers will cause the user to be prompted to select from a list of all potential openers. + */ + Default = 2, + + /** + * The opener can open the uri and should be automatically selected over any + * default openers, include the built-in one from the editor. + * + * A preferred opener will be automatically selected if no other preferred openers + * are available. If multiple preferred openers are available, then the user + * is shown a prompt with all potential openers (not just preferred openers). + */ + Preferred = 3, + } + + /** + * Handles opening uris to external resources, such as http(s) links. + * + * Extensions can implement an `ExternalUriOpener` to open `http` links to a webserver + * inside of the editor instead of having the link be opened by the web browser. + * + * Currently openers may only be registered for `http` and `https` uris. + */ + export interface ExternalUriOpener { + + /** + * Check if the opener can open a uri. + * + * @param uri The uri being opened. This is the uri that the user clicked on. It has + * not yet gone through port forwarding. + * @param token Cancellation token indicating that the result is no longer needed. + * + * @return Priority indicating if the opener can open the external uri. + */ + canOpenExternalUri(uri: Uri, token: CancellationToken): ExternalUriOpenerPriority | Thenable; + + /** + * Open a uri. + * + * This is invoked when: + * + * - The user clicks a link which does not have an assigned opener. In this case, first `canOpenExternalUri` + * is called and if the user selects this opener, then `openExternalUri` is called. + * - The user sets the default opener for a link in their settings and then visits a link. + * + * @param resolvedUri The uri to open. This uri may have been transformed by port forwarding, so it + * may not match the original uri passed to `canOpenExternalUri`. Use `ctx.originalUri` to check the + * original uri. + * @param ctx Additional information about the uri being opened. + * @param token Cancellation token indicating that opening has been canceled. + * + * @return Thenable indicating that the opening has completed. + */ + openExternalUri(resolvedUri: Uri, ctx: OpenExternalUriContext, token: CancellationToken): Thenable | void; + } + + /** + * Additional information about the uri being opened. + */ + interface OpenExternalUriContext { + /** + * The uri that triggered the open. + * + * This is the original uri that the user clicked on or that was passed to `openExternal.` + * Due to port forwarding, this may not match the `resolvedUri` passed to `openExternalUri`. + */ + readonly sourceUri: Uri; + } + + /** + * Additional metadata about a registered `ExternalUriOpener`. + */ + interface ExternalUriOpenerMetadata { + + /** + * List of uri schemes the opener is triggered for. + * + * Currently only `http` and `https` are supported. + */ + readonly schemes: readonly string[] + + /** + * Text displayed to the user that explains what the opener does. + * + * For example, 'Open in browser preview' + */ + readonly label: string; + } + + namespace window { + /** + * Register a new `ExternalUriOpener`. + * + * When a uri is about to be opened, an `onOpenExternalUri:SCHEME` activation event is fired. + * + * @param id Unique id of the opener, such as `myExtension.browserPreview`. This is used in settings + * and commands to identify the opener. + * @param opener Opener to register. + * @param metadata Additional information about the opener. + * + * @returns Disposable that unregisters the opener. + */ + export function registerExternalUriOpener(id: string, opener: ExternalUriOpener, metadata: ExternalUriOpenerMetadata): Disposable; + } + + interface OpenExternalOptions { + /** + * Allows using openers contributed by extensions through `registerExternalUriOpener` + * when opening the resource. + * + * If `true`, the editor will check if any contributed openers can handle the + * uri, and fallback to the default opener behavior. + * + * If it is string, this specifies the id of the `ExternalUriOpener` + * that should be used if it is available. Use `'default'` to force the editor's + * standard external opener to be used. + */ + readonly allowContributedOpeners?: boolean | string; + } + + namespace env { + export function openExternal(target: Uri, options?: OpenExternalOptions): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts b/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts new file mode 100644 index 00000000000..bf7bc5ecba3 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/73524 + + /** + * The parameters of a query for file search. + */ + export interface FileSearchQuery { + /** + * The search pattern to match against file paths. + */ + pattern: string; + } + + /** + * Options that apply to file search. + */ + export interface FileSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults?: number; + + /** + * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, + * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. + */ + session?: CancellationToken; + } + + /** + * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. + * + * A FileSearchProvider is the more powerful of two ways to implement file search in the editor. Use a FileSearchProvider if you wish to search within a folder for + * all files that match the user's query. + * + * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, + * and in that case, every file in the folder should be returned. + */ + export interface FileSearchProvider { + /** + * Provide the set of files that match a certain file path pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching files. + * @param token A cancellation token. + */ + provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts b/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts new file mode 100644 index 00000000000..14cd44d2048 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/59924 + + /** + * Options that can be set on a findTextInFiles search. + */ + export interface FindTextInFilesOptions { + /** + * A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern + * will be matched against the file paths of files relative to their workspace. Use a {@link RelativePattern relative pattern} + * to restrict the search results to a {@link WorkspaceFolder workspace folder}. + */ + include?: GlobPattern; + + /** + * A {@link GlobPattern glob pattern} that defines files and folders to exclude. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. When `undefined`, default excludes will + * apply. + */ + exclude?: GlobPattern; + + /** + * Whether to use the default and user-configured excludes. Defaults to true. + */ + useDefaultExcludes?: boolean; + + /** + * The maximum number of results to search for + */ + maxResults?: number; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles?: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles?: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks?: boolean; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + export namespace workspace { + /** + * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + + /** + * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.fsChunks.d.ts b/src/vscode-dts/vscode.proposed.fsChunks.d.ts new file mode 100644 index 00000000000..fe582949348 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.fsChunks.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/84515 + + export interface FileSystemProvider { + open?(resource: Uri, options: { create: boolean; }): number | Thenable; + close?(fd: number): void | Thenable; + read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; + write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.inlayHints.d.ts b/src/vscode-dts/vscode.proposed.inlayHints.d.ts new file mode 100644 index 00000000000..9b3a30a7e3f --- /dev/null +++ b/src/vscode-dts/vscode.proposed.inlayHints.d.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/16221 + + // todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range) + // todo@API add "mini-markdown" for links and styles + // (done) remove description + // (done) rename to InlayHint + // (done) add InlayHintKind with type, argument, etc + + export namespace languages { + /** + * Register a inlay hints provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inlay hints provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable; + } + + export enum InlayHintKind { + Other = 0, + Type = 1, + Parameter = 2, + } + + /** + * Inlay hint information. + */ + export class InlayHint { + /** + * The text of the hint. + */ + // todo@API label? + text: string; + /** + * The position of this hint. + */ + position: Position; + /** + * The kind of this hint. + */ + kind?: InlayHintKind; + /** + * Whitespace before the hint. + */ + whitespaceBefore?: boolean; + /** + * Whitespace after the hint. + */ + whitespaceAfter?: boolean; + + // todo@API make range first argument + constructor(text: string, position: Position, kind?: InlayHintKind); + } + + /** + * The inlay hints provider interface defines the contract between extensions and + * the inlay hints feature. + */ + export interface InlayHintsProvider { + + /** + * An optional event to signal that inlay hints have changed. + * @see {@link EventEmitter} + */ + //todo@API needs proper doc (like others) + onDidChangeInlayHints?: Event; + + /** + * + * @param model The document in which the command was invoked. + * @param range The range for which inlay hints should be computed. + * @param token A cancellation token. + * @return A list of inlay hints or a thenable that resolves to such. + */ + provideInlayHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; + } +} diff --git a/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts new file mode 100644 index 00000000000..a232a6d07e2 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima + + export namespace languages { + /** + * Registers an inline completion provider. + */ + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; + } + + export interface InlineCompletionItemProvider { + /** + * Provides inline completion items for the given position and document. + * If inline completions are enabled, this method will be called whenever the user stopped typing. + * It will also be called when the user explicitly triggers inline completions or asks for the next or previous inline completion. + * Use `context.triggerKind` to distinguish between these scenarios. + */ + provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult | T[]>; + } + + export interface InlineCompletionContext { + /** + * How the completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; + + /** + * Provides information about the currently selected item in the autocomplete widget if it is visible. + * + * If set, provided inline completions must extend the text of the selected item + * and use the same range, otherwise they are not shown as preview. + * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, + * the inline completion must also replace `.` and start with `.log`, for example `.log()`. + * + * Inline completion providers are requested again whenever the selected item changes. + * + * The user must configure `"editor.suggest.preview": true` for this feature. + */ + readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; + } + + export interface SelectedCompletionInfo { + range: Range; + text: string; + } + + /** + * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 0, + + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Explicit = 1, + } + + export class InlineCompletionList { + items: T[]; + + constructor(items: T[]); + } + + export class InlineCompletionItem { + /** + * The text to replace the range with. + * + * The text the range refers to should be a prefix of this value and must be a subword (`AB` and `BEF` are subwords of `ABCDEF`, but `Ab` is not). + */ + text: string; + + /** + * The range to replace. + * Must begin and end on the same line. + * + * Prefer replacements over insertions to avoid cache invalidation: + * Instead of reporting a completion that inserts an extension at the end of a word, + * the whole word should be replaced with the extended word. + */ + range?: Range; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command; + + constructor(text: string, range?: Range, command?: Command); + } + + + /** + * Be aware that this API will not ever be finalized. + */ + export namespace window { + export function getInlineCompletionItemController(provider: InlineCompletionItemProvider): InlineCompletionController; + } + + /** + * Be aware that this API will not ever be finalized. + */ + export interface InlineCompletionController { + /** + * Is fired when an inline completion item is shown to the user. + */ + // eslint-disable-next-line vscode-dts-event-naming + readonly onDidShowCompletionItem: Event>; + } + + /** + * Be aware that this API will not ever be finalized. + */ + export interface InlineCompletionItemDidShowEvent { + completionItem: T; + } +} diff --git a/src/vscode-dts/vscode.proposed.languageStatus.d.ts b/src/vscode-dts/vscode.proposed.languageStatus.d.ts new file mode 100644 index 00000000000..0249626162b --- /dev/null +++ b/src/vscode-dts/vscode.proposed.languageStatus.d.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// https://github.com/microsoft/vscode/issues/129037 + +declare module 'vscode' { + + enum LanguageStatusSeverity { + Information = 0, + Warning = 1, + Error = 2 + } + + interface LanguageStatusItem { + readonly id: string; + selector: DocumentSelector; + // todo@jrieken replace with boolean ala needsAttention + severity: LanguageStatusSeverity; + name: string | undefined; + text: string; + detail?: string; + command: Command | undefined; + accessibilityInformation?: AccessibilityInformation; + dispose(): void; + } + + namespace languages { + export function createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts b/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts new file mode 100644 index 00000000000..67e5c4b0fbf --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/124970 + + /** + * The execution state of a notebook cell. + */ + export enum NotebookCellExecutionState { + /** + * The cell is idle. + */ + Idle = 1, + /** + * Execution for the cell is pending. + */ + Pending = 2, + /** + * The cell is currently executing. + */ + Executing = 3, + } + + /** + * An event describing a cell execution state change. + */ + export interface NotebookCellExecutionStateChangeEvent { + /** + * The {@link NotebookCell cell} for which the execution state has changed. + */ + readonly cell: NotebookCell; + + /** + * The new execution state of the cell. + */ + readonly state: NotebookCellExecutionState; + } + + export namespace notebooks { + + /** + * An {@link Event} which fires when the execution state of a cell has changed. + */ + // todo@API this is an event that is fired for a property that cells don't have and that makes me wonder + // how a correct consumer works, e.g the consumer could have been late and missed an event? + export const onDidChangeNotebookCellExecutionState: Event; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts b/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts new file mode 100644 index 00000000000..fd4e0e0b001 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + export namespace notebooks { + /** + * Create a document that is the concatenation of all notebook cells. By default all code-cells are included + * but a selector can be provided to narrow to down the set of cells. + * + * @param notebook + * @param selector + */ + // todo@API really needed? we didn't find a user here + export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; + } + + export interface NotebookConcatTextDocument { + readonly uri: Uri; + readonly isClosed: boolean; + dispose(): void; + readonly onDidChange: Event; + readonly version: number; + getText(): string; + getText(range: Range): string; + + offsetAt(position: Position): number; + positionAt(offset: number): Position; + validateRange(range: Range): Range; + validatePosition(position: Position): Position; + + locationAt(positionOrRange: Position | Range): Location; + positionAt(location: Location): Position; + contains(uri: Uri): boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts new file mode 100644 index 00000000000..d30fdb167f4 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + interface NotebookDocumentBackup { + /** + * Unique identifier for the backup. + * + * This id is passed back to your extension in `openNotebook` when opening a notebook editor from a backup. + */ + readonly id: string; + + /** + * Delete the current backup. + * + * This is called by the editor when it is clear the current backup is no longer needed, such as when a new backup + * is made or when the file is saved. + */ + delete(): void; + } + + interface NotebookDocumentBackupContext { + readonly destination: Uri; + } + + interface NotebookDocumentOpenContext { + readonly backupId?: string; + readonly untitledDocumentData?: Uint8Array; + } + + // todo@API use openNotebookDOCUMENT to align with openCustomDocument etc? + // todo@API rename to NotebookDocumentContentProvider + export interface NotebookContentProvider { + + readonly options?: NotebookDocumentContentOptions; + readonly onDidChangeNotebookContentOptions?: Event; + + /** + * Content providers should always use {@link FileSystemProvider file system providers} to + * resolve the raw content for `uri` as the resouce is not necessarily a file on disk. + */ + openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext, token: CancellationToken): NotebookData | Thenable; + + // todo@API use NotebookData instead + saveNotebook(document: NotebookDocument, token: CancellationToken): Thenable; + + // todo@API use NotebookData instead + saveNotebookAs(targetResource: Uri, document: NotebookDocument, token: CancellationToken): Thenable; + + // todo@API use NotebookData instead + backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable; + } + + export namespace workspace { + + // TODO@api use NotebookDocumentFilter instead of just notebookType:string? + // TODO@API options duplicates the more powerful variant on NotebookContentProvider + export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions): Disposable; + } +} diff --git a/extensions/vscode-colorize-tests/src/typings/ref.d.ts b/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts similarity index 61% rename from extensions/vscode-colorize-tests/src/typings/ref.d.ts rename to src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts index a17099ac50c..b8a4a5376e8 100644 --- a/extensions/vscode-colorize-tests/src/typings/ref.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts @@ -3,7 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// -/// +declare module 'vscode' { + // https://github.com/microsoft/vscode-jupyter/issues/7373 + + export interface NotebookController { + /** + * The human-readable label used to categorise controllers. + */ + kind?: string; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts b/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts new file mode 100644 index 00000000000..6bd35115a81 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // eslint-disable-next-line vscode-dts-region-comments + // @roblourens: new debug session option for simple UI 'managedByParent' (see https://github.com/microsoft/vscode/issues/128588) + + /** + * Options for {@link debug.startDebugging starting a debug session}. + */ + export interface DebugSessionOptions { + + debugUI?: { + /** + * When true, the debug toolbar will not be shown for this session, the window statusbar color will not be changed, and the debug viewlet will not be automatically revealed. + */ + simple?: boolean; + } + + /** + * When true, a save will not be triggered for open editors when starting a debug session, regardless of the value of the `debug.saveBeforeStart` setting. + */ + suppressSaveBeforeStart?: boolean; + } +} diff --git a/extensions/github-authentication/src/typings/ref.d.ts b/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts similarity index 71% rename from extensions/github-authentication/src/typings/ref.d.ts rename to src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts index c9849d48e08..c9ac1073772 100644 --- a/extensions/github-authentication/src/typings/ref.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts @@ -3,5 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + export interface NotebookCellOutput { + id: string; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookEditor.d.ts b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts new file mode 100644 index 00000000000..0181b8f7ef2 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + */ + export enum NotebookEditorRevealType { + /** + * The range will be revealed with as little scrolling as possible. + */ + Default = 0, + + /** + * The range will always be revealed in the center of the viewport. + */ + InCenter = 1, + + /** + * If the range is outside the viewport, it will be revealed in the center of the viewport. + * Otherwise, it will be revealed with as little scrolling as possible. + */ + InCenterIfOutsideViewport = 2, + + /** + * The range will always be revealed at the top of the viewport. + */ + AtTop = 3 + } + + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + */ + export interface NotebookEditor { + /** + * The document associated with this notebook editor. + */ + //todo@api rename to notebook? + readonly document: NotebookDocument; + + /** + * The selections on this notebook editor. + * + * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; + */ + selections: NotebookRange[]; + + /** + * The current visible ranges in the editor (vertically). + */ + readonly visibleRanges: NotebookRange[]; + + /** + * Scroll as indicated by `revealType` in order to reveal the given range. + * + * @param range A range. + * @param revealType The scrolling strategy for revealing `range`. + */ + revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; + + /** + * The column in which this editor shows. + */ + readonly viewColumn?: ViewColumn; + } + + export interface NotebookDocumentMetadataChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the document metadata have changed. + */ + //todo@API rename to notebook? + readonly document: NotebookDocument; + } + + export interface NotebookCellsChangeData { + readonly start: number; + // todo@API end? Use NotebookCellRange instead? + readonly deletedCount: number; + // todo@API removedCells, deletedCells? + readonly deletedItems: NotebookCell[]; + // todo@API addedCells, insertedCells, newCells? + readonly items: NotebookCell[]; + } + + export interface NotebookCellsChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the cells have changed. + */ + //todo@API rename to notebook? + readonly document: NotebookDocument; + readonly changes: ReadonlyArray; + } + + export interface NotebookCellOutputsChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the cell outputs have changed. + */ + //todo@API remove? use cell.notebook instead? + readonly document: NotebookDocument; + // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell + readonly cells: NotebookCell[]; + } + + export interface NotebookCellMetadataChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the cell metadata have changed. + */ + //todo@API remove? use cell.notebook instead? + readonly document: NotebookDocument; + // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell + readonly cell: NotebookCell; + } + + export interface NotebookEditorSelectionChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the selections have changed. + */ + readonly notebookEditor: NotebookEditor; + readonly selections: ReadonlyArray + } + + export interface NotebookEditorVisibleRangesChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. + */ + readonly notebookEditor: NotebookEditor; + readonly visibleRanges: ReadonlyArray; + } + + + export interface NotebookDocumentShowOptions { + viewColumn?: ViewColumn; + preserveFocus?: boolean; + preview?: boolean; + selections?: NotebookRange[]; + } + + export namespace notebooks { + + + + export const onDidSaveNotebookDocument: Event; + + export const onDidChangeNotebookDocumentMetadata: Event; + export const onDidChangeNotebookCells: Event; + + // todo@API add onDidChangeNotebookCellOutputs + export const onDidChangeCellOutputs: Event; + + // todo@API add onDidChangeNotebookCellMetadata + export const onDidChangeCellMetadata: Event; + } + + export namespace window { + export const visibleNotebookEditors: NotebookEditor[]; + export const onDidChangeVisibleNotebookEditors: Event; + export const activeNotebookEditor: NotebookEditor | undefined; + export const onDidChangeActiveNotebookEditor: Event; + export const onDidChangeNotebookEditorSelection: Event; + export const onDidChangeNotebookEditorVisibleRanges: Event; + + export function showNotebookDocument(uri: Uri, options?: NotebookDocumentShowOptions): Thenable; + export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts b/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts new file mode 100644 index 00000000000..5a07e05ef78 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + export interface NotebookEditor { + setDecorations(decorationType: NotebookEditorDecorationType, range: NotebookRange): void; + } + + export interface NotebookDecorationRenderOptions { + backgroundColor?: string | ThemeColor; + borderColor?: string | ThemeColor; + top?: ThemableDecorationAttachmentRenderOptions; + } + + export interface NotebookEditorDecorationType { + readonly key: string; + dispose(): void; + } + + export namespace notebooks { + export function createNotebookEditorDecorationType(options: NotebookDecorationRenderOptions): NotebookEditorDecorationType; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts b/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts new file mode 100644 index 00000000000..6c954b5f76c --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + // todo@API add NotebookEdit-type which handles all these cases? + // export class NotebookEdit { + // range: NotebookRange; + // newCells: NotebookCellData[]; + // newMetadata?: NotebookDocumentMetadata; + // constructor(range: NotebookRange, newCells: NotebookCellData) + // } + + // export class NotebookCellEdit { + // newMetadata?: NotebookCellMetadata; + // } + + // export interface WorkspaceEdit { + // set(uri: Uri, edits: TextEdit[] | NotebookEdit[]): void + // } + + export interface WorkspaceEdit { + // todo@API add NotebookEdit-type which handles all these cases? + replaceNotebookMetadata(uri: Uri, value: { [key: string]: any }): void; + replaceNotebookCells(uri: Uri, range: NotebookRange, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; + replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: { [key: string]: any }, metadata?: WorkspaceEditEntryMetadata): void; + } + + export interface NotebookEditorEdit { + replaceMetadata(value: { [key: string]: any }): void; + replaceCells(start: number, end: number, cells: NotebookCellData[]): void; + replaceCellMetadata(index: number, metadata: { [key: string]: any }): void; + } + + export interface NotebookEditor { + /** + * Perform an edit on the notebook associated with this notebook editor. + * + * The given callback-function is invoked with an {@link NotebookEditorEdit edit-builder} which must + * be used to make edits. Note that the edit-builder is only valid while the + * callback executes. + * + * @param callback A function which can create edits using an {@link NotebookEditorEdit edit-builder}. + * @return A promise that resolves with a value indicating if the edits could be applied. + */ + // @jrieken REMOVE maybe + edit(callback: (editBuilder: NotebookEditorEdit) => void): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts b/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts new file mode 100644 index 00000000000..de2966db16c --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + export interface NotebookRegistrationData { + displayName: string; + filenamePattern: (GlobPattern | { include: GlobPattern; exclude: GlobPattern; })[]; + exclusive?: boolean; + } + + export namespace workspace { + // SPECIAL overload with NotebookRegistrationData + export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions, registrationData?: NotebookRegistrationData): Disposable; + // SPECIAL overload with NotebookRegistrationData + export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions, registration?: NotebookRegistrationData): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts b/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts new file mode 100644 index 00000000000..88660ded2e6 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/123601 + + /** + * Represents a script that is loaded into the notebook renderer before rendering output. This allows + * to provide and share functionality for notebook markup and notebook output renderers. + */ + export class NotebookRendererScript { + + /** + * APIs that the preload provides to the renderer. These are matched + * against the `dependencies` and `optionalDependencies` arrays in the + * notebook renderer contribution point. + */ + provides: string[]; + + /** + * URI of the JavaScript module to preload. + * + * This module must export an `activate` function that takes a context object that contains the notebook API. + */ + uri: Uri; + + /** + * @param uri URI of the JavaScript module to preload + * @param provides Value for the `provides` property + */ + constructor(uri: Uri, provides?: string | string[]); + } + + export interface NotebookController { + + // todo@API allow add, not remove + readonly rendererScripts: NotebookRendererScript[]; + + /** + * An event that fires when a {@link NotebookController.rendererScripts renderer script} has send a message to + * the controller. + */ + readonly onDidReceiveMessage: Event<{ editor: NotebookEditor, message: any }>; + + /** + * Send a message to the renderer of notebook editors. + * + * Note that only editors showing documents that are bound to this controller + * are receiving the message. + * + * @param message The message to send. + * @param editor A specific editor to send the message to. When `undefined` all applicable editors are receiving the message. + * @returns A promise that resolves to a boolean indicating if the message has been send or not. + */ + postMessage(message: any, editor?: NotebookEditor): Thenable; + + //todo@API validate this works + asWebviewUri(localResource: Uri): Uri; + } + + export namespace notebooks { + + export function createNotebookController(id: string, viewType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable, rendererScripts?: NotebookRendererScript[]): NotebookController; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookMime.d.ts b/src/vscode-dts/vscode.proposed.notebookMime.d.ts new file mode 100644 index 00000000000..fb1cf089dcc --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookMime.d.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/126280 @mjbvz + + export interface NotebookCellData { + /** + * Mime type determines how the cell's `value` is interpreted. + * + * The mime selects which notebook renders is used to render the cell. + * + * If not set, internally the cell is treated as having a mime type of `text/plain`. + * Cells that set `language` to `markdown` instead are treated as `text/markdown`. + */ + mime?: string; + } + + export interface NotebookCell { + /** + * Mime type determines how the markup cell's `value` is interpreted. + * + * The mime selects which notebook renders is used to render the cell. + * + * If not set, internally the cell is treated as having a mime type of `text/plain`. + * Cells that set `language` to `markdown` instead are treated as `text/markdown`. + */ + mime: string | undefined; + } +} diff --git a/src/vscode-dts/vscode.proposed.portsAttributes.d.ts b/src/vscode-dts/vscode.proposed.portsAttributes.d.ts new file mode 100644 index 00000000000..1842605a5e7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.portsAttributes.d.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/115616 @alexr00 + + export enum PortAutoForwardAction { + Notify = 1, + OpenBrowser = 2, + OpenPreview = 3, + Silent = 4, + Ignore = 5, + OpenBrowserOnce = 6 + } + + export class PortAttributes { + /** + * The port number associated with this this set of attributes. + */ + port: number; + + /** + * The action to be taken when this port is detected for auto forwarding. + */ + autoForwardAction: PortAutoForwardAction; + + /** + * Creates a new PortAttributes object + * @param port the port number + * @param autoForwardAction the action to take when this port is detected + */ + constructor(port: number, autoForwardAction: PortAutoForwardAction); + } + + export interface PortAttributesProvider { + /** + * Provides attributes for the given port. For ports that your extension doesn't know about, simply + * return undefined. For example, if `providePortAttributes` is called with ports 3000 but your + * extension doesn't know anything about 3000 you should return undefined. + */ + providePortAttributes(port: number, pid: number | undefined, commandLine: string | undefined, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * If your extension listens on ports, consider registering a PortAttributesProvider to provide information + * about the ports. For example, a debug extension may know about debug ports in it's debuggee. By providing + * this information with a PortAttributesProvider the extension can tell the editor that these ports should be + * ignored, since they don't need to be user facing. + * + * @param portSelector If registerPortAttributesProvider is called after you start your process then you may already + * know the range of ports or the pid of your process. All properties of a the portSelector must be true for your + * provider to get called. + * The `portRange` is start inclusive and end exclusive. + * @param provider The PortAttributesProvider + */ + export function registerPortAttributesProvider(portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: PortAttributesProvider): Disposable; + } +} diff --git a/extensions/vscode-api-tests/src/typings/ref.d.ts b/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts similarity index 54% rename from extensions/vscode-api-tests/src/typings/ref.d.ts rename to src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts index e3e47385d66..405d67671d7 100644 --- a/extensions/vscode-api-tests/src/typings/ref.d.ts +++ b/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts @@ -3,6 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// -/// +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/73904 + + export interface QuickPick extends QuickInput { + /** + * An optional flag to sort the final results by index of first query match in label. Defaults to true. + */ + sortByLabel: boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.resolvers.d.ts b/src/vscode-dts/vscode.proposed.resolvers.d.ts new file mode 100644 index 00000000000..9847b67bac3 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.resolvers.d.ts @@ -0,0 +1,223 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + //resolvers: @alexdima + + export interface MessageOptions { + /** + * Do not render a native message box. + */ + useCustom?: boolean; + } + + export interface RemoteAuthorityResolverContext { + resolveAttempt: number; + } + + export class ResolvedAuthority { + readonly host: string; + readonly port: number; + readonly connectionToken: string | undefined; + + constructor(host: string, port: number, connectionToken?: string); + } + + export interface ResolvedOptions { + extensionHostEnv?: { [key: string]: string | null; }; + + isTrusted?: boolean; + } + + export interface TunnelPrivacy { + themeIcon: string; + id: string; + label: string; + } + + export interface TunnelOptions { + remoteAddress: { port: number, host: string; }; + // The desired local port. If this port can't be used, then another will be chosen. + localAddressPort?: number; + label?: string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; + privacy?: string; + protocol?: string; + } + + export interface TunnelDescription { + remoteAddress: { port: number, host: string; }; + //The complete local address(ex. localhost:1234) + localAddress: { port: number, host: string; } | string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; + privacy?: string; + // If protocol is not provided it is assumed to be http, regardless of the localAddress. + protocol?: string; + } + + export interface Tunnel extends TunnelDescription { + // Implementers of Tunnel should fire onDidDispose when dispose is called. + onDidDispose: Event; + dispose(): void | Thenable; + } + + /** + * Used as part of the ResolverResult if the extension has any candidate, + * published, or forwarded ports. + */ + export interface TunnelInformation { + /** + * Tunnels that are detected by the extension. The remotePort is used for display purposes. + * The localAddress should be the complete local address (ex. localhost:1234) for connecting to the port. Tunnels provided through + * detected are read-only from the forwarded ports UI. + */ + environmentTunnels?: TunnelDescription[]; + + } + + export interface TunnelCreationOptions { + /** + * True when the local operating system will require elevation to use the requested local port. + */ + elevationRequired?: boolean; + } + + export enum CandidatePortSource { + None = 0, + Process = 1, + Output = 2 + } + + export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; + + export class RemoteAuthorityResolverError extends Error { + static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError; + static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError; + + constructor(message?: string); + } + + export interface RemoteAuthorityResolver { + /** + * Resolve the authority part of the current opened `vscode-remote://` URI. + * + * This method will be invoked once during the startup of the editor and again each time + * the editor detects a disconnection. + * + * @param authority The authority part of the current opened `vscode-remote://` URI. + * @param context A context indicating if this is the first call or a subsequent call. + */ + resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable; + + /** + * Get the canonical URI (if applicable) for a `vscode-remote://` URI. + * + * @returns The canonical URI or undefined if the uri is already canonical. + */ + getCanonicalURI?(uri: Uri): ProviderResult; + + /** + * Can be optionally implemented if the extension can forward ports better than the core. + * When not implemented, the core will use its default forwarding logic. + * When implemented, the core will use this to forward ports. + * + * To enable the "Change Local Port" action on forwarded ports, make sure to set the `localAddress` of + * the returned `Tunnel` to a `{ port: number, host: string; }` and not a string. + */ + tunnelFactory?: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined; + + /**p + * Provides filtering for candidate ports. + */ + showCandidatePort?: (host: string, port: number, detail: string) => Thenable; + + /** + * Lets the resolver declare which tunnel factory features it supports. + * UNDER DISCUSSION! MAY CHANGE SOON. + */ + tunnelFeatures?: { + elevation: boolean; + /** + * @deprecated Use privacy instead + */ + public: boolean; + /** + * One of the the options must have the ID "private". + */ + privacyOptions: TunnelPrivacy[]; + }; + + candidatePortSource?: CandidatePortSource; + } + + export namespace workspace { + /** + * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. + * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips. + * + * @throws When run in an environment without a remote. + * + * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. + */ + export function openTunnel(tunnelOptions: TunnelOptions): Thenable; + + /** + * Gets an array of the currently available tunnels. This does not include environment tunnels, only tunnels that have been created by the user. + * Note that these are of type TunnelDescription and cannot be disposed. + */ + export let tunnels: Thenable; + + /** + * Fired when the list of tunnels has changed. + */ + export const onDidChangeTunnels: Event; + } + + export interface ResourceLabelFormatter { + scheme: string; + authority?: string; + formatting: ResourceLabelFormatting; + } + + export interface ResourceLabelFormatting { + label: string; // myLabel:/${path} + // For historic reasons we use an or string here. Once we finalize this API we should start using enums instead and adopt it in extensions. + // eslint-disable-next-line vscode-dts-literal-or-types + separator: '/' | '\\' | ''; + tildify?: boolean; + normalizeDriveLetter?: boolean; + workspaceSuffix?: string; + workspaceTooltip?: string; + authorityPrefix?: string; + stripPathStartingSeparator?: boolean; + } + + export namespace workspace { + export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; + export function registerResourceLabelFormatter(formatter: ResourceLabelFormatter): Disposable; + } + + export namespace env { + + /** + * The authority part of the current opened `vscode-remote://` URI. + * Defined by extensions, e.g. `ssh-remote+${host}` for remotes using a secure shell. + * + * *Note* that the value is `undefined` when there is no remote extension host but that the + * value is defined in all extension hosts (local and remote) in case a remote extension host + * exists. Use {@link Extension.extensionKind} to know if + * a specific extension runs remote or not. + */ + export const remoteAuthority: string | undefined; + + } +} diff --git a/extensions/debug-auto-launch/src/typings/ref.d.ts b/src/vscode-dts/vscode.proposed.scmActionButton.d.ts similarity index 70% rename from extensions/debug-auto-launch/src/typings/ref.d.ts rename to src/vscode-dts/vscode.proposed.scmActionButton.d.ts index bc057c55878..f60dc3047c7 100644 --- a/extensions/debug-auto-launch/src/typings/ref.d.ts +++ b/src/vscode-dts/vscode.proposed.scmActionButton.d.ts @@ -3,5 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// +declare module 'vscode' { + // https://github.com/microsoft/vscode/issues/133935 + + export interface SourceControl { + actionButton?: Command; + } +} diff --git a/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts b/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts new file mode 100644 index 00000000000..bced264e5a5 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // todo@joaomoreno add issue reference + + export interface SourceControl { + + /** + * Whether the source control is selected. + */ + readonly selected: boolean; + + /** + * An event signaling when the selection state changes. + */ + readonly onDidChangeSelection: Event; + } +} diff --git a/src/vscode-dts/vscode.proposed.scmValidation.d.ts b/src/vscode-dts/vscode.proposed.scmValidation.d.ts new file mode 100644 index 00000000000..a5fad56ebc2 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.scmValidation.d.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // todo@joaomoreno add issue reference + + /** + * Represents the validation type of the Source Control input. + */ + export enum SourceControlInputBoxValidationType { + + /** + * Something not allowed by the rules of a language or other means. + */ + Error = 0, + + /** + * Something suspicious but allowed. + */ + Warning = 1, + + /** + * Something to inform about but not a problem. + */ + Information = 2 + } + + export interface SourceControlInputBoxValidation { + + /** + * The validation message to display. + */ + readonly message: string | MarkdownString; + + /** + * The validation type. + */ + readonly type: SourceControlInputBoxValidationType; + } + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Shows a transient contextual message on the input. + */ + showValidationMessage(message: string | MarkdownString, type: SourceControlInputBoxValidationType): void; + + /** + * A validation function for the input box. It's possible to change + * the validation provider simply by setting this property to a different function. + */ + validateInput?(value: string, cursorPosition: number): ProviderResult; + } +} diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts new file mode 100644 index 00000000000..691cfd8f07c --- /dev/null +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/Microsoft/vscode/issues/15178 + + /** + * Represents a tab within the window + */ + export interface Tab { + /** + * The text displayed on the tab + */ + readonly label: string; + + /** + * The index of the tab within the column + */ + readonly index: number; + + /** + * The column which the tab belongs to + */ + readonly viewColumn: ViewColumn; + + /** + * The resource represented by the tab if available. + * Note: Not all tabs have a resource associated with them. + */ + readonly resource: Uri | undefined; + + /** + * The identifier of the view contained in the tab + * This is equivalent to `viewType` for custom editors and `notebookType` for notebooks. + * The built-in text editor has an id of 'default' for all configurations. + */ + readonly viewId: string | undefined; + + /** + * All the resources and viewIds represented by a tab + * {@link Tab.resource resource} and {@link Tab.viewId viewId} will + * always be at index 0. + */ + readonly additionalResourcesAndViewIds: readonly { + readonly resource: Uri | undefined, + readonly viewId: string | undefined + }[]; + + /** + * Whether or not the tab is currently active + * Dictated by being the selected tab in the active group + */ + readonly isActive: boolean; + + /** + * Moves a tab to the given index within the column. + * If the index is out of range, the tab will be moved to the end of the column. + * If the column is out of range, a new one will be created after the last existing column. + * @param index The index to move the tab to + * @param viewColumn The column to move the tab into + */ + move(index: number, viewColumn: ViewColumn): Thenable; + + /** + * Closes the tab. This makes the tab object invalid and the tab + * should no longer be used for further actions. + */ + close(): Thenable; + } + + export namespace window { + /** + * A list of all opened tabs + * Ordered from left to right + */ + export const tabs: readonly Tab[]; + + /** + * The currently active tab + * Undefined if no tabs are currently opened + */ + export const activeTab: Tab | undefined; + + /** + * An {@link Event} which fires when the array of {@link window.tabs tabs} + * has changed. + */ + export const onDidChangeTabs: Event; + + /** + * An {@link Event} which fires when the {@link window.activeTab activeTab} + * has changed. + */ + export const onDidChangeActiveTab: Event; + + } +} diff --git a/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts b/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts new file mode 100644 index 00000000000..52e631a0495 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/47265 + + export interface TaskPresentationOptions { + /** + * Controls whether the task is executed in a specific terminal group using split panes. + */ + group?: string; + + /** + * Controls whether the terminal is closed after executing the task. + */ + close?: boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts new file mode 100644 index 00000000000..c9c4c0e99af --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/78502 + + export interface TerminalDataWriteEvent { + /** + * The {@link Terminal} for which the data was written. + */ + readonly terminal: Terminal; + /** + * The data being written. + */ + readonly data: string; + } + + namespace window { + /** + * An event which fires when the terminal's child pseudo-device is written to (the shell). + * In other words, this provides access to the raw data stream from the process running + * within the terminal, including VT sequences. + */ + export const onDidWriteTerminalData: Event; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts b/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts new file mode 100644 index 00000000000..a0d28379d60 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/55718 + + /** + * An {@link Event} which fires when a {@link Terminal}'s dimensions change. + */ + export interface TerminalDimensionsChangeEvent { + /** + * The {@link Terminal} for which the dimensions have changed. + */ + readonly terminal: Terminal; + /** + * The new value for the {@link Terminal.dimensions terminal's dimensions}. + */ + readonly dimensions: TerminalDimensions; + } + + export namespace window { + /** + * An event which fires when the {@link Terminal.dimensions dimensions} of the terminal change. + */ + export const onDidChangeTerminalDimensions: Event; + } + + export interface Terminal { + /** + * The current dimensions of the terminal. This will be `undefined` immediately after the + * terminal is created as the dimensions are not known until shortly after the terminal is + * created. + */ + readonly dimensions: TerminalDimensions | undefined; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalLocation.d.ts b/src/vscode-dts/vscode.proposed.terminalLocation.d.ts new file mode 100644 index 00000000000..9c2a10a3feb --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalLocation.d.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/45407 + + export interface TerminalOptions { + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + } + + export interface ExtensionTerminalOptions { + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + } + + export enum TerminalLocation { + Panel = 1, + Editor = 2, + } + + export interface TerminalEditorLocationOptions { + /** + * A view column in which the {@link Terminal terminal} should be shown in the editor area. + * Use {@link ViewColumn.Active active} to open in the active editor group, other values are + * adjusted to be `Min(column, columnCount + 1)`, the + * {@link ViewColumn.Active active}-column is not adjusted. Use + * {@linkcode ViewColumn.Beside} to open the editor to the side of the currently active one. + */ + viewColumn: ViewColumn; + /** + * An optional flag that when `true` will stop the {@link Terminal} from taking focus. + */ + preserveFocus?: boolean; + } + + export interface TerminalSplitLocationOptions { + /** + * The parent terminal to split this terminal beside. This works whether the parent terminal + * is in the panel or the editor area. + */ + parentTerminal: Terminal; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts new file mode 100644 index 00000000000..e72aeca9b82 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // todo@API,@jrieken is this needed? This is also in vscode.d.ts... + // https://github.com/microsoft/vscode/issues/114898 + + export interface Pseudoterminal { + /** + * An event that when fired allows changing the name of the terminal. + * + * **Example:** Change the terminal name to "My new terminal". + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const changeNameEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * onDidChangeName: changeNameEmitter.event, + * open: () => changeNameEmitter.fire('My new terminal'), + * close: () => {} + * }; + * vscode.window.createTerminal({ name: 'My terminal', pty }); + * ``` + */ + onDidChangeName?: Event; + } +} diff --git a/src/vscode-dts/vscode.proposed.testCoverage.d.ts b/src/vscode-dts/vscode.proposed.testCoverage.d.ts new file mode 100644 index 00000000000..3f0ec8f1d67 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.testCoverage.d.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/123713 + + export interface TestRun { + /** + * Test coverage provider for this result. An extension can defer setting + * this until after a run is complete and coverage is available. + */ + coverageProvider?: TestCoverageProvider + // ... + } + + /** + * Provides information about test coverage for a test result. + * Methods on the provider will not be called until the test run is complete + */ + export interface TestCoverageProvider { + /** + * Returns coverage information for all files involved in the test run. + * @param token A cancellation token. + * @return Coverage metadata for all files involved in the test. + */ + provideFileCoverage(token: CancellationToken): ProviderResult; + + /** + * Give a FileCoverage to fill in more data, namely {@link FileCoverage.detailedCoverage}. + * The editor will only resolve a FileCoverage once, and onyl if detailedCoverage + * is undefined. + * + * @param coverage A coverage object obtained from {@link provideFileCoverage} + * @param token A cancellation token. + * @return The resolved file coverage, or a thenable that resolves to one. It + * is OK to return the given `coverage`. When no result is returned, the + * given `coverage` will be used. + */ + resolveFileCoverage?(coverage: T, token: CancellationToken): ProviderResult; + } + + /** + * A class that contains information about a covered resource. A count can + * be give for lines, branches, and functions in a file. + */ + export class CoveredCount { + /** + * Number of items covered in the file. + */ + covered: number; + /** + * Total number of covered items in the file. + */ + total: number; + + /** + * @param covered Value for {@link CovereredCount.covered} + * @param total Value for {@link CovereredCount.total} + */ + constructor(covered: number, total: number); + } + + /** + * Contains coverage metadata for a file. + */ + export class FileCoverage { + /** + * File URI. + */ + readonly uri: Uri; + + /** + * Statement coverage information. If the reporter does not provide statement + * coverage information, this can instead be used to represent line coverage. + */ + statementCoverage: CoveredCount; + + /** + * Branch coverage information. + */ + branchCoverage?: CoveredCount; + + /** + * Function coverage information. + */ + functionCoverage?: CoveredCount; + + /** + * Detailed, per-statement coverage. If this is undefined, the editor will + * call {@link TestCoverageProvider.resolveFileCoverage} when necessary. + */ + detailedCoverage?: DetailedCoverage[]; + + /** + * Creates a {@link FileCoverage} instance with counts filled in from + * the coverage details. + * @param uri Covered file URI + * @param detailed Detailed coverage information + */ + static fromDetails(uri: Uri, details: readonly DetailedCoverage[]): FileCoverage; + + /** + * @param uri Covered file URI + * @param statementCoverage Statement coverage information. If the reporter + * does not provide statement coverage information, this can instead be + * used to represent line coverage. + * @param branchCoverage Branch coverage information + * @param functionCoverage Function coverage information + */ + constructor( + uri: Uri, + statementCoverage: CoveredCount, + branchCoverage?: CoveredCount, + functionCoverage?: CoveredCount, + ); + } + + /** + * Contains coverage information for a single statement or line. + */ + export class StatementCoverage { + /** + * The number of times this statement was executed. If zero, the + * statement will be marked as un-covered. + */ + executionCount: number; + + /** + * Statement location. + */ + location: Position | Range; + + /** + * Coverage from branches of this line or statement. If it's not a + * conditional, this will be empty. + */ + branches: BranchCoverage[]; + + /** + * @param location The statement position. + * @param executionCount The number of times this statement was + * executed. If zero, the statement will be marked as un-covered. + * @param branches Coverage from branches of this line. If it's not a + * conditional, this should be omitted. + */ + constructor(executionCount: number, location: Position | Range, branches?: BranchCoverage[]); + } + + /** + * Contains coverage information for a branch of a {@link StatementCoverage}. + */ + export class BranchCoverage { + /** + * The number of times this branch was executed. If zero, the + * branch will be marked as un-covered. + */ + executionCount: number; + + /** + * Branch location. + */ + location?: Position | Range; + + /** + * @param executionCount The number of times this branch was executed. + * @param location The branch position. + */ + constructor(executionCount: number, location?: Position | Range); + } + + /** + * Contains coverage information for a function or method. + */ + export class FunctionCoverage { + /** + * The number of times this function was executed. If zero, the + * function will be marked as un-covered. + */ + executionCount: number; + + /** + * Function location. + */ + location: Position | Range; + + /** + * @param executionCount The number of times this function was executed. + * @param location The function position. + */ + constructor(executionCount: number, location: Position | Range); + } + + export type DetailedCoverage = StatementCoverage | FunctionCoverage; + +} diff --git a/src/vscode-dts/vscode.proposed.testObserver.d.ts b/src/vscode-dts/vscode.proposed.testObserver.d.ts new file mode 100644 index 00000000000..2bdb21d7473 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.testObserver.d.ts @@ -0,0 +1,202 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/107467 + + export namespace tests { + /** + * Requests that tests be run by their controller. + * @param run Run options to use. + * @param token Cancellation token for the test run + */ + export function runTests(run: TestRunRequest, token?: CancellationToken): Thenable; + + /** + * Returns an observer that watches and can request tests. + */ + export function createTestObserver(): TestObserver; + /** + * List of test results stored by the editor, sorted in descending + * order by their `completedAt` time. + */ + export const testResults: ReadonlyArray; + + /** + * Event that fires when the {@link testResults} array is updated. + */ + export const onDidChangeTestResults: Event; + } + + export interface TestObserver { + /** + * List of tests returned by test provider for files in the workspace. + */ + readonly tests: ReadonlyArray; + + /** + * An event that fires when an existing test in the collection changes, or + * null if a top-level test was added or removed. When fired, the consumer + * should check the test item and all its children for changes. + */ + readonly onDidChangeTest: Event; + + /** + * Dispose of the observer, allowing the editor to eventually tell test + * providers that they no longer need to update tests. + */ + dispose(): void; + } + + export interface TestsChangeEvent { + /** + * List of all tests that are newly added. + */ + readonly added: ReadonlyArray; + + /** + * List of existing tests that have updated. + */ + readonly updated: ReadonlyArray; + + /** + * List of existing tests that have been removed. + */ + readonly removed: ReadonlyArray; + } + + /** + * A test item is an item shown in the "test explorer" view. It encompasses + * both a suite and a test, since they have almost or identical capabilities. + */ + export interface TestItem { + /** + * Marks the test as outdated. This can happen as a result of file changes, + * for example. In "auto run" mode, tests that are outdated will be + * automatically rerun after a short delay. Invoking this on a + * test with children will mark the entire subtree as outdated. + * + * Extensions should generally not override this method. + */ + // todo@api still unsure about this + invalidateResults(): void; + } + + + /** + * TestResults can be provided to the editor in {@link tests.publishTestResult}, + * or read from it in {@link tests.testResults}. + * + * The results contain a 'snapshot' of the tests at the point when the test + * run is complete. Therefore, information such as its {@link Range} may be + * out of date. If the test still exists in the workspace, consumers can use + * its `id` to correlate the result instance with the living test. + */ + export interface TestRunResult { + /** + * Unix milliseconds timestamp at which the test run was completed. + */ + readonly completedAt: number; + + /** + * Optional raw output from the test run. + */ + readonly output?: string; + + /** + * List of test results. The items in this array are the items that + * were passed in the {@link tests.runTests} method. + */ + readonly results: ReadonlyArray>; + } + + /** + * A {@link TestItem}-like interface with an associated result, which appear + * or can be provided in {@link TestResult} interfaces. + */ + export interface TestResultSnapshot { + /** + * Unique identifier that matches that of the associated TestItem. + * This is used to correlate test results and tests in the document with + * those in the workspace (test explorer). + */ + readonly id: string; + + /** + * Parent of this item. + */ + readonly parent?: TestResultSnapshot; + + /** + * URI this TestItem is associated with. May be a file or file. + */ + readonly uri?: Uri; + + /** + * Display name describing the test case. + */ + readonly label: string; + + /** + * Optional description that appears next to the label. + */ + readonly description?: string; + + /** + * Location of the test item in its `uri`. This is only meaningful if the + * `uri` points to a file. + */ + readonly range?: Range; + + /** + * State of the test in each task. In the common case, a test will only + * be executed in a single task and the length of this array will be 1. + */ + readonly taskStates: ReadonlyArray; + + /** + * Optional list of nested tests for this item. + */ + readonly children: Readonly[]; + } + + export interface TestSnapshotTaskState { + /** + * Current result of the test. + */ + readonly state: TestResultState; + + /** + * The number of milliseconds the test took to run. This is set once the + * `state` is `Passed`, `Failed`, or `Errored`. + */ + readonly duration?: number; + + /** + * Associated test run message. Can, for example, contain assertion + * failure information if the test fails. + */ + readonly messages: ReadonlyArray; + } + + /** + * Possible states of tests in a test run. + */ + export enum TestResultState { + // Test will be run, but is not currently running. + Queued = 1, + // Test is currently running + Running = 2, + // Test run has passed + Passed = 3, + // Test run has failed (on an assertion) + Failed = 4, + // Test run has been skipped + Skipped = 5, + // Test run failed for some other reason (compilation error, timeout, etc) + Errored = 6 + } +} diff --git a/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts b/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts new file mode 100644 index 00000000000..0b7e1f7b391 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/102091 + + export interface TextDocument { + + /** + * The {@link NotebookDocument notebook} that contains this document as a notebook cell or `undefined` when + * the document is not contained by a notebook (this should be the more frequent case). + */ + notebook: NotebookDocument | undefined; + } +} diff --git a/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts b/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts new file mode 100644 index 00000000000..9eba555d007 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts @@ -0,0 +1,275 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/59921 + + /** + * The parameters of a query for text search. + */ + export interface TextSearchQuery { + /** + * The text pattern to search for. + */ + pattern: string; + + /** + * Whether or not `pattern` should match multiple lines of text. + */ + isMultiline?: boolean; + + /** + * Whether or not `pattern` should be interpreted as a regular expression. + */ + isRegExp?: boolean; + + /** + * Whether or not the search should be case-sensitive. + */ + isCaseSensitive?: boolean; + + /** + * Whether or not to search for whole word matches only. + */ + isWordMatch?: boolean; + } + + /** + * A file glob pattern to match file paths against. + * TODO@roblourens merge this with the GlobPattern docs/definition in vscode.d.ts. + * @see {@link GlobPattern} + */ + export type GlobString = string; + + /** + * Options common to file and text search + */ + export interface SearchOptions { + /** + * The root folder to search within. + */ + folder: Uri; + + /** + * Files that match an `includes` glob pattern should be included in the search. + */ + includes: GlobString[]; + + /** + * Files that match an `excludes` glob pattern should be excluded from the search. + */ + excludes: GlobString[]; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles: boolean; + } + + /** + * Options to specify the size of the result text preview. + * These options don't affect the size of the match itself, just the amount of preview text. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + * Only search providers that support multiline search will ever return more than one line in the match. + */ + matchLines: number; + + /** + * The maximum number of characters included per line. + */ + charsPerLine: number; + } + + /** + * Options that apply to text search. + */ + export interface TextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + /** + * Represents the severiry of a TextSearchComplete message. + */ + export enum TextSearchCompleteMessageType { + Information = 1, + Warning = 2, + } + + /** + * A message regarding a completed search. + */ + export interface TextSearchCompleteMessage { + /** + * Markdown text of the message. + */ + text: string, + /** + * Whether the source of the message is trusted, command links are disabled for untrusted message sources. + * Messaged are untrusted by default. + */ + trusted?: boolean, + /** + * The message type, this affects how the message will be rendered. + */ + type: TextSearchCompleteMessageType, + } + + /** + * Information collected when text search is complete. + */ + export interface TextSearchComplete { + /** + * Whether the search hit the limit on the maximum number of search results. + * `maxResults` on {@linkcode TextSearchOptions} specifies the max number of results. + * - If exactly that number of matches exist, this should be false. + * - If `maxResults` matches are returned and more exist, this should be true. + * - If search hits an internal limit which is less than `maxResults`, this should be true. + */ + limitHit?: boolean; + + /** + * Additional information regarding the state of the completed search. + * + * Messages with "Information" style support links in markdown syntax: + * - Click to [run a command](command:workbench.action.OpenQuickPick) + * - Click to [open a website](https://aka.ms) + * + * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. + */ + message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; + } + + /** + * A preview of the text result. + */ + export interface TextSearchMatchPreview { + /** + * The matching lines of text, or a portion of the matching line that contains the match. + */ + text: string; + + /** + * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. + */ + matches: Range | Range[]; + } + + /** + * A match from a text search + */ + export interface TextSearchMatch { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * The range of the match within the document, or multiple ranges for multiple matches. + */ + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + export type TextSearchResult = TextSearchMatch | TextSearchContext; + + /** + * A TextSearchProvider provides search results for text results inside files in the workspace. + */ + export interface TextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.timeline.d.ts b/src/vscode-dts/vscode.proposed.timeline.d.ts new file mode 100644 index 00000000000..625bc8c9bed --- /dev/null +++ b/src/vscode-dts/vscode.proposed.timeline.d.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/84297 + + export class TimelineItem { + /** + * A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred. + */ + timestamp: number; + + /** + * A human-readable string describing the timeline item. + */ + label: string; + + /** + * Optional id for the timeline item. It must be unique across all the timeline items provided by this source. + * + * If not provided, an id is generated using the timeline item's timestamp. + */ + id?: string; + + /** + * The icon path or {@link ThemeIcon} for the timeline item. + */ + iconPath?: Uri | { light: Uri; dark: Uri; } | ThemeIcon; + + /** + * A human readable string describing less prominent details of the timeline item. + */ + description?: string; + + /** + * The tooltip text when you hover over the timeline item. + */ + detail?: string; + + /** + * The {@link Command} that should be executed when the timeline item is selected. + */ + command?: Command; + + /** + * Context value of the timeline item. This can be used to contribute specific actions to the item. + * For example, a timeline item is given a context value as `commit`. When contributing actions to `timeline/item/context` + * using `menus` extension point, you can specify context value for key `timelineItem` in `when` expression like `timelineItem == commit`. + * ``` + * "contributes": { + * "menus": { + * "timeline/item/context": [ + * { + * "command": "extension.copyCommitId", + * "when": "timelineItem == commit" + * } + * ] + * } + * } + * ``` + * This will show the `extension.copyCommitId` action only for items where `contextValue` is `commit`. + */ + contextValue?: string; + + /** + * Accessibility information used when screen reader interacts with this timeline item. + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * @param label A human-readable string describing the timeline item + * @param timestamp A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred + */ + constructor(label: string, timestamp: number); + } + + export interface TimelineChangeEvent { + /** + * The {@link Uri} of the resource for which the timeline changed. + */ + uri: Uri; + + /** + * A flag which indicates whether the entire timeline should be reset. + */ + reset?: boolean; + } + + export interface Timeline { + readonly paging?: { + /** + * A provider-defined cursor specifying the starting point of timeline items which are after the ones returned. + * Use `undefined` to signal that there are no more items to be returned. + */ + readonly cursor: string | undefined; + }; + + /** + * An array of {@link TimelineItem timeline items}. + */ + readonly items: readonly TimelineItem[]; + } + + export interface TimelineOptions { + /** + * A provider-defined cursor specifying the starting point of the timeline items that should be returned. + */ + cursor?: string; + + /** + * An optional maximum number timeline items or the all timeline items newer (inclusive) than the timestamp or id that should be returned. + * If `undefined` all timeline items should be returned. + */ + limit?: number | { timestamp: number; id?: string; }; + } + + export interface TimelineProvider { + /** + * An optional event to signal that the timeline for a source has changed. + * To signal that the timeline for all resources (uris) has changed, do not pass any argument or pass `undefined`. + */ + onDidChange?: Event; + + /** + * An identifier of the source of the timeline items. This can be used to filter sources. + */ + readonly id: string; + + /** + * A human-readable string describing the source of the timeline items. This can be used as the display label when filtering sources. + */ + readonly label: string; + + /** + * Provide {@link TimelineItem timeline items} for a {@link Uri}. + * + * @param uri The {@link Uri} of the file to provide the timeline for. + * @param options A set of options to determine how results should be returned. + * @param token A cancellation token. + * @return The {@link TimelineResult timeline result} or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideTimeline(uri: Uri, options: TimelineOptions, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a timeline provider. + * + * Multiple providers can be registered. In that case, providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param scheme A scheme or schemes that defines which documents this provider is applicable to. Can be `*` to target all documents. + * @param provider A timeline provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTimelineProvider(scheme: string | string[], provider: TimelineProvider): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.tokenInformation.d.ts b/src/vscode-dts/vscode.proposed.tokenInformation.d.ts new file mode 100644 index 00000000000..538788818d1 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.tokenInformation.d.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/91555 + + export enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 4 + } + + export interface TokenInformation { + type: StandardTokenType; + range: Range; + } + + export namespace languages { + /** @deprecated */ + export function getTokenInformationAtPosition(document: TextDocument, position: Position): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts new file mode 100644 index 00000000000..70fd7dc8b5a --- /dev/null +++ b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/32592 + + /** + * A data provider that provides tree data + */ + export interface TreeDataProvider { + /** + * An optional event to signal that an element or root has changed. + * This will trigger the view to update the changed element/root and its children recursively (if shown). + * To signal that root has changed, do not pass any argument or pass `undefined` or `null`. + */ + onDidChangeTreeData2?: Event; + } + + export interface TreeViewOptions { + /** + * An optional interface to implement drag and drop in the tree view. + */ + dragAndDropController?: DragAndDropController; + } + + export interface TreeDataTransferItem { + asString(): Thenable; + } + + export interface TreeDataTransfer { + /** + * A map containing a mapping of the mime type of the corresponding data. + * The type for tree elements is text/treeitem. + * For example, you can reconstruct the your tree elements: + * ```ts + * JSON.parse(await (items.get('text/treeitem')!.asString())) + * ``` + */ + items: { get: (mimeType: string) => TreeDataTransferItem | undefined }; + } + + export interface DragAndDropController extends Disposable { + readonly supportedTypes: string[]; + + /** + * todo@API maybe + * + * When the user drops an item from this DragAndDropController on **another tree item** in **the same tree**, + * `onWillDrop` will be called with the dropped tree item. This is the DragAndDropController's opportunity to + * package the data from the dropped tree item into whatever format they want the target tree item to receive. + * + * The returned `TreeDataTransfer` will be merged with the original`TreeDataTransfer` for the operation. + * + * Note for implementation later: This means that the `text/treeItem` mime type will go away. + * + * @param source + */ + // onWillDrop?(source: T): Thenable; + + /** + * Extensions should fire `TreeDataProvider.onDidChangeTreeData` for any elements that need to be refreshed. + * + * @param source + * @param target + */ + onDrop(source: TreeDataTransfer, target: T): Thenable; + } +} diff --git a/extensions/vscode-notebook-tests/src/typings/ref.d.ts b/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts similarity index 56% rename from extensions/vscode-notebook-tests/src/typings/ref.d.ts rename to src/vscode-dts/vscode.proposed.treeViewReveal.d.ts index a17099ac50c..e71c2f71879 100644 --- a/extensions/vscode-notebook-tests/src/typings/ref.d.ts +++ b/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts @@ -3,7 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// -/// -/// +declare module 'vscode' { + // https://github.com/microsoft/vscode/issues/61313 @alexr00 + + export interface TreeView extends Disposable { + reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number; }): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts b/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts new file mode 100644 index 00000000000..d48071af2cc --- /dev/null +++ b/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/120173 + + /** + * The object describing the properties of the workspace trust request + */ + export interface WorkspaceTrustRequestOptions { + /** + * Custom message describing the user action that requires workspace + * trust. If omitted, a generic message will be displayed in the workspace + * trust request dialog. + */ + readonly message?: string; + } + + export namespace workspace { + /** + * Prompt the user to chose whether to trust the current workspace + * @param options Optional object describing the properties of the + * workspace trust request. + */ + export function requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Thenable; + } +} diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index aa67c834e0d..6f7a8a7e9ee 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -128,6 +128,7 @@ export async function spawn(options: SpawnOptions): Promise { const env = { ...process.env }; const codePath = options.codePath; + const logsPath = path.join(repoPath, '.build', 'logs', options.remote ? 'smoke-tests-remote' : 'smoke-tests'); const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath(); const args = [ @@ -142,7 +143,7 @@ export async function spawn(options: SpawnOptions): Promise { '--disable-workspace-trust', `--extensions-dir=${options.extensionsPath}`, `--user-data-dir=${options.userDataDir}`, - `--logsPath=${path.join(repoPath, '.build', 'logs', 'smoke-tests')}`, + `--logsPath=${logsPath}`, '--driver', handle ]; @@ -170,6 +171,7 @@ export async function spawn(options: SpawnOptions): Promise { } env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir; + env['TESTRESOLVER_LOGS_FOLDER'] = path.join(logsPath, 'server'); } const spawnOptions: cp.SpawnOptions = { env }; diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 54462bd7792..bae2e43cc51 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -163,7 +163,11 @@ if (!opts.web) { quality = Quality.Stable; } - console.log(`Running desktop smoke tests against ${electronPath}`); + if (opts.remote) { + console.log(`Running desktop remote smoke tests against ${electronPath}`); + } else { + console.log(`Running desktop smoke tests against ${electronPath}`); + } } // diff --git a/yarn.lock b/yarn.lock index e923691c537..06debad293e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -417,10 +417,10 @@ dependencies: "@octokit/openapi-types" "^10.2.2" -"@parcel/watcher@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.0.tgz#ebe992a4838b35c3da9a568eb95a71cb26ddf551" - integrity sha512-ByalKmRRXNNAhwZ0X1r0XeIhh1jG8zgdlvjgHk9ZV3YxiersEGNQkwew+RfqJbIL4gOJfvC2ey6lg5kaeRainw== +"@parcel/watcher@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.1.tgz#ec4bb6c43d9588a1ffd3d2abe6df5b501463c62d" + integrity sha512-XegFF4L8sFn1RzU5KKOZxXUuzgOSwd6+X2ez3Cy6MVhYMbiLZ1moceMTqDhuT3N8DNbdumK3zP1wojsIsnX40w== dependencies: node-addon-api "^3.2.1" node-gyp-build "^4.3.0"