diff --git a/build/gulpfile.vscode.web.mjs b/build/gulpfile.vscode.web.mjs index e976ed77a61bf7..f568f47efa09f7 100644 --- a/build/gulpfile.vscode.web.mjs +++ b/build/gulpfile.vscode.web.mjs @@ -88,6 +88,7 @@ const vscodeWebEntryPoints = [ buildfile.workerBackgroundTokenization, buildfile.keyboardMaps, buildfile.workbenchWeb, + buildfile.codeWeb, buildfile.entrypoint('vs/workbench/workbench.web.main.internal') // TODO@esm remove line when we stop supporting web-amd-esm-bridge ].flat(); diff --git a/build/hygiene.mjs b/build/hygiene.mjs index 3497cafdcc8ad7..9a54ece5be03ec 100644 --- a/build/hygiene.mjs +++ b/build/hygiene.mjs @@ -103,19 +103,20 @@ export function hygiene(some, runEslint = true) { this.emit('data', file); }); - const copyrights = es.through(function (file) { - const lines = file.__lines; + // MEMBRANE: + // const copyrights = es.through(function (file) { + // const lines = file.__lines; - for (let i = 0; i < copyrightHeaderLines.length; i++) { - if (lines[i] !== copyrightHeaderLines[i]) { - console.error(file.relative + ': Missing or bad copyright statement'); - errorCount++; - break; - } - } + // for (let i = 0; i < copyrightHeaderLines.length; i++) { + // if (lines[i] !== copyrightHeaderLines[i]) { + // console.error(file.relative + ': Missing or bad copyright statement'); + // errorCount++; + // break; + // } + // } - this.emit('data', file); - }); + // this.emit('data', file); + // }); const formatting = es.map(function (file, cb) { try { @@ -165,9 +166,10 @@ export function hygiene(some, runEslint = true) { .pipe(unicode) .pipe(unicodeFilterStream.restore) .pipe(filter(indentationFilter)) - .pipe(indentation) - .pipe(filter(copyrightFilter)) - .pipe(copyrights); + .pipe(indentation); + // MEMBRANE: + // .pipe(filter(copyrightFilter)) + // .pipe(copyrights); /** @type {import('stream').Stream[]} */ const streams = [ diff --git a/build/lib/extensions.js b/build/lib/extensions.js index e373688892474d..9e690d46a99e82 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -43,6 +43,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.fromMarketplace = fromMarketplace; exports.fromVsix = fromVsix; exports.fromGithub = fromGithub; +exports.isAllowedInMembrane = isAllowedInMembrane; exports.packageNonNativeLocalExtensionsStream = packageNonNativeLocalExtensionsStream; exports.packageNativeLocalExtensionsStream = packageNativeLocalExtensionsStream; exports.packageAllLocalExtensionsStream = packageAllLocalExtensionsStream; @@ -361,6 +362,45 @@ function isWebExtension(manifest) { } return true; } +const allowedExtensions = [ + 'configuration-editing', + 'css', + 'css-language-features', + 'diff', + 'emmet', + 'handlebars', + 'html', + 'html-language-features', + 'javascript', + 'json', + 'json-language-features', + 'log', + 'markdown', + 'markdown-basics', + 'markdown-language-features', + 'markdown-math', + 'media-preview', + 'merge-conflict', + 'microsoft-authentication', + 'npm', + 'php', + 'references-view', + 'scss', + 'search-result', + 'simple-browser', + 'sql', + 'theme-defaults', + 'theme-solarized-dark', + 'theme-solarized-light', + 'typescript', + 'typescript-basics', + 'typescript-language-features', + 'xml', + 'yaml', +]; +function isAllowedInMembrane(name) { + return allowedExtensions.some(allowedExtensionName => allowedExtensionName === name); +} /** * Package local extensions that are known to not have native dependencies. Mutually exclusive to {@link packageNativeLocalExtensionsStream}. * @param forWeb build the extensions that have web targets @@ -411,6 +451,7 @@ function doPackageLocalExtensionsStream(forWeb, disableMangle, native) { .filter(({ name }) => native ? nativeExtensionsSet.has(name) : !nativeExtensionsSet.has(name)) .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) + .filter(({ name }) => isAllowedInMembrane(name)) .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true))); const localExtensionsStream = minifyExtensionResources(event_stream_1.default.merge(...localExtensionsDescriptions.map(extension => { return fromLocal(extension.path, forWeb, disableMangle) @@ -465,6 +506,10 @@ function scanBuiltinExtensions(extensionsRoot, exclude = []) { if (!isWebExtension(packageJSON)) { continue; } + // MEMBRANE: only include the minimum set of extensions + if (!isAllowedInMembrane(packageJSON.name)) { + continue; + } const children = fs_1.default.readdirSync(path_1.default.join(extensionsRoot, extensionFolder)); const packageNLSPath = children.filter(child => child === 'package.nls.json')[0]; const packageNLS = packageNLSPath ? JSON.parse(fs_1.default.readFileSync(path_1.default.join(extensionsRoot, extensionFolder, packageNLSPath)).toString()) : undefined; diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 4779ddba03a306..e4ec1e7764d9ff 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -373,6 +373,46 @@ function isWebExtension(manifest: IExtensionManifest): boolean { return true; } +const allowedExtensions = [ + 'configuration-editing', + 'css', + 'css-language-features', + 'diff', + 'emmet', + 'handlebars', + 'html', + 'html-language-features', + 'javascript', + 'json', + 'json-language-features', + 'log', + 'markdown', + 'markdown-basics', + 'markdown-language-features', + 'markdown-math', + 'media-preview', + 'merge-conflict', + 'microsoft-authentication', + 'npm', + 'php', + 'references-view', + 'scss', + 'search-result', + 'simple-browser', + 'sql', + 'theme-defaults', + 'theme-solarized-dark', + 'theme-solarized-light', + 'typescript', + 'typescript-basics', + 'typescript-language-features', + 'xml', + 'yaml', +]; +export function isAllowedInMembrane(name: string): boolean { + return allowedExtensions.some(allowedExtensionName => allowedExtensionName === name); +} + /** * Package local extensions that are known to not have native dependencies. Mutually exclusive to {@link packageNativeLocalExtensionsStream}. * @param forWeb build the extensions that have web targets @@ -427,6 +467,7 @@ function doPackageLocalExtensionsStream(forWeb: boolean, disableMangle: boolean, .filter(({ name }) => native ? nativeExtensionsSet.has(name) : !nativeExtensionsSet.has(name)) .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) + .filter(({ name }) => isAllowedInMembrane(name)) .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true)) ); const localExtensionsStream = minifyExtensionResources( @@ -510,6 +551,11 @@ export function scanBuiltinExtensions(extensionsRoot: string, exclude: string[] if (!isWebExtension(packageJSON)) { continue; } + + // MEMBRANE: only include the minimum set of extensions + if (!isAllowedInMembrane(packageJSON.name)) { + continue; + } const children = fs.readdirSync(path.join(extensionsRoot, extensionFolder)); const packageNLSPath = children.filter(child => child === 'package.nls.json')[0]; const packageNLS = packageNLSPath ? JSON.parse(fs.readFileSync(path.join(extensionsRoot, extensionFolder, packageNLSPath)).toString()) : undefined; diff --git a/build/lib/git.js b/build/lib/git.js index 30de97ed6e3699..9d00ee5e970d7e 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -14,7 +14,24 @@ const fs_1 = __importDefault(require("fs")); * Returns the sha1 commit version of a repository or undefined in case of failure. */ function getVersion(repo) { - const git = path_1.default.join(repo, '.git'); + // MEMBRANE: make `getVersion` work when vscode is a submodule and it's .git was hoisted. + const maybeGit = path_1.default.join(repo, '.git'); + const stat = fs_1.default.statSync(maybeGit); + let git; + if (stat.isFile()) { + const data = fs_1.default.readFileSync(maybeGit, 'utf8'); + const gitdir = data.match(/^gitdir: (.*)$/m)?.[1]; + if (!gitdir) { + throw new Error(`Failed to parse .git submodule info in ${maybeGit}`); + } + git = path_1.default.join(repo, gitdir); + } + else if (stat.isDirectory()) { + git = maybeGit; + } + else { + return undefined; + } const headPath = path_1.default.join(git, 'HEAD'); let head; try { diff --git a/build/lib/git.ts b/build/lib/git.ts index a3c23d8c29b3bd..4be51f81e00dea 100644 --- a/build/lib/git.ts +++ b/build/lib/git.ts @@ -9,7 +9,23 @@ import fs from 'fs'; * Returns the sha1 commit version of a repository or undefined in case of failure. */ export function getVersion(repo: string): string | undefined { - const git = path.join(repo, '.git'); + // MEMBRANE: make `getVersion` work when vscode is a submodule and it's .git was hoisted. + const maybeGit = path.join(repo, '.git'); + const stat = fs.statSync(maybeGit); + let git: string; + if (stat.isFile()) { + const data = fs.readFileSync(maybeGit, 'utf8'); + const gitdir = data.match(/^gitdir: (.*)$/m)?.[1]; + if (!gitdir) { + throw new Error(`Failed to parse .git submodule info in ${maybeGit}`); + } + git = path.join(repo, gitdir); + } else if (stat.isDirectory()) { + git = maybeGit; + } else { + return undefined; + } + const headPath = path.join(git, 'HEAD'); let head: string; diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index fa8da7d08c69ea..5bb58caba1a120 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -185,5 +185,8 @@ for (let dir of dirs) { npmInstall(dir, opts); } -cp.execSync('git config pull.rebase merges'); -cp.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs'); +// This commands don't play well with being ran inside a submodule so just skip them in that context +if (!process.cwd().includes("mnode")) { + cp.execSync('git config pull.rebase merges'); + cp.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs'); +} diff --git a/eslint.config.js b/eslint.config.js index 9d83f9269e307c..fa6d56725c3bd9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2172,4 +2172,17 @@ export default tseslint.config( '@typescript-eslint/consistent-generic-constructors': ['warn', 'constructor'], } }, + // Membrane files: disable code-import-patterns to allow imports from platform/terminal/common + { + files: ['**/*.membrane.ts'], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'local/code-import-patterns': 'off' + } + }, ); diff --git a/extensions/package-lock.json b/extensions/package-lock.json index acaba35fbf0268..c14cce692f7b48 100644 --- a/extensions/package-lock.json +++ b/extensions/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "typescript": "^5.9.3" + "typescript": "npm:@membrane/typescript@5.3.2-4" }, "devDependencies": { "@parcel/watcher": "parcel-bundler/watcher#1ca032aa8339260a8a3bcf825c3a1a71e3e43542", @@ -18,78 +18,8 @@ "vscode-grammar-updater": "^1.1.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/darwin-arm64": { "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], @@ -103,350 +33,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.1", "resolved": "git+ssh://git@github.com/parcel-bundler/watcher.git#1ca032aa8339260a8a3bcf825c3a1a71e3e43542", - "integrity": "sha512-Z0lk8pM5vwuOJU6pfheRXHrOpQYIIEnVl/z8DY6370D4+ZnrOTvFa5BUdf3pGxahT5ILbPWwQSm2Wthy4q1OTg==", + "integrity": "sha512-/HDK5D9Oa1ymveUV3zxRcegbHO7XsVU+1jjnWecoFugJmORIY6csRhuN1oqJ0NKp0xJ7Y3nzCVCgsTtUhsp7qQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -466,9 +56,8 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -478,9 +67,8 @@ }, "node_modules/coffeescript": { "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz", - "integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA==", "dev": true, + "license": "MIT", "bin": { "cake": "bin/cake", "coffee": "bin/coffee" @@ -491,9 +79,8 @@ }, "node_modules/cson-parser": { "version": "4.0.9", - "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-4.0.9.tgz", - "integrity": "sha512-I79SAcCYquWnEfXYj8hBqOOWKj6eH6zX1hhX3yqmS4K3bYp7jME3UFpHPzu3rUew0oyfc0s8T6IlWGXRAheHag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "coffeescript": "1.12.7" }, @@ -503,8 +90,6 @@ }, "node_modules/detect-libc": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -513,8 +98,6 @@ }, "node_modules/esbuild": { "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -554,15 +137,13 @@ }, "node_modules/fast-plist": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/fast-plist/-/fast-plist-0.1.2.tgz", - "integrity": "sha1-pFr/NFGWAG1AbKbNzQX2kFHvNbg= sha512-2HxzrqJhmMoxVzARjYFvkzkL2dCBB8sogU5sD8gqcZWv5UCivK9/cXM9KIPDRwU+eD3mbRDN/GhW8bO/4dtMfg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -572,18 +153,16 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -593,17 +172,14 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -616,8 +192,6 @@ }, "node_modules/node-addon-api": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", - "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", "dev": true, "license": "MIT", "engines": { @@ -626,9 +200,8 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -638,9 +211,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -649,9 +221,10 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "name": "@membrane/typescript", + "version": "5.3.2-4", + "resolved": "https://registry.npmjs.org/@membrane/typescript/-/typescript-5.3.2-4.tgz", + "integrity": "sha512-J/Jy6xbTsn/BFMjbG2Pt06888HhXkAgcK8jY7SEbC8AypTmjNMlV7LpGFKgYEGOd4jUtPYbEisehbcu2D34NmQ==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -663,9 +236,8 @@ }, "node_modules/vscode-grammar-updater": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vscode-grammar-updater/-/vscode-grammar-updater-1.1.0.tgz", - "integrity": "sha512-rWcJXyEFK27Mh9bxfBTLaul0KiGQk0GMXj2qTDH9cy3UZVx5MrF035B03os1w4oIXwl/QDhdLnsBK0j2SNiL1A==", "dev": true, + "license": "MIT", "dependencies": { "cson-parser": "^4.0.9", "fast-plist": "0.1.2" diff --git a/extensions/package.json b/extensions/package.json index 7b1d8defa3e500..599dfe3751125f 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "^5.9.3" + "typescript": "npm:@membrane/typescript@5.3.2-4" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index f54499dc2272cd..357c380f4dc94d 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -19,6 +19,7 @@ const tsLoaderOptions = { 'sourceMap': true, }, onlyCompileBundledFiles: true, + transpileOnly: true, // Skip type checking to avoid TypeScript version incompatibilities }; function withNodeDefaults(/**@type WebpackConfig & { context: string }*/extConfig) { @@ -108,13 +109,23 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo const defaultConfig = { mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') target: 'webworker', // extensions run in a webworker context + node: false, // Disable Node.js polyfills - we handle them via fallback resolve: { + alias: { + // MEMBRANE: ts-plugin is bundled inside typescript-language-features since vscode web doesn't support the + // normal typescriptServerPlugins extension setting. + './platform/vscode': path.resolve(import.meta.dirname, '../../ts-plugin/src/platform/browser.ts'), + // Ensure 'events' module is resolved correctly for browser builds + 'events': require.resolve('events'), + }, mainFields: ['browser', 'module', 'main'], extensions: ['.ts', '.js'], // support ts-files and js-files fallback: { - 'path': require.resolve('path-browserify'), 'os': require.resolve('os-browserify'), - 'util': require.resolve('util') + 'events': require.resolve('events/'), + // 'os': require.resolve('os-browserify'), + 'path': require.resolve('path-browserify'), + 'util': require.resolve('util/') }, extensionAlias: { // this is needed to resolve dynamic imports that now require the .js extension @@ -122,30 +133,36 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo }, }, module: { - rules: [{ - test: /\.ts$/, - exclude: /node_modules/, - use: [ - { - // configure TypeScript loader: - // * enable sources maps for end-to-end source maps - loader: 'ts-loader', - options: { - ...tsLoaderOptions, - // ...(additionalOptions ? {} : { configFile: additionalOptions.configFile }), - } - }, - { - loader: path.resolve(import.meta.dirname, 'mangle-loader.js'), - options: { - configFile: path.join(extConfig.context, additionalOptions?.configFile ?? 'tsconfig.json') + rules: [ + // MEMBRANE: see ts-plugin/src/membraneLib.ts + { + test: /\.d\.ts$/, + type: 'asset/source' + }, + { + test: /\.ts$/, + exclude: /node_modules|typings\/.*\.d\.ts$/, // Exclude node_modules and .d.ts files in the typings folder + use: [ + { + // configure TypeScript loader: + // * enable sources maps for end-to-end source maps + loader: 'ts-loader', + options: { + ...tsLoaderOptions, + ...(additionalOptions?.configFile ? { configFile: additionalOptions.configFile } : {}), + } }, - }, - ] - }, { - test: /\.wasm$/, - type: 'asset/inline' - }] + { + loader: path.resolve(import.meta.dirname, 'mangle-loader.js'), + options: { + configFile: path.join(extConfig.context, additionalOptions?.configFile ?? 'tsconfig.json') + }, + }, + ] + }, { + test: /\.wasm$/, + type: 'asset/inline' + }] }, externals: { 'vscode': 'commonjs vscode', // ignored because it doesn't exist, @@ -196,7 +213,15 @@ function browserPlugins(context) { 'process.platform': JSON.stringify('web'), 'process.env': JSON.stringify({}), 'process.env.BROWSER_ENV': JSON.stringify('true') - }) + }), + new webpack.ProvidePlugin({ + 'events': require.resolve('events') + }), + // Ensure events module is bundled + new webpack.NormalModuleReplacementPlugin( + /^events$/, + require.resolve('events') + ) ]; } diff --git a/extensions/theme-defaults/package.json b/extensions/theme-defaults/package.json index 2458ef317da70c..a151317462dd9c 100644 --- a/extensions/theme-defaults/package.json +++ b/extensions/theme-defaults/package.json @@ -37,6 +37,18 @@ "uiTheme": "vs", "path": "./themes/light_modern.json" }, + { + "id": "Membrane Light", + "label": "%membraneLightThemeLabel%", + "uiTheme": "vs", + "path": "./themes/membrane_light.json" + }, + { + "id": "Membrane Dark", + "label": "%membraneDarkThemeLabel%", + "uiTheme": "vs-dark", + "path": "./themes/membrane_dark.json" + }, { "id": "Visual Studio Dark", "label": "%darkColorThemeLabel%", diff --git a/extensions/theme-defaults/package.nls.json b/extensions/theme-defaults/package.nls.json index cacbd6b8d9a87e..bf532ba44d80de 100644 --- a/extensions/theme-defaults/package.nls.json +++ b/extensions/theme-defaults/package.nls.json @@ -5,6 +5,8 @@ "darkModernThemeLabel": "Dark Modern", "lightPlusColorThemeLabel": "Light+", "lightModernThemeLabel": "Light Modern", + "membraneLightThemeLabel": "Membrane Light", + "membraneDarkThemeLabel": "Membrane Dark", "darkColorThemeLabel": "Dark (Visual Studio)", "lightColorThemeLabel": "Light (Visual Studio)", "hcColorThemeLabel": "Dark High Contrast", diff --git a/extensions/theme-defaults/themes/membrane_dark.json b/extensions/theme-defaults/themes/membrane_dark.json new file mode 100644 index 00000000000000..19e8bd49390387 --- /dev/null +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -0,0 +1,700 @@ +{ + "$schema": "vscode://schemas/color-theme", + "name": "Membrane Dark", + "include": "./dark_modern.json", + "colors": { + "sash.hoverBorder": "#808080", + "editorCodeLens.foreground": "#845bff", + "menu.selectionBackground": "#2a2a2a", + }, + "tokenColors": [ + // BEGIN STYLE RESET: makes everything white (actual colors are below) + { + "scope": "comment", + "settings": { + "foreground": "#999" + } + }, + { + "name": "Function declarations", + "scope": [ + "entity.name.function", + "support.function", + "support.constant.handlebars", + "source.powershell variable.other.member", + "entity.name.operator.custom-literal" + ], + "settings": { + "foreground": "#0f0" + } + }, + { + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "header", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "variable.other.enummember", + "keyword.operator.plus.exponent", + "keyword.operator.minus.exponent" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "constant.regexp", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "entity.name.tag", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "entity.name.tag.css", + "entity.name.tag.less" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "entity.other.attribute-name.class.css", + "source.css entity.other.attribute-name.class", + "entity.other.attribute-name.id.css", + "entity.other.attribute-name.parent-selector.css", + "entity.other.attribute-name.parent.less", + "source.css entity.other.attribute-name.pseudo-class", + "entity.other.attribute-name.pseudo-element.css", + "source.css.less entity.other.attribute-name.id", + "entity.other.attribute-name.scss" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "invalid", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "scope": "markup.bold", + "settings": { + "fontStyle": "bold", + "foreground": "#ececec" + } + }, + { + "scope": "markup.heading", + "settings": { + "fontStyle": "bold", + "foreground": "#ececec" + } + }, + { + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.strikethrough", + "settings": { + "fontStyle": "strikethrough" + } + }, + { + "scope": "markup.inserted", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "markup.deleted", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "markup.changed", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "punctuation.definition.quote.begin.markdown", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "punctuation.definition.list.begin.markdown", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "markup.inline.raw", + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "brackets of XML/HTML tags", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "meta.preprocessor", + "entity.name.function.preprocessor" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "meta.preprocessor.string", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "meta.preprocessor.numeric", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "meta.structure.dictionary.key.python", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "meta.diff.header", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "storage", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "storage.modifier", + "keyword.operator.noexcept" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "string", + "meta.embedded.assembly" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "string.tag", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "string.value", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "string.regexp", + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "String interpolation", + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Reset JavaScript string interpolation expression", + "scope": [ + "meta.template.expression" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "source.css variable", + "source.coffee.embedded" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "keyword", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "keyword.operator.new", + "keyword.operator.expression", + "keyword.operator.cast", + "keyword.operator.sizeof", + "keyword.operator.alignof", + "keyword.operator.typeid", + "keyword.operator.alignas", + "keyword.operator.instanceof", + "keyword.operator.logical.python", + "keyword.operator.wordlike" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "keyword.other.unit", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "support.function.git-rebase", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "constant.sha.git-rebase", + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "coloring of the Java import and package identifiers", + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Types declaration and references", + "scope": [ + "support.class", + "support.type", + "entity.name.type", + "entity.name.namespace", + "entity.other.attribute", + "entity.name.scope-resolution", + "entity.name.class", + "storage.type.numeric.go", + "storage.type.byte.go", + "storage.type.boolean.go", + "storage.type.string.go", + "storage.type.uintptr.go", + "storage.type.error.go", + "storage.type.rune.go", + "storage.type.cs", + "storage.type.generic.cs", + "storage.type.modifier.cs", + "storage.type.variable.cs", + "storage.type.annotation.java", + "storage.type.generic.java", + "storage.type.java", + "storage.type.object.array.java", + "storage.type.primitive.array.java", + "storage.type.primitive.java", + "storage.type.token.java", + "storage.type.groovy", + "storage.type.annotation.groovy", + "storage.type.parameters.groovy", + "storage.type.generic.groovy", + "storage.type.object.array.groovy", + "storage.type.primitive.array.groovy", + "storage.type.primitive.groovy" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Types declaration and references, TS grammar specific", + "scope": [ + "meta.type.cast.expr", + "meta.type.new.expr", + "support.constant.math", + "support.constant.dom", + "support.constant.json", + "entity.other.inherited-class" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Control flow / Special keywords", + "scope": [ + "keyword.control", + "source.cpp keyword.operator.new", + "keyword.operator.delete", + "keyword.other.using", + "keyword.other.directive.using", + "keyword.other.operator", + "entity.name.operator" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Variable and parameter name", + "scope": [ + "variable", + "meta.definition.variable.name", + "support.variable", + "entity.name.variable", + "constant.other.placeholder" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Constants and enums", + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Object keys, TS grammar specific", + "scope": [ + "meta.object-literal.key" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "CSS property value", + "scope": [ + "support.constant.property-value", + "support.constant.font-name", + "support.constant.media-type", + "support.constant.media", + "constant.other.color.rgb-value", + "constant.other.rgb-value", + "support.constant.color" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "name": "Regular expression groups", + "scope": [ + "punctuation.definition.group.regexp", + "punctuation.definition.group.assertion.regexp", + "punctuation.definition.character-class.regexp", + "punctuation.character.set.begin.regexp", + "punctuation.character.set.end.regexp", + "keyword.operator.negation.regexp", + "support.other.parenthesis.regexp" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "constant.character.character-class.regexp", + "constant.other.character-class.set.regexp", + "constant.other.character-class.regexp", + "constant.character.set.regexp" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "keyword.operator.or.regexp", + "keyword.control.anchor.regexp" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "keyword.operator.quantifier.regexp", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "constant.character", + "constant.other.option" + ], + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "constant.character.escape", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "entity.name.label", + "settings": { + "foreground": "#ececec" + } + }, + // BEGIN STYLE + { + "scope": "constant.numeric", + "settings": { + "foreground": "#8dcb23", + } + }, + { + "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin, punctuation.definition.string.end, string.regexp", + "settings": { + "foreground": "#f89216", + } + }, + { + "scope": "constant.character.escape, punctuation.definition.group.regexp, punctuation.definition.character-class.regexp", + "settings": { + "foreground": "#b86c10", + } + }, + { + "scope": "constant.language.boolean, constant.language.null, constant.language.undefined", + "settings": { + "foreground": "#8dcb23" + } + }, + { + "scope": "comment, punctuation.definition.comment", + "settings": { + "foreground": "#5a5a5a" + } + }, + { + "scope": "variable, variable.other, variable.other.readwrite, variable.other.object, support.constant", + "settings": { + "foreground": "#eee", + } + }, + { + "scope": "keyword.control.export, keyword.control.import, keyword.control.switch, storage.type.interface", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.conditional, keyword.control.loop", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator.new, keyword.control.flow, keyword.control.from, storage.type.function, keyword.control.as, keyword.operator.expression.typeof, keyword.operator.expression.in", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.modifier.async", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.trycatch", + "settings": { + "foreground": "#c02727", + } + }, + { + "scope": "entity.name.type.interface, entity.name.type", + "settings": { + "foreground": "#eee", + } + }, + { + "scope": "entity.name.function", + "settings": { + "foreground": "#eee", + } + }, + { + "scope": "meta.object-literal.member", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type.function.arrow", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "punctuation.separator.key-value, punctuation.accessor, keyword.operator.type.annotation, punctuation.terminator.statement", + "settings": { + "foreground": "#808080", + } + }, + { + "scope": "punctuation.definition.binding-pattern.array", + "settings": { + "foreground": "#808080", + } + }, + // JSX tags + { + "scope": "entity.name.tag, punctuation.definition.tag", + "settings": { + "foreground": "#68778F", + } + } + ], + "semanticHighlighting": true, + "semanticTokenColors": { + "newOperator": "#ececec", + "stringLiteral": "#ececec", + "customLiteral": "#ececec", + "numberLiteral": "#ececec", + } +} diff --git a/extensions/theme-defaults/themes/membrane_light.json b/extensions/theme-defaults/themes/membrane_light.json new file mode 100644 index 00000000000000..876b923f385f8d --- /dev/null +++ b/extensions/theme-defaults/themes/membrane_light.json @@ -0,0 +1,854 @@ +{ + "$schema": "vscode://schemas/color-theme", + "name": "Membrane Light", + "include": "./light_modern.json", + "colors": { + "sash.hoverBorder": "#999", + "diffEditor.unchangedRegionBackground": "#efefef", + "activityBar.activeBorder": "#3c3c3c", + "activityBar.background": "#ddd", + "activityBar.border": "#bbb", + "activityBar.foreground": "#3c3c3c", + "activityBar.inactiveForeground": "#727272", + "activityBarBadge.background": "#3c3c3c", + "activityBarBadge.foreground": "#efefef", + "badge.background": "#bbb", + "badge.foreground": "#3c3c3c", + "button.background": "#3c3c3c", + "button.border": "#3c3c3c", + "button.foreground": "#efefef", + "button.hoverBackground": "#3c3c3c75", + "button.secondaryBackground": "#ddd", + "button.secondaryForeground": "#3c3c3c", + "button.secondaryHoverBackground": "#dddddd75", + "checkbox.background": "#efefef", + "checkbox.border": "#bbb", + "descriptionForeground": "#3c3c3c", + "dropdown.background": "#efefef", + "dropdown.border": "#bbb", + "dropdown.foreground": "#3c3c3c", + "dropdown.listBackground": "#efefef", + "editor.background": "#efefef", + "editor.foreground": "#3c3c3c", + "editor.inactiveSelectionBackground": "#eeeeee50", + "editor.selectionHighlightBackground": "#bbbbbb50", + "editor.findMatchBackground": "#4e1", + "editor.findMatchBorder": "#4e1", + "editor.findMatchHighlightBackground": "#add6ff99", + "editor.findMatchHighlightBorder": "#add6ff99", + "editor.lineHighlightBackground": "#ddd", + "editorCodeLens.foreground": "#3c3c3c", + "editorGroup.border": "#bbb", + "editorGroupHeader.tabsBackground": "#efefef", + "editorGroupHeader.tabsBorder": "#bbb", + "editorGutter.addedBackground": "#4e1", + "editorGutter.deletedBackground": "#f36", + "editorGutter.modifiedBackground": "#43f", + "editorIndentGuide.background1": "#bbb", + "editorIndentGuide.activeBackground1": "#bbb", + "editorLineNumber.foreground": "#aaa", + "editorLineNumber.activeForeground": "#222", + "editorOverviewRuler.border": "#efefef", + "editorSuggestWidget.background": "#efefef", + "editorWidget.background": "#efefef", + "focusBorder": "#999", + "foreground": "#3c3c3c", + "icon.foreground": "#3c3c3c", + "input.background": "#efefef", + "input.border": "#999", + "input.foreground": "#3c3c3c", + "input.placeholderForeground": "#666", + "inputOption.activeBackground": "#999", + "inputOption.activeBorder": "#666", + "inputOption.activeForeground": "#000", + "keybindingLabel.foreground": "#3c3c3c", + "list.activeSelectionBackground": "#ddd", + "list.activeSelectionForeground": "#000", + "list.activeSelectionIconForeground": "#000", + "list.hoverBackground": "#efefef", + "list.focusAndSelectionOutline": "#3c3c3c", + "menu.border": "#bbb", + "notebook.cellBorderColor": "#bbb", + "notebook.selectedCellBackground": "#eeeeee50", + "notificationCenterHeader.background": "#efefef", + "notificationCenterHeader.foreground": "#3c3c3c", + "notifications.background": "#efefef", + "notifications.border": "#bbb", + "notifications.foreground": "#3c3c3c", + "panel.background": "#efefef", + "panel.border": "#bbb", + "panelInput.border": "#bbb", + "panelTitle.activeBorder": "#bbb", + "panelTitle.activeForeground": "#3c3c3c", + "panelTitle.inactiveForeground": "#3c3c3c", + "peekViewEditor.matchHighlightBackground": "#BB800966", + "peekViewResult.background": "#efefef", + "peekViewResult.matchHighlightBackground": "#BB800966", + "pickerGroup.border": "#bbb", + "pickerGroup.foreground": "#bbb", + "ports.iconRunningProcessForeground": "#369432", + "progressBar.background": "#3c3c3c", + "quickInput.background": "#efefef", + "quickInput.foreground": "#3c3c3c", + "scrollbarSlider.background": "#bbb", + "scrollbarSlider.hoverBackground": "#999", + "scrollbarSlider.activeBackground": "#666", + "scrollbar.shadow": "#bbb", + "searchEditor.textInputBorder": "#bbb", + "settings.dropdownBackground": "#efefef", + "settings.dropdownBorder": "#bbb", + "settings.headerForeground": "#3c3c3c", + "settings.modifiedItemIndicator": "#BB800966", + "settings.numberInputBorder": "#bbb", + "settings.textInputBorder": "#bbb", + "sideBar.background": "#ddd", + "sideBar.border": "#bbb", + "sideBar.foreground": "#3c3c3c", + "sideBarSectionHeader.background": "#efefef", + "sideBarSectionHeader.border": "#bbb", + "sideBarSectionHeader.foreground": "#3c3c3c", + "sideBarTitle.foreground": "#3c3c3c", + "statusBar.background": "#ddd", + "statusBar.foreground": "#3c3c3c", + "statusBar.border": "#bbb", + "tab.activeBackground": "#efefef", + "tab.activeBorder": "#bbb", + "tab.activeBorderTop": "#bbb", + "tab.activeForeground": "#3c3c3c", + "tab.border": "#bbb", + "tab.hoverBackground": "#ddd", + "tab.inactiveBackground": "#efefef", + "tab.inactiveForeground": "#727272", + "tab.lastPinnedBorder": "#bbb", + "tab.unfocusedActiveBorder": "#bbb", + "tab.unfocusedActiveBorderTop": "#bbb", + "tab.unfocusedHoverBackground": "#efefef", + "textBlockQuote.background": "#ddd", + "textBlockQuote.border": "#bbb", + "textCodeBlock.background": "#ddd", + "textLink.activeForeground": "#43f", + "textLink.foreground": "#43f", + "textPreformat.foreground": "#3c3c3c", + "textPreformat.background": "#efefef", + "textSeparator.foreground": "#3c3c3c", + "titleBar.activeBackground": "#ddd", + "titleBar.activeForeground": "#3c3c3c", + "titleBar.border": "#bbb", + "titleBar.inactiveBackground": "#ddd", + "titleBar.inactiveForeground": "#8B949E", + "widget.border": "#bbb" + }, + // Copied from light_vs.json and light_plus.json + // Made all colors #000 except comments and functions + "tokenColors": [ + // BEGIN STYLE RESET: makes everything black (actual colors are below) + { + "scope": "comment", + "settings": { + "foreground": "#999" + } + }, + { + "name": "Function declarations", + "scope": [ + "entity.name.function", + "support.function", + "support.constant.handlebars", + "source.powershell variable.other.member", + "entity.name.operator.custom-literal" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "meta.diff.header", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "constant.numeric", + "variable.other.enummember", + "keyword.operator.plus.exponent", + "keyword.operator.minus.exponent" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "constant.regexp", + "settings": { + "foreground": "#000" + } + }, + { + "name": "css tags in selectors, xml tags", + "scope": "entity.name.tag", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "entity.name.selector", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "entity.other.attribute-name", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "entity.other.attribute-name.class.css", + "source.css entity.other.attribute-name.class", + "entity.other.attribute-name.id.css", + "entity.other.attribute-name.parent-selector.css", + "entity.other.attribute-name.parent.less", + "source.css entity.other.attribute-name.pseudo-class", + "entity.other.attribute-name.pseudo-element.css", + "source.css.less entity.other.attribute-name.id", + "entity.other.attribute-name.scss" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "invalid", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "markup.underline", + "settings": { + "fontStyle": "underline" + } + }, + { + "scope": "markup.bold", + "settings": { + "fontStyle": "bold", + "foreground": "#000" + } + }, + { + "scope": "markup.heading", + "settings": { + "fontStyle": "bold", + "foreground": "#000" + } + }, + { + "scope": "markup.italic", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "markup.strikethrough", + "settings": { + "fontStyle": "strikethrough" + } + }, + { + "scope": "markup.inserted", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "markup.deleted", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "markup.changed", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "punctuation.definition.quote.begin.markdown", + "punctuation.definition.list.begin.markdown" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "markup.inline.raw", + "settings": { + "foreground": "#000" + } + }, + { + "name": "brackets of XML/HTML tags", + "scope": "punctuation.definition.tag", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "meta.preprocessor", + "entity.name.function.preprocessor" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "meta.preprocessor.string", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "meta.preprocessor.numeric", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "meta.structure.dictionary.key.python", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "storage", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "storage.modifier", + "keyword.operator.noexcept" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "string", + "meta.embedded.assembly" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "string.comment.buffered.block.pug", + "string.quoted.pug", + "string.interpolated.pug", + "string.unquoted.plain.in.yaml", + "string.unquoted.plain.out.yaml", + "string.unquoted.block.yaml", + "string.quoted.single.yaml", + "string.quoted.double.xml", + "string.quoted.single.xml", + "string.unquoted.cdata.xml", + "string.quoted.double.html", + "string.quoted.single.html", + "string.unquoted.html", + "string.quoted.single.handlebars", + "string.quoted.double.handlebars" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "string.regexp", + "settings": { + "foreground": "#000" + } + }, + { + "name": "String interpolation", + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "Reset JavaScript string interpolation expression", + "scope": [ + "meta.template.expression" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "support.constant.property-value", + "support.constant.font-name", + "support.constant.media-type", + "support.constant.media", + "constant.other.color.rgb-value", + "constant.other.rgb-value", + "support.constant.color" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "source.css variable", + "source.coffee.embedded" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "support.type.property-name.json" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "keyword", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "keyword.operator.new", + "keyword.operator.expression", + "keyword.operator.cast", + "keyword.operator.sizeof", + "keyword.operator.alignof", + "keyword.operator.typeid", + "keyword.operator.alignas", + "keyword.operator.instanceof", + "keyword.operator.logical.python", + "keyword.operator.wordlike" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "keyword.other.unit", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "support.function.git-rebase", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "constant.sha.git-rebase", + "settings": { + "foreground": "#000" + } + }, + { + "name": "coloring of the Java import and package identifiers", + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "this.self", + "scope": "variable.language", + "settings": { + "foreground": "#000" + } + }, + { + "name": "Types declaration and references", + "scope": [ + "support.class", + "support.type", + "entity.name.type", + "entity.name.namespace", + "entity.other.attribute", + "entity.name.scope-resolution", + "entity.name.class", + "storage.type.numeric.go", + "storage.type.byte.go", + "storage.type.boolean.go", + "storage.type.string.go", + "storage.type.uintptr.go", + "storage.type.error.go", + "storage.type.rune.go", + "storage.type.cs", + "storage.type.generic.cs", + "storage.type.modifier.cs", + "storage.type.variable.cs", + "storage.type.annotation.java", + "storage.type.generic.java", + "storage.type.java", + "storage.type.object.array.java", + "storage.type.primitive.array.java", + "storage.type.primitive.java", + "storage.type.token.java", + "storage.type.groovy", + "storage.type.annotation.groovy", + "storage.type.parameters.groovy", + "storage.type.generic.groovy", + "storage.type.object.array.groovy", + "storage.type.primitive.array.groovy", + "storage.type.primitive.groovy" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "Types declaration and references, TS grammar specific", + "scope": [ + "meta.type.cast.expr", + "meta.type.new.expr", + "support.constant.math", + "support.constant.dom", + "support.constant.json", + "entity.other.inherited-class" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "Control flow / Special keywords", + "scope": [ + "keyword.control", + "source.cpp keyword.operator.new", + "source.cpp keyword.operator.delete", + "keyword.other.using", + "keyword.other.directive.using", + "keyword.other.operator", + "entity.name.operator" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "Variable and parameter name", + "scope": [ + "variable", + "meta.definition.variable.name", + "support.variable", + "entity.name.variable", + "constant.other.placeholder" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "Constants and enums", + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "Object keys, TS grammar specific", + "scope": [ + "meta.object-literal.key" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "CSS property value", + "scope": [ + "support.constant.property-value", + "support.constant.font-name", + "support.constant.media-type", + "support.constant.media", + "constant.other.color.rgb-value", + "constant.other.rgb-value", + "support.constant.color" + ], + "settings": { + "foreground": "#000" + } + }, + { + "name": "Regular expression groups", + "scope": [ + "punctuation.definition.group.regexp", + "punctuation.definition.group.assertion.regexp", + "punctuation.definition.character-class.regexp", + "punctuation.character.set.begin.regexp", + "punctuation.character.set.end.regexp", + "keyword.operator.negation.regexp", + "support.other.parenthesis.regexp" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "constant.character.character-class.regexp", + "constant.other.character-class.set.regexp", + "constant.other.character-class.regexp", + "constant.character.set.regexp" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "keyword.operator.quantifier.regexp", + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "keyword.operator.or.regexp", + "keyword.control.anchor.regexp" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": [ + "constant.character", + "constant.other.option" + ], + "settings": { + "foreground": "#000" + } + }, + { + "scope": "constant.character.escape", + "settings": { + "foreground": "#000" + } + }, + { + "scope": "entity.name.label", + "settings": { + "foreground": "#000" + } + }, + // BEGIN STYLE + { + "scope": "constant.numeric", + "settings": { + "foreground": "#6e9e1b", + } + }, + { + "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin, punctuation.definition.string.end, string.regexp", + "settings": { + "foreground": "#d17a11", + } + }, + { + "scope": "constant.character.escape, punctuation.definition.group.regexp, punctuation.definition.character-class.regexp", + "settings": { + "foreground": "#b86c10", + } + }, + { + "scope": "constant.language.boolean, constant.language.null, constant.language.undefined", + "settings": { + "foreground": "#6e9e1b" + } + }, + { + "scope": "comment, punctuation.definition.comment", + "settings": { + "foreground": "#9a9a9a" + } + }, + { + "scope": "variable, variable.other, variable.other.readwrite, variable.other.object, support.constant", + "settings": { + "foreground": "#333", + } + }, + { + "scope": "keyword.control.export, keyword.control.import, keyword.control.switch, storage.type.interface", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.conditional, keyword.control.loop", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator.new, keyword.control.flow, keyword.control.from, storage.type.function, keyword.control.as, keyword.operator.expression.typeof, keyword.operator.expression.in", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.modifier.async", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.trycatch", + "settings": { + "foreground": "#b62828", + } + }, + { + "scope": "entity.name.type.interface, entity.name.type", + "settings": { + "foreground": "#333", + } + }, + { + "scope": "entity.name.function", + "settings": { + "foreground": "#333", + } + }, + { + "scope": "meta.object-literal.member", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type.function.arrow", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "punctuation.separator.key-value, punctuation.accessor, keyword.operator.type.annotation, punctuation.terminator.statement", + "settings": { + "foreground": "#808080", + } + }, + { + "scope": "punctuation.definition.binding-pattern.array", + "settings": { + "foreground": "#808080", + } + }, // JSX tags + { + "scope": "entity.name.tag, punctuation.definition.tag", + "settings": { + "foreground": "#68778F", + } + } + ], + "semanticHighlighting": true, + "semanticTokenColors": { + "newOperator": "#000", + "stringLiteral": "#000", + "customLiteral": "#000", + "numberLiteral": "#000" + } +} diff --git a/extensions/tsconfig.base.json b/extensions/tsconfig.base.json index 9d939dd568aa72..f2fb16a5dcfbe0 100644 --- a/extensions/tsconfig.base.json +++ b/extensions/tsconfig.base.json @@ -13,7 +13,7 @@ "noImplicitAny": true, "noImplicitReturns": true, "noImplicitOverride": true, - "noUnusedLocals": true, + "noUnusedLocals": false, "noUnusedParameters": true, "forceConsistentCasingInFileNames": true } diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js index 86733dfd1ee307..605bc5b35a8397 100644 --- a/extensions/typescript-language-features/extension-browser.webpack.config.js +++ b/extensions/typescript-language-features/extension-browser.webpack.config.js @@ -5,8 +5,11 @@ // @ts-check import CopyPlugin from 'copy-webpack-plugin'; import path from 'path'; +import { createRequire } from 'node:module'; import defaultConfig, { browser as withBrowserDefaults, browserPlugins } from '../shared.webpack.config.mjs'; +const require = createRequire(import.meta.url); + const languages = [ 'zh-tw', 'cs', @@ -27,6 +30,12 @@ export default [withBrowserDefaults({ entry: { extension: './src/extension.browser.ts', }, + resolve: { + // Ensure events is available for ts-plugin and its dependencies + fallback: { + 'events': require.resolve('events/'), + } + }, plugins: [ ...browserPlugins(import.meta.dirname), // add plugins, don't replace inherited @@ -72,7 +81,14 @@ export default [withBrowserDefaults({ path: path.join(import.meta.dirname, 'dist', 'browser'), libraryTarget: undefined, }, + resolve: { + // Override to ensure events is bundled, not externalized + fallback: { + 'events': require.resolve('events/'), + } + }, externals: { 'perf_hooks': 'commonjs perf_hooks', + // Explicitly do NOT externalize 'events' - it must be bundled } })]; diff --git a/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts index 04e068916edf30..62ece21b43b7df 100644 --- a/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts @@ -695,6 +695,8 @@ export default class BufferSyncSupport extends Disposable { } private triggerDiagnostics(delay: number = 200) { + /// MEMBRNAE: make intellisense and ts-plugin respond faster by reducing the artificial delay. + delay = 50; this.diagnosticDelayer.trigger(() => { this.sendPendingDiagnostics(); }, delay); diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index 5adf1866112561..422b4ca65a10cb 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -40,7 +40,7 @@ export class WorkerServerProcessFactory implements TsServerProcessFactory { version: TypeScriptVersion, args: readonly string[], kind: TsServerProcessKind, - configuration: TypeScriptServiceConfiguration, + _configuration: TypeScriptServiceConfiguration, _versionManager: TypeScriptVersionManager, _nodeVersionManager: NodeVersionManager, tsServerLog: TsServerLog | undefined, @@ -50,8 +50,6 @@ export class WorkerServerProcessFactory implements TsServerProcessFactory { ...args, // Explicitly give TS Server its path so it can load local resources '--executingFilePath', tsServerPath, - // Enable/disable web type acquisition - (configuration.webTypeAcquisitionEnabled && supportsReadableByteStreams() ? '--experimentalTypeAcquisition' : '--disableAutomaticTypingAcquisition'), ]; return new WorkerServerProcess(kind, tsServerPath, this._extensionUri, launchArgs, tsServerLog, this._logger); @@ -59,7 +57,6 @@ export class WorkerServerProcessFactory implements TsServerProcessFactory { } class WorkerServerProcess implements TsServerProcess { - private static idPool = 0; private readonly id = WorkerServerProcess.idPool++; @@ -155,7 +152,19 @@ class WorkerServerProcess implements TsServerProcess { } write(serverRequest: Proto.Request): void { - this._tsserver.postMessage(serverRequest); + // MEMBRANE: this function has been modified to allow transfering objects to tsserver running on a web worker. + // Specifically, the Membrane extension sends a MessagePort so that it can talk to our ts-plugin. + const { arguments: args } = serverRequest; + const transfer = args?.configuration?.transfer; + const request = { + ...serverRequest, + arguments: args, + }; + if (transfer) { + this._tsserver.postMessage(request, transfer); + } else { + this._tsserver.postMessage(serverRequest); + } } onData(handler: (response: Proto.Response) => void): void { diff --git a/extensions/typescript-language-features/web/src/logging.ts b/extensions/typescript-language-features/web/src/logging.ts index 843228f0df0974..38523e87afd76e 100644 --- a/extensions/typescript-language-features/web/src/logging.ts +++ b/extensions/typescript-language-features/web/src/logging.ts @@ -21,6 +21,8 @@ export class Logger { const doLog = typeof logLevel === 'undefined' ? (_message: string) => { } : (message: string) => { postMessage({ type: 'log', body: message }); }; + // MEMBRANE: uncomment to see the logs in the console + // : (message: string) => { console.log(message) } this.tsLogger = { close: () => { }, diff --git a/extensions/typescript-language-features/web/src/pathMapper.ts b/extensions/typescript-language-features/web/src/pathMapper.ts index dbe9aada758f03..58262e318808bf 100644 --- a/extensions/typescript-language-features/web/src/pathMapper.ts +++ b/extensions/typescript-language-features/web/src/pathMapper.ts @@ -76,7 +76,10 @@ export function fromResource(extensionUri: URI, uri: URI) { && uri.path.endsWith('.d.ts')) { return uri.path; } - return `/${uri.scheme}/${uri.authority}${uri.path}`; + + // MEMBRANE: this function is used by the file watcher which uses `ts-nul-authority` + // instead of an empty string so we must do the same + return `/${uri.scheme}/${uri.authority || 'ts-nul-authority'}${uri.path}`; } export function looksLikeLibDtsPath(filepath: string) { @@ -103,14 +106,17 @@ function filePathToResourceUri(filepath: string): URI | undefined { return URI.from({ scheme, authority, path: (path ? '/' + path : path) }); } -export function mapUri(uri: URI, mappedScheme: string): URI { +export function mapUri(uri: URI, _mappedScheme: string): URI { if (uri.scheme === 'vscode-global-typings') { throw new Error('can\'t map vscode-global-typings'); } if (!uri.authority) { uri = uri.with({ authority: 'ts-nul-authority' }); } - uri = uri.with({ scheme: mappedScheme, path: `/${uri.scheme}/${uri.authority || 'ts-nul-authority'}${uri.path}` }); - - return uri; + // MEMBRANE: make all request to memfs instead of vscode-* fs + return uri.with({ + scheme: 'memfs', + authority: '', + path: `/${uri.authority}${uri.path}`, + }); } diff --git a/extensions/typescript-language-features/web/src/serverHost.ts b/extensions/typescript-language-features/web/src/serverHost.ts index fdc617868b5c82..cca4f6be23da60 100644 --- a/extensions/typescript-language-features/web/src/serverHost.ts +++ b/extensions/typescript-language-features/web/src/serverHost.ts @@ -37,6 +37,7 @@ interface TsInternals extends TsModule { getDirectoryPath: (path: string) => string; directorySeparator: string; } +import membraneTsPlugin from '../../../../../ts-plugin/src/index'; type ServerHostWithImport = ts.server.ServerHost & { importPlugin(root: string, moduleName: string): Promise }; @@ -72,7 +73,36 @@ function createServerHost( const textEncoder = new TextEncoder(); return { - watchFile: watchManager.watchFile.bind(watchManager), + watchFile: ( + path: string, + callback: ts.FileWatcherCallback, + pollingInterval?: number, + options?: ts.WatchOptions, + ): ts.FileWatcher => { + const wrappedCallback: ts.FileWatcherCallback = ( + filePath: string, + eventKind: ts.FileWatcherEventKind, + ) => { + // MEMBRANE: Ignore package.json created events, removed on typescript@v5.5 + // https://github.com/microsoft/TypeScript/commit/f3f70df94e120bab69dd766bacd22089347b71c9 + // only for memfs/ts-nul-authority/{program}/package.json + if ( + filePath.endsWith("package.json") && + /^\/memfs\/ts-nul-authority\/[^/]+\/package\.json$/.test(filePath) && + eventKind === ts.FileWatcherEventKind.Created + ) { + return; + } + callback(filePath, eventKind); + }; + + return watchManager.watchFile( + path, + wrappedCallback, + pollingInterval, + options, + ); + }, watchDirectory: watchManager.watchDirectory.bind(watchManager), setTimeout(callback: (...args: unknown[]) => void, ms: number, ...args: unknown[]): unknown { return setTimeout(callback, ms, ...args); @@ -87,27 +117,16 @@ function createServerHost( this.clearTimeout(timeoutId); }, importPlugin: async (root, moduleName) => { - const packageRoot = combinePaths(root, moduleName); - - let packageJson: any | undefined; - try { - const packageJsonResponse = await fetch(combinePaths(packageRoot, 'package.json')); - packageJson = await packageJsonResponse.json(); - } catch (e) { - return { module: undefined, error: new Error(`Could not load plugin. Could not load 'package.json'.`) }; - } - - const browser = packageJson.browser; - if (!browser) { - return { module: undefined, error: new Error(`Could not load plugin. No 'browser' field found in package.json.`) }; + if (moduleName !== 'membrane-ts-plugin') { + throw new Error('Only the Membrane TS plugin is supported'); } - - const scriptPath = combinePaths(packageRoot, browser); + const scriptPath = combinePaths(root, moduleName); try { - const { default: module } = await import(/* webpackIgnore: true */ scriptPath); - return { module, error: undefined }; + // Dynamically import the script using the constructed path + // This assumes the script is accessible via your web server and is correctly set up to be imported as a module + return { module: membraneTsPlugin, error: undefined }; } catch (e) { - return { module: undefined, error: e }; + return { module: undefined, error: new Error(`Could not load plugin from ${scriptPath}`) }; } }, args: Array.from(args), diff --git a/extensions/typescript-language-features/web/src/webServer.ts b/extensions/typescript-language-features/web/src/webServer.ts index 5a964231519aed..1f4a6e885c2884 100644 --- a/extensions/typescript-language-features/web/src/webServer.ts +++ b/extensions/typescript-language-features/web/src/webServer.ts @@ -4,6 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /// +// Force webpack to bundle events module +import 'events'; + import ts from 'typescript/lib/tsserverlibrary'; import { URI } from 'vscode-uri'; import { FileWatcherManager } from './fileWatcherManager'; @@ -26,7 +29,8 @@ async function initializeSession( extensionUri: URI, ports: { tsserver: MessagePort; sync: MessagePort; watcher: MessagePort }, ): Promise { - const logLevel = parseLogLevel(findArgument(args, '--logVerbosity')); + // MEMBRANE: Can be set to "normal" or "verbose" for debugging + const logLevel = parseLogLevel(findArgument(args, '--logVerbosity')); // ?? "normal"); const logger = new Logger(logLevel); const modeOrUnknown = parseServerMode(args); diff --git a/src/tsconfig.json b/src/tsconfig.json index bfda0ebafc1e56..cb4c3d2ed37430 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "esModuleInterop": true, + "moduleSuffixes": [".membrane", ""], "removeComments": false, "preserveConstEnums": true, "sourceMap": false, diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index c723dffdb206ef..9cceea84eea977 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -6,6 +6,8 @@ import { toErrorMessage } from '../common/errorMessage.js'; import { ErrorNoTelemetry, getErrorMessage } from '../common/errors.js'; import { mark } from '../common/performance.js'; +// eslint-disable-next-line local/code-import-patterns +import { membraneApi } from '../../code/browser/workbench/membrane.js'; class MissingStoresError extends Error { constructor(readonly db: IDBDatabase) { @@ -20,6 +22,8 @@ export class DBClosedError extends Error { } } +const userSettingsResource = '/User/settings.json'; + export class IndexedDB { static async create(name: string, version: number | undefined, stores: string[]): Promise { @@ -117,17 +121,64 @@ export class IndexedDB { } const transaction = this.database.transaction(store, transactionMode); this.pendingTransactions.push(transaction); - return new Promise((c, e) => { - transaction.oncomplete = () => { - if (Array.isArray(request)) { - c(request.map(r => r.result)); - } else { - c(request.result); + // MEMBRANE + // Proxy to save indexeddb reqs + const requests: Array<{ prop: string; key: string; value?: unknown; request: IDBRequest }> = []; + const storeProxy = new Proxy(transaction.objectStore(store), { + get(target: IDBObjectStore, prop: string): unknown { + return (...args: unknown[]) => { + const result = (target[prop as keyof IDBObjectStore] as Function).apply(target, args); + requests.push({ + prop, + key: (prop === 'get' ? args[0] : args[1]) as string, + value: prop === 'put' ? args[0] : undefined, + request: result + }); + return result; + }; + }, + }); + + const dbOperations: IDBRequest | IDBRequest[] = dbRequestFn(storeProxy); + return new Promise((resolve, reject) => { + transaction.oncomplete = async () => { + try { + + const membraneRequests = requests.filter(req => isMembraneKey(req.key)); + const res = await handleMembraneRequests(membraneRequests); + const getResult = (r: IDBRequest) => { + // find the req + const req = requests.find(req => req.request === r); + if (!req) { + return r.result; + } + + // Membrane keys + if (isMembraneKey(req.key)) { + return res.find(mr => mr.key === req.key)?.value; + } + + const isGetAllKeys = req.prop === 'getAllKeys'; + const isUserDataStore = + r.source instanceof IDBObjectStore && + r.source.name === 'vscode-userdata-store'; + + if (isGetAllKeys && isUserDataStore) { + const result = Array.isArray(r.result) ? r.result : []; + return result.includes(userSettingsResource) ? result : [...result, userSettingsResource]; + } + + return r.result; + }; + + resolve(Array.isArray(dbOperations) ? dbOperations.map(getResult) : getResult(dbOperations)); + } catch (error) { + console.error('Error in transaction oncomplete:', error); + reject(error); } }; - transaction.onerror = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error')); - transaction.onabort = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error')); - const request = dbRequestFn(transaction.objectStore(store)); + transaction.onerror = () => reject(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error')); + transaction.onabort = () => reject(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error')); }).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1)); } @@ -175,3 +226,42 @@ export class IndexedDB { }).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1)); } } + + +function isMembraneKey(key: unknown): boolean { + const MEMBRANE_KEYS = [ + '/User/settings.json', + '/User/keybindings.json' + ]; + return typeof key === 'string' && MEMBRANE_KEYS.includes(key); +} + +async function handleMembraneRequests(requests: Array<{ prop: string; key: string; value?: unknown }>): Promise> { + const handleGet = async (key: string) => { + try { + const res = await membraneApi('GET', `/settings?keys=${key}`); + const value = res.status === 404 ? undefined : (await res.json())[key]; + return { key, value }; + } catch (error) { + console.log(`Error fetching data for key ${key}:`, error); + return { key, value: undefined }; + } + }; + + const handlePut = async (key: string, value: unknown) => { + try { + const stringValue = value instanceof Uint8Array + ? new TextDecoder().decode(value) + : value; + await membraneApi('POST', '/settings', JSON.stringify({ key, value: stringValue })); + return { key, value: true }; + } catch (error) { + console.log(`Error putting data for key ${key}:`, error); + return { key, value: undefined }; + } + }; + + return Promise.all(requests.map(({ prop, key, value }) => + prop === 'get' ? handleGet(key) : handlePut(key, value) + )); +} diff --git a/src/vs/base/browser/ui/dialog/membraneDialog.ts b/src/vs/base/browser/ui/dialog/membraneDialog.ts new file mode 100644 index 00000000000000..7ef9b4d00b2cf9 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/membraneDialog.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDialogOptions, IDialogResult } from './dialog.js'; +import { Disposable } from '../../../common/lifecycle.js'; +import { generateUuid } from '../../../common/uuid.js'; +// import { mainWindow } from '../../../browser/window.js'; +import { MembranePortManager } from '../../../../base/browser/ui/dialog/membranePortManager.js'; + +export interface MembraneDialogMessage { + type: 'confirm' | 'prompt' | 'info' | 'warn' | 'error' | 'input'; + id: string; + message: string; + detail?: string; + buttons: string[]; + checkboxLabel?: string; + checkboxChecked?: boolean; + inputs?: Array<{ + placeholder?: string; + type?: 'text' | 'password'; + value?: string; + }>; + cancelId?: number; + iconType?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending'; +} + +export interface MembraneDialogResponse { + id: string; + button: number; + checkboxChecked?: boolean; + values?: string[]; +} + +export class MembraneDialog extends Disposable { + private static pendingResponses = new Map void; + reject: (error: Error) => void; + }>(); + + private readonly dialogId: string; + + // Handle dialog responses from Gaze + private handleDialogResponse(response: MembraneDialogResponse): void { + const pending = MembraneDialog.pendingResponses.get(response.id); + if (pending) { + MembraneDialog.pendingResponses.delete(response.id); + pending.resolve({ + button: response.button, + checkboxChecked: response.checkboxChecked, + values: response.values + }); + } + } + + constructor( + _container: HTMLElement, + private message: string, + private buttons: string[] | undefined, + private readonly options: IDialogOptions + ) { + super(); + this.dialogId = generateUuid(); + + // Register this instance as the dialog response handler + MembranePortManager.setDialogResponseHandler((response: unknown) => { + this.handleDialogResponse(response as MembraneDialogResponse); + }); + } + + async show(): Promise { + return new Promise((resolve, reject) => { + // Store the promise resolvers + MembraneDialog.pendingResponses.set(this.dialogId, { resolve, reject }); + + // Prepare the message to send to the client + const dialogMessage: MembraneDialogMessage = { + type: this.inferDialogType(), + id: this.dialogId, + message: this.message, + detail: this.options.detail, + buttons: this.getButtonLabels(), + checkboxLabel: this.options.checkboxLabel, + checkboxChecked: this.options.checkboxChecked, + inputs: this.options.inputs, + cancelId: this.options.cancelId, + iconType: this.options.type + }; + + + // Send via MessagePort using shared port manager + MembranePortManager.sendMessage('membraneDialog', dialogMessage); + + // Set up timeout to prevent hanging dialogs + setTimeout(() => { + const pending = MembraneDialog.pendingResponses.get(this.dialogId); + if (pending) { + MembraneDialog.pendingResponses.delete(this.dialogId); + // Default to cancel/close behavior + pending.resolve({ + button: this.options.cancelId || 0, + checkboxChecked: this.options.checkboxChecked + }); + } + }, 30000); // 30 second timeout + }); + } + + private inferDialogType(): MembraneDialogMessage['type'] { + // Try to infer dialog type from options or button text + if (this.options.inputs && this.options.inputs.length > 0) { + return 'input'; + } + + const buttonText = this.getButtonLabels().join(' ').toLowerCase(); + if (buttonText.includes('ok') && buttonText.includes('cancel')) { + return 'confirm'; + } + if (buttonText.includes('yes') && buttonText.includes('no')) { + return 'confirm'; + } + + switch (this.options.type) { + case 'error': + return 'error'; + case 'warning': + return 'warn'; + case 'info': + case 'question': + return 'info'; + default: + return 'prompt'; + } + } + + private getButtonLabels(): string[] { + if (Array.isArray(this.buttons) && this.buttons.length > 0) { + return this.buttons; + } else if (!this.options.disableDefaultAction) { + return ['OK']; + } else { + return []; + } + } + + updateMessage(message: string): void { + this.message = message; + + // Send update to Gaze if dialog is currently showing + if (MembraneDialog.pendingResponses.has(this.dialogId)) { + MembranePortManager.sendMessage('membraneDialogUpdate', { + id: this.dialogId, + message: message + }); + } + } + + override dispose(): void { + super.dispose(); + // Clean up any pending dialog + MembraneDialog.pendingResponses.delete(this.dialogId); + } +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/membranePortManager.ts b/src/vs/base/browser/ui/dialog/membranePortManager.ts new file mode 100644 index 00000000000000..5a1b653758b4a5 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/membranePortManager.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +interface IMembraneWindow extends Window { + dialogsToGazePort?: MessagePort; +} + +declare const window: IMembraneWindow; + +export class MembranePortManager { + private static dialogsPort: MessagePort | null = null; + private static notificationResponseHandler: ((response: unknown) => void) | null = null; + private static dialogResponseHandler: ((response: unknown) => void) | null = null; + + static setNotificationResponseHandler(handler: (response: unknown) => void): void { + MembranePortManager.notificationResponseHandler = handler; + } + + static setDialogResponseHandler(handler: (response: unknown) => void): void { + MembranePortManager.dialogResponseHandler = handler; + } + + static ensureInitialized(): void { + if (MembranePortManager.dialogsPort) { + return; // Already initialized + } + + MembranePortManager.dialogsPort = window.dialogsToGazePort || null; + if (window.dialogsToGazePort) { + delete window.dialogsToGazePort; + } + + // Check if the port was actually available + if (!MembranePortManager.dialogsPort) { + console.warn('MembranePortManager: dialogsToGazePort not available. Dialogs and notifications may not work.'); + return; + } + + // Set up listener for dialog and notification responses + MembranePortManager.dialogsPort.onmessage = (event) => { + try { + if (event.data.messageType === 'membraneDialogResponse') { + MembranePortManager.dialogResponseHandler?.(event.data); + } else if (event.data.messageType === 'membraneNotificationResponse') { + MembranePortManager.notificationResponseHandler?.(event.data); + } + } catch (error) { + console.error(`Error handling ${event.data.messageType} message:`, error); + } + }; + + } + + static sendMessage(messageType: string, data: object): void { + try { + MembranePortManager.ensureInitialized(); + if (!MembranePortManager.dialogsPort) { + console.warn(`MembranePortManager: Cannot send ${messageType} message - dialogs port not available`); + return; + } + const message = { + messageType, + ...data + }; + MembranePortManager.dialogsPort.postMessage(message); + } catch (error) { + console.error(`Error sending ${messageType} message:`, error); + } + } + +} \ No newline at end of file diff --git a/src/vs/base/test/common/glob.test.ts b/src/vs/base/test/common/glob.test.ts index 3021b924988f05..53a80555c4e20d 100644 --- a/src/vs/base/test/common/glob.test.ts +++ b/src/vs/base/test/common/glob.test.ts @@ -1132,6 +1132,12 @@ suite('Glob', () => { assertGlobMatch(p, URI.file('super/duper/long/some/file.md').with({ scheme: 'scheme' }).toString()); }); + test('URI with same path but different scheme do not match', () => { + const p = 'scheme:/**/*.md'; + assertGlobMatch(p, URI.file('super/duper/long/some/file.md').with({ scheme: 'scheme' }).toString()); + assertNoGlobMatch(p, URI.file('super/duper/long/some/file.md').with({ scheme: 'not-scheme' }).toString()); + }); + test('expression fails when siblings use promises (https://github.com/microsoft/vscode/issues/146294)', async function () { const siblings = ['test.html', 'test.txt', 'test.ts']; const hasSibling = (name: string) => Promise.resolve(siblings.indexOf(name) !== -1); diff --git a/src/vs/code/browser/workbench/membrane.ts b/src/vs/code/browser/workbench/membrane.ts new file mode 100644 index 00000000000000..0aa40aab712e28 --- /dev/null +++ b/src/vs/code/browser/workbench/membrane.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ISecretStorageProvider } from '../../../platform/secrets/common/secrets.js'; + +export class SecretStorageProvider implements ISecretStorageProvider { + public type: 'persisted'; + private static instance: SecretStorageProvider; + public getAuthToken: () => Promise; + + constructor() { + this.type = 'persisted'; + // Capture the window function + // eslint-disable-next-line no-restricted-globals, local/code-no-any-casts, @typescript-eslint/no-explicit-any + this.getAuthToken = (window as any).globalIdeState.getAuthToken; + // eslint-disable-next-line no-restricted-globals, local/code-no-any-casts, @typescript-eslint/no-explicit-any + (window as any).globalIdeState.getAuthToken = () => { + throw new Error('This function is no longer available'); + }; + } + + public static getInstance(): SecretStorageProvider { + if (!SecretStorageProvider.instance) { + SecretStorageProvider.instance = new SecretStorageProvider(); + } + return SecretStorageProvider.instance; + } + + async get(key: string): Promise { + let extensionKey; + try { + // Check if the key is for an extension (it's a JSON string) + extensionKey = JSON.parse(key); + } catch (err) { + // Only keys for extensions are stored as JSON so this must not be an extension secret. + } + if ( + extensionKey?.extensionId === 'membrane.membrane' && + extensionKey?.key === 'membraneApiToken' + ) { + try { + return await this.getAuthToken(); + } catch (error) { + throw new Error(`Failed to read Membrane API token: ${error}`); + } + } + return localStorage.getItem(key) ?? undefined; + } + + async set(key: string, value: string): Promise { + localStorage.setItem(key, value); + } + + async delete(key: string): Promise { + localStorage.removeItem(key); + } +} + + +export async function membraneApi( + method: 'GET' | 'POST', + path: `/${string}`, + body?: BodyInit +): Promise { + // WARNING: It's important that this url is NOT controlled by the extension settings (i.e. the url used by the + // extension and gaze) because this function is used to load the user settings themselves. + const baseUrl = 'https://ide.membrane.io/api'; + + const secretProvider = SecretStorageProvider.getInstance(); + const token = await secretProvider.getAuthToken(); + + if (!token) { + throw new Error('Failed to retrieve Membrane API token'); + } + + return await fetch(`${baseUrl}${path}`, { + method, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body, + }); +} diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 533cb26ae8e5c8..779f7506bc6ce1 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,621 +3,158 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isStandalone } from '../../../base/browser/browser.js'; -import { addDisposableListener } from '../../../base/browser/dom.js'; -import { mainWindow } from '../../../base/browser/window.js'; -import { VSBuffer, decodeBase64, encodeBase64 } from '../../../base/common/buffer.js'; -import { Emitter } from '../../../base/common/event.js'; -import { Disposable, IDisposable } from '../../../base/common/lifecycle.js'; -import { parse } from '../../../base/common/marshalling.js'; -import { Schemas } from '../../../base/common/network.js'; -import { posix } from '../../../base/common/path.js'; -import { isEqual } from '../../../base/common/resources.js'; -import { ltrim } from '../../../base/common/strings.js'; -import { URI, UriComponents } from '../../../base/common/uri.js'; -import product from '../../../platform/product/common/product.js'; -import { ISecretStorageProvider } from '../../../platform/secrets/common/secrets.js'; -import { isFolderToOpen, isWorkspaceToOpen } from '../../../platform/window/common/window.js'; -import type { IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from '../../../workbench/browser/web.api.js'; -import { AuthenticationSessionInfo } from '../../../workbench/services/authentication/browser/authenticationService.js'; -import type { IURLCallbackProvider } from '../../../workbench/services/url/browser/urlService.js'; import { create } from '../../../workbench/workbench.web.main.internal.js'; - -interface ISecretStorageCrypto { - seal(data: string): Promise; - unseal(data: string): Promise; -} - -class TransparentCrypto implements ISecretStorageCrypto { - - async seal(data: string): Promise { - return data; - } - - async unseal(data: string): Promise { - return data; - } -} - -const enum AESConstants { - ALGORITHM = 'AES-GCM', - KEY_LENGTH = 256, - IV_LENGTH = 12, -} - -class NetworkError extends Error { - - constructor(inner: Error) { - super(inner.message); - this.name = inner.name; - this.stack = inner.stack; - } -} - -class ServerKeyedAESCrypto implements ISecretStorageCrypto { - - private serverKey: Uint8Array | undefined; - - /** - * Gets whether the algorithm is supported; requires a secure context - */ - static supported() { - return !!crypto.subtle; - } - - constructor(private readonly authEndpoint: string) { } - - async seal(data: string): Promise { - // Get a new key and IV on every change, to avoid the risk of reusing the same key and IV pair with AES-GCM - // (see also: https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams#properties) - const iv = mainWindow.crypto.getRandomValues(new Uint8Array(AESConstants.IV_LENGTH)); - // crypto.getRandomValues isn't a good-enough PRNG to generate crypto keys, so we need to use crypto.subtle.generateKey and export the key instead - const clientKeyObj = await mainWindow.crypto.subtle.generateKey( - { name: AESConstants.ALGORITHM as const, length: AESConstants.KEY_LENGTH as const }, - true, - ['encrypt', 'decrypt'] - ); - - const clientKey = new Uint8Array(await mainWindow.crypto.subtle.exportKey('raw', clientKeyObj)); - const key = await this.getKey(clientKey); - const dataUint8Array = new TextEncoder().encode(data); - const cipherText: ArrayBuffer = await mainWindow.crypto.subtle.encrypt( - { name: AESConstants.ALGORITHM as const, iv }, - key, - dataUint8Array - ); - - // Base64 encode the result and store the ciphertext, the key, and the IV in localStorage - // Note that the clientKey and IV don't need to be secret - const result = new Uint8Array([...clientKey, ...iv, ...new Uint8Array(cipherText)]); - return encodeBase64(VSBuffer.wrap(result)); - } - - async unseal(data: string): Promise { - // encrypted should contain, in order: the key (32-byte), the IV for AES-GCM (12-byte) and the ciphertext (which has the GCM auth tag at the end) - // Minimum length must be 44 (key+IV length) + 16 bytes (1 block encrypted with AES - regardless of key size) - const dataUint8Array = decodeBase64(data); - - if (dataUint8Array.byteLength < 60) { - throw Error('Invalid length for the value for credentials.crypto'); - } - - const keyLength = AESConstants.KEY_LENGTH / 8; - const clientKey = dataUint8Array.slice(0, keyLength); - const iv = dataUint8Array.slice(keyLength, keyLength + AESConstants.IV_LENGTH); - const cipherText = dataUint8Array.slice(keyLength + AESConstants.IV_LENGTH); - - // Do the decryption and parse the result as JSON - const key = await this.getKey(clientKey.buffer); - const decrypted = await mainWindow.crypto.subtle.decrypt( - { name: AESConstants.ALGORITHM as const, iv: iv.buffer as Uint8Array }, - key, - cipherText.buffer as Uint8Array - ); - - return new TextDecoder().decode(new Uint8Array(decrypted)); - } - - /** - * Given a clientKey, returns the CryptoKey object that is used to encrypt/decrypt the data. - * The actual key is (clientKey XOR serverKey) - */ - private async getKey(clientKey: Uint8Array): Promise { - if (!clientKey || clientKey.byteLength !== AESConstants.KEY_LENGTH / 8) { - throw Error('Invalid length for clientKey'); - } - - const serverKey = await this.getServerKeyPart(); - const keyData = new Uint8Array(AESConstants.KEY_LENGTH / 8); - - for (let i = 0; i < keyData.byteLength; i++) { - keyData[i] = clientKey[i] ^ serverKey[i]; - } - - return mainWindow.crypto.subtle.importKey( - 'raw', - keyData, - { - name: AESConstants.ALGORITHM as const, - length: AESConstants.KEY_LENGTH as const, - }, - true, - ['encrypt', 'decrypt'] - ); - } - - private async getServerKeyPart(): Promise { - if (this.serverKey) { - return this.serverKey; - } - - let attempt = 0; - let lastError: Error | undefined; - - while (attempt <= 3) { - try { - const res = await fetch(this.authEndpoint, { credentials: 'include', method: 'POST' }); - if (!res.ok) { - throw new Error(res.statusText); - } - - const serverKey = new Uint8Array(await res.arrayBuffer()); - if (serverKey.byteLength !== AESConstants.KEY_LENGTH / 8) { - throw Error(`The key retrieved by the server is not ${AESConstants.KEY_LENGTH} bit long.`); - } - - this.serverKey = serverKey; - - return this.serverKey; - } catch (e) { - lastError = e instanceof Error ? e : new Error(String(e)); - attempt++; - - // exponential backoff - await new Promise(resolve => setTimeout(resolve, attempt * attempt * 100)); - } - } - - if (lastError) { - throw new NetworkError(lastError); - } - - throw new Error('Unknown error'); - } -} - -export class LocalStorageSecretStorageProvider implements ISecretStorageProvider { - - private readonly storageKey = 'secrets.provider'; - - private secretsPromise: Promise>; - - type: 'in-memory' | 'persisted' | 'unknown' = 'persisted'; - - constructor( - private readonly crypto: ISecretStorageCrypto, - ) { - this.secretsPromise = this.load(); - } - - private async load(): Promise> { - const record = this.loadAuthSessionFromElement(); - - const encrypted = localStorage.getItem(this.storageKey); - if (encrypted) { - try { - const decrypted = JSON.parse(await this.crypto.unseal(encrypted)); - - return { ...record, ...decrypted }; - } catch (err) { - // TODO: send telemetry - console.error('Failed to decrypt secrets from localStorage', err); - if (!(err instanceof NetworkError)) { - localStorage.removeItem(this.storageKey); - } - } - } - - return record; - } - - private loadAuthSessionFromElement(): Record { - let authSessionInfo: (AuthenticationSessionInfo & { scopes: string[][] }) | undefined; - // eslint-disable-next-line no-restricted-syntax - const authSessionElement = mainWindow.document.getElementById('vscode-workbench-auth-session'); - const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined; - if (authSessionElementAttribute) { - try { - authSessionInfo = JSON.parse(authSessionElementAttribute); - } catch (error) { /* Invalid session is passed. Ignore. */ } - } - - if (!authSessionInfo) { - return {}; - } - - const record: Record = {}; - - // Settings Sync Entry - record[`${product.urlProtocol}.loginAccount`] = JSON.stringify(authSessionInfo); - - // Auth extension Entry - if (authSessionInfo.providerId !== 'github') { - console.error(`Unexpected auth provider: ${authSessionInfo.providerId}. Expected 'github'.`); - return record; - } - - const authAccount = JSON.stringify({ extensionId: 'vscode.github-authentication', key: 'github.auth' }); - record[authAccount] = JSON.stringify(authSessionInfo.scopes.map(scopes => ({ - id: authSessionInfo.id, - scopes, - accessToken: authSessionInfo.accessToken - }))); - - return record; - } - - async get(key: string): Promise { - const secrets = await this.secretsPromise; - - return secrets[key]; - } - - async set(key: string, value: string): Promise { - const secrets = await this.secretsPromise; - secrets[key] = value; - this.secretsPromise = Promise.resolve(secrets); - this.save(); - } - - async delete(key: string): Promise { - const secrets = await this.secretsPromise; - delete secrets[key]; - this.secretsPromise = Promise.resolve(secrets); - this.save(); - } - - async keys(): Promise { - const secrets = await this.secretsPromise; - return Object.keys(secrets) || []; - } - - private async save(): Promise { - try { - const encrypted = await this.crypto.seal(JSON.stringify(await this.secretsPromise)); - localStorage.setItem(this.storageKey, encrypted); - } catch (err) { - console.error(err); - } - } -} - -class LocalStorageURLCallbackProvider extends Disposable implements IURLCallbackProvider { - - private static REQUEST_ID = 0; - - private static QUERY_KEYS: ('scheme' | 'authority' | 'path' | 'query' | 'fragment')[] = [ - 'scheme', - 'authority', - 'path', - 'query', - 'fragment' - ]; - - private readonly _onCallback = this._register(new Emitter()); - readonly onCallback = this._onCallback.event; - - private pendingCallbacks = new Set(); - private lastTimeChecked = Date.now(); - private checkCallbacksTimeout: Timeout | undefined = undefined; - private onDidChangeLocalStorageDisposable: IDisposable | undefined; - - constructor(private readonly _callbackRoute: string) { - super(); - } - - create(options: Partial = {}): URI { - const id = ++LocalStorageURLCallbackProvider.REQUEST_ID; - const queryParams: string[] = [`vscode-reqid=${id}`]; - - for (const key of LocalStorageURLCallbackProvider.QUERY_KEYS) { - const value = options[key]; - - if (value) { - queryParams.push(`vscode-${key}=${encodeURIComponent(value)}`); +import { URI } from '../../../base/common/uri.js'; +import { + IWorkbenchConstructionOptions, + IWorkspace, + // IWorkspaceProvider, +} from '../../../workbench/browser/web.api.js'; +import { SecretStorageProvider } from '../workbench/membrane.js'; +declare const window: Window & { + product?: Writeable; + SENTRY_REPORT_ISSUE?: (params: { + source?: string; + message?: string; + context?: unknown; + }) => void; + vscodeTargetContainer?: HTMLElement | null; + completeInitialization?: () => void; + SENTRY_CAPTURE_EXCEPTION?: (error: Error) => void; + extensionToGazePort?: MessagePort; +}; +type Writeable = { -readonly [P in keyof T]: T[P] }; + + +(async function () { + // create workbench + let config: Writeable; + + if (window.product) { + config = window.product; + } else { + const result = await fetch('/product.json'); + config = await result.json(); + } + + // Forward the MessagePort to the extension so it can directly talk to gaze + if (window.extensionToGazePort) { + config.messagePorts = new Map([ + ['membrane.membrane', window.extensionToGazePort], + ]); + delete window.extensionToGazePort; + } + + + const isHttps = window.location.protocol === 'https:'; + const isDev = window.location.hostname === 'localhost'; + const extensionUrl = { + authority: window.location.host, + scheme: isHttps ? 'https' : 'http', + path: isDev ? '/membrane-dev' : '/membrane', + }; + + config.additionalBuiltinExtensions = [URI.revive(extensionUrl)]; + + config.workspaceProvider = { + // IMPORTANT: this filename must match the filename used in `memfs.ts`. + // TODO: Somehow use product.json to configure that globally + workspace: { workspaceUri: URI.parse('memfs:/membrane.code-workspace') }, + payload: { + 'skipReleaseNotes': 'true', + 'skipWelcome': 'true', + }, + trusted: true, + open: async ( + _workspace: IWorkspace, + _options?: { reuse?: boolean; payload?: object } + ) => { + return true; + }, + }; + + config.secretStorageProvider = SecretStorageProvider.getInstance(); + + config.commands = [ + // Used to refresh the page from the extension when a new version of the IDE is known to exist. + { id: 'membrane.refreshPage', handler: () => window.location.reload() }, + // Invoked when the navigator finishes loading + { + id: 'membrane.completeInitialization', handler: () => window.completeInitialization?.() + }, + // For product tour, emit an event to advance to the next step + { + id: 'membrane.advanceTour', handler: (...args: unknown[]) => { + const cmdArgs = args[0] as { trigger: string }; + window.dispatchEvent(new Event(`tour:${cmdArgs.trigger}`)); } - } - - // TODO@joao remove eventually - // https://github.com/microsoft/vscode-dev/issues/62 - // https://github.com/microsoft/vscode/blob/159479eb5ae451a66b5dac3c12d564f32f454796/extensions/github-authentication/src/githubServer.ts#L50-L50 - if (!(options.authority === 'vscode.github-authentication' && options.path === '/dummy')) { - const key = `vscode-web.url-callbacks[${id}]`; - localStorage.removeItem(key); - - this.pendingCallbacks.add(id); - this.startListening(); - } - - return URI.parse(mainWindow.location.href).with({ path: this._callbackRoute, query: queryParams.join('&') }); - } - - private startListening(): void { - if (this.onDidChangeLocalStorageDisposable) { - return; - } - - this.onDidChangeLocalStorageDisposable = addDisposableListener(mainWindow, 'storage', () => this.onDidChangeLocalStorage()); - } - - private stopListening(): void { - this.onDidChangeLocalStorageDisposable?.dispose(); - this.onDidChangeLocalStorageDisposable = undefined; - } - - // this fires every time local storage changes, but we - // don't want to check more often than once a second - private async onDidChangeLocalStorage(): Promise { - const ellapsed = Date.now() - this.lastTimeChecked; - - if (ellapsed > 1000) { - this.checkCallbacks(); - } else if (this.checkCallbacksTimeout === undefined) { - this.checkCallbacksTimeout = setTimeout(() => { - this.checkCallbacksTimeout = undefined; - this.checkCallbacks(); - }, 1000 - ellapsed); - } - } - - private checkCallbacks(): void { - let pendingCallbacks: Set | undefined; - - for (const id of this.pendingCallbacks) { - const key = `vscode-web.url-callbacks[${id}]`; - const result = localStorage.getItem(key); - - if (result !== null) { - try { - this._onCallback.fire(URI.revive(JSON.parse(result))); - } catch (error) { - console.error(error); - } - - pendingCallbacks = pendingCallbacks ?? new Set(this.pendingCallbacks); - pendingCallbacks.delete(id); - localStorage.removeItem(key); - } - } - - if (pendingCallbacks) { - this.pendingCallbacks = pendingCallbacks; - - if (this.pendingCallbacks.size === 0) { - this.stopListening(); - } - } - - this.lastTimeChecked = Date.now(); - } -} - -class WorkspaceProvider implements IWorkspaceProvider { - - private static QUERY_PARAM_EMPTY_WINDOW = 'ew'; - private static QUERY_PARAM_FOLDER = 'folder'; - private static QUERY_PARAM_WORKSPACE = 'workspace'; - - private static QUERY_PARAM_PAYLOAD = 'payload'; - - static create(config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents }) { - let foundWorkspace = false; - let workspace: IWorkspace; - let payload = Object.create(null); - - const query = new URL(document.location.href).searchParams; - query.forEach((value, key) => { - switch (key) { - - // Folder - case WorkspaceProvider.QUERY_PARAM_FOLDER: - if (config.remoteAuthority && value.startsWith(posix.sep)) { - // when connected to a remote and having a value - // that is a path (begins with a `/`), assume this - // is a vscode-remote resource as simplified URL. - workspace = { folderUri: URI.from({ scheme: Schemas.vscodeRemote, path: value, authority: config.remoteAuthority }) }; - } else { - workspace = { folderUri: URI.parse(value) }; - } - foundWorkspace = true; - break; - - // Workspace - case WorkspaceProvider.QUERY_PARAM_WORKSPACE: - if (config.remoteAuthority && value.startsWith(posix.sep)) { - // when connected to a remote and having a value - // that is a path (begins with a `/`), assume this - // is a vscode-remote resource as simplified URL. - workspace = { workspaceUri: URI.from({ scheme: Schemas.vscodeRemote, path: value, authority: config.remoteAuthority }) }; - } else { - workspace = { workspaceUri: URI.parse(value) }; - } - foundWorkspace = true; - break; - - // Empty - case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: - workspace = undefined; - foundWorkspace = true; - break; - - // Payload - case WorkspaceProvider.QUERY_PARAM_PAYLOAD: - try { - payload = parse(value); // use marshalling#parse() to revive potential URIs - } catch (error) { - console.error(error); // possible invalid JSON - } - break; - } - }); - - // If no workspace is provided through the URL, check for config - // attribute from server - if (!foundWorkspace) { - if (config.folderUri) { - workspace = { folderUri: URI.revive(config.folderUri) }; - } else if (config.workspaceUri) { - workspace = { workspaceUri: URI.revive(config.workspaceUri) }; - } - } - - return new WorkspaceProvider(workspace, payload, config); - } - - readonly trusted = true; - - private constructor( - readonly workspace: IWorkspace, - readonly payload: object, - private readonly config: IWorkbenchConstructionOptions - ) { - } - - async open(workspace: IWorkspace, options?: { reuse?: boolean; payload?: object }): Promise { - if (options?.reuse && !options.payload && this.isSame(this.workspace, workspace)) { - return true; // return early if workspace and environment is not changing and we are reusing window - } - - const targetHref = this.createTargetUrl(workspace, options); - if (targetHref) { - if (options?.reuse) { - mainWindow.location.href = targetHref; + }, + // For extension panels to bubble up errors + { + id: 'membrane.reportError', + handler: (...args: unknown[]) => { + const cmdArgs = args[0] as { error: { message: string; stack?: string } }; + const error = new Error(cmdArgs.error.message); + error.stack = cmdArgs.error.stack; + window.SENTRY_CAPTURE_EXCEPTION?.(error); + }, + }, + { + id: 'membrane.reportIssue', + handler: (...args: unknown[]) => { + const cmdArgs = args[0] as { source?: string; message?: string; context?: unknown }; + window.SENTRY_REPORT_ISSUE?.({ + source: cmdArgs.source, + message: cmdArgs.message, + context: cmdArgs.context + }); + }, + }, + { + id: 'membrane.extensionToGaze', + handler: (response) => { + window.dispatchEvent( + new CustomEvent('extensionToGaze', { + detail: response, + }), + ); return true; - } else { - let result; - if (isStandalone()) { - result = mainWindow.open(targetHref, '_blank', 'toolbar=no'); // ensures to open another 'standalone' window! - } else { - result = mainWindow.open(targetHref); - } - - return !!result; - } - } - - return false; - } - - private createTargetUrl(workspace: IWorkspace, options?: { reuse?: boolean; payload?: object }): string | undefined { - - // Empty - let targetHref: string | undefined = undefined; - if (!workspace) { - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW}=true`; - } - - // Folder - else if (isFolderToOpen(workspace)) { - const queryParamFolder = this.encodeWorkspacePath(workspace.folderUri); - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${queryParamFolder}`; - } - - // Workspace - else if (isWorkspaceToOpen(workspace)) { - const queryParamWorkspace = this.encodeWorkspacePath(workspace.workspaceUri); - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${queryParamWorkspace}`; - } - - // Append payload if any - if (options?.payload) { - targetHref += `&${WorkspaceProvider.QUERY_PARAM_PAYLOAD}=${encodeURIComponent(JSON.stringify(options.payload))}`; - } - - return targetHref; - } - - private encodeWorkspacePath(uri: URI): string { - if (this.config.remoteAuthority && uri.scheme === Schemas.vscodeRemote) { - - // when connected to a remote and having a folder - // or workspace for that remote, only use the path - // as query value to form shorter, nicer URLs. - // however, we still need to `encodeURIComponent` - // to ensure to preserve special characters, such - // as `+` in the path. - - return encodeURIComponent(`${posix.sep}${ltrim(uri.path, posix.sep)}`).replaceAll('%2F', '/'); - } - - return encodeURIComponent(uri.toString(true)); - } - - private isSame(workspaceA: IWorkspace, workspaceB: IWorkspace): boolean { - if (!workspaceA || !workspaceB) { - return workspaceA === workspaceB; // both empty - } - - if (isFolderToOpen(workspaceA) && isFolderToOpen(workspaceB)) { - return isEqual(workspaceA.folderUri, workspaceB.folderUri); // same workspace - } - - if (isWorkspaceToOpen(workspaceA) && isWorkspaceToOpen(workspaceB)) { - return isEqual(workspaceA.workspaceUri, workspaceB.workspaceUri); // same workspace - } - - return false; - } - - hasRemote(): boolean { - if (this.workspace) { - if (isFolderToOpen(this.workspace)) { - return this.workspace.folderUri.scheme === Schemas.vscodeRemote; - } - - if (isWorkspaceToOpen(this.workspace)) { - return this.workspace.workspaceUri.scheme === Schemas.vscodeRemote; - } - } - - return true; - } -} - -function readCookie(name: string): string | undefined { - const cookies = document.cookie.split('; '); - for (const cookie of cookies) { - if (cookie.startsWith(name + '=')) { - return cookie.substring(name.length + 1); - } - } - - return undefined; -} + }, + }, + { + id: 'membrane.getLaunchParams', + handler: () => { + // eslint-disable-next-line no-restricted-syntax + const meta = document.querySelector('meta[name="membrane-launch-params"]') as HTMLMetaElement; + return meta?.content ?? ''; + }, + }, + ]; -(function () { + (config as Writeable & { homeIndicator?: { href: string; icon: string; title: string } }).homeIndicator = { + href: window.location.origin, + icon: 'home', + title: 'Membrane Home', + }; + + // MEMBRANE: Configure workbench settings + config.defaultLayout = { + ...config.defaultLayout, + views: [ + ...(config.defaultLayout?.views || []), + ], + layout: { + ...(config.defaultLayout?.layout || {}), + }, + }; + + // MEMBRANE: Configure editor settings + config.configurationDefaults = { + ...config.configurationDefaults, + 'window.commandCenter': false, // Hide command center + }; - // Find config by checking for DOM // eslint-disable-next-line no-restricted-syntax - const configElement = mainWindow.document.getElementById('vscode-workbench-web-configuration'); - const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; - if (!configElement || !configElementAttribute) { - throw new Error('Missing web configuration element'); - } - const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute); - const secretStorageKeyPath = readCookie('vscode-secret-key-path'); - const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported() - ? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto(); - - // Create workbench - create(mainWindow.document.body, { - ...config, - windowIndicator: config.windowIndicator ?? { label: '$(remote)', tooltip: `${product.nameShort} Web` }, - settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined, - workspaceProvider: WorkspaceProvider.create(config), - urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), - secretStorageProvider: config.remoteAuthority && !secretStorageKeyPath - ? undefined /* with a remote without embedder-preferred storage, store on the remote */ - : new LocalStorageSecretStorageProvider(secretStorageCrypto), - }); -})(); + const domElement = window.vscodeTargetContainer || document.body; + create(domElement, config); +})(); \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css index 4a31db23d0e1e5..841ad00908b20d 100644 --- a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.css @@ -7,6 +7,6 @@ position: absolute; top: 0; left: 0; - height: 6px; - box-shadow: var(--vscode-scrollbar-shadow) 0 6px 6px -6px inset; + height: 10px; + box-shadow: var(--vscode-scrollbar-shadow) 0 6px 10px -8px inset; } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index a6e7322d41c186..61a9771bb7d02c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -6298,7 +6298,7 @@ export const EditorOptions = { { description: nls.localize('formatOnType', "Controls whether the editor should automatically format the line after typing.") } )), glyphMargin: register(new EditorBooleanOption( - EditorOption.glyphMargin, 'glyphMargin', true, + EditorOption.glyphMargin, 'glyphMargin', false, { description: nls.localize('glyphMargin', "Controls whether the editor should render the vertical glyph margin. Glyph margin is mostly used for debugging.") } )), gotoLocation: register(new EditorGoToLocation()), diff --git a/src/vs/editor/contrib/codelens/browser/codelensController.ts b/src/vs/editor/contrib/codelens/browser/codelensController.ts index 3a3877a266487d..7f3cc6cc960ab2 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensController.ts +++ b/src/vs/editor/contrib/codelens/browser/codelensController.ts @@ -52,8 +52,9 @@ export class CodeLensContribution implements IEditorContribution { @INotificationService private readonly _notificationService: INotificationService, @ICodeLensCache private readonly _codeLensCache: ICodeLensCache ) { - this._provideCodeLensDebounce = debounceService.for(_languageFeaturesService.codeLensProvider, 'CodeLensProvide', { min: 250 }); - this._resolveCodeLensesDebounce = debounceService.for(_languageFeaturesService.codeLensProvider, 'CodeLensResolve', { min: 250, salt: 'resolve' }); + /// MEMBRANE: reduce the debounce time to make our CodeLens appear faster + this._provideCodeLensDebounce = debounceService.for(_languageFeaturesService.codeLensProvider, 'CodeLensProvide', { min: 10 }); + this._resolveCodeLensesDebounce = debounceService.for(_languageFeaturesService.codeLensProvider, 'CodeLensResolve', { min: 10, salt: 'resolve' }); this._resolveCodeLensesScheduler = new RunOnceScheduler(() => this._resolveCodeLensesInViewport(), this._resolveCodeLensesDebounce.default()); this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange())); diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 8e85ad2cac5fae..e0f8823c282443 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -2,536 +2,107 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { DisposableStore, Disposable, IDisposable, MutableDisposable, combinedDisposable, toDisposable } from '../../../base/common/lifecycle.js'; -import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ITerminalDimensionsDto, ExtHostTerminalIdentifier, TerminalQuickFix, ITerminalCommandDto } from '../common/extHost.protocol.js'; +import { MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ExtHostTerminalIdentifier } from '../common/extHost.protocol.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; -import { URI } from '../../../base/common/uri.js'; -import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; -import { ILogService } from '../../../platform/log/common/log.js'; -import { IProcessProperty, IProcessReadyWindowsPty, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalOutputMatch, ITerminalOutputMatcher, ProcessPropertyType, TerminalExitReason, TerminalLocation, type IProcessPropertyMap } from '../../../platform/terminal/common/terminal.js'; -import { TerminalDataBufferer } from '../../../platform/terminal/common/terminalDataBuffering.js'; -import { ITerminalEditorService, ITerminalExternalLinkProvider, ITerminalGroupService, ITerminalInstance, ITerminalLink, ITerminalService } from '../../contrib/terminal/browser/terminal.js'; -import { TerminalProcessExtHostProxy } from '../../contrib/terminal/browser/terminalProcessExtHostProxy.js'; -import { IEnvironmentVariableService } from '../../contrib/terminal/common/environmentVariable.js'; -import { deserializeEnvironmentDescriptionMap, deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from '../../../platform/terminal/common/environmentVariableShared.js'; -import { IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy, ITerminalProfileResolverService, ITerminalProfileService } from '../../contrib/terminal/common/terminal.js'; -import { IRemoteAgentService } from '../../services/remote/common/remoteAgentService.js'; -import { OperatingSystem, OS } from '../../../base/common/platform.js'; -import { TerminalEditorLocationOptions } from 'vscode'; -import { Promises } from '../../../base/common/async.js'; +import { IProcessProperty, IProcessReadyWindowsPty, ITerminalOutputMatch, ITerminalOutputMatcher } from '../../../platform/terminal/common/terminal.js'; import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from '../../../platform/terminal/common/environmentVariable.js'; -import { ITerminalLinkProviderService } from '../../contrib/terminalContrib/links/browser/links.js'; -import { ITerminalQuickFixService, ITerminalQuickFix, TerminalQuickFixType } from '../../contrib/terminalContrib/quickFix/browser/quickFix.js'; -import { TerminalCapability } from '../../../platform/terminal/common/capabilities/capabilities.js'; -import { ITerminalCompletionService } from '../../contrib/terminalContrib/suggest/browser/terminalCompletionService.js'; -import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js'; -import { hasKey } from '../../../base/common/types.js'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) -export class MainThreadTerminalService extends Disposable implements MainThreadTerminalServiceShape { - - private readonly _proxy: ExtHostTerminalServiceShape; - - /** - * Stores a map from a temporary terminal id (a UUID generated on the extension host side) - * to a numeric terminal id (an id generated on the renderer side) - * This comes in play only when dealing with terminals created on the extension host side - */ - private readonly _extHostTerminals = new Map>(); - private readonly _terminalProcessProxies = new Map(); - private readonly _profileProviders = new Map(); - private readonly _completionProviders = new Map(); - private readonly _quickFixProviders = new Map(); - private readonly _dataEventTracker = this._register(new MutableDisposable()); - private readonly _sendCommandEventListener = this._register(new MutableDisposable()); - - /** - * A single shared terminal link provider for the exthost. When an ext registers a link - * provider, this is registered with the terminal on the renderer side and all links are - * provided through this, even from multiple ext link providers. Xterm should remove lower - * priority intersecting links itself. - */ - private readonly _linkProvider = this._register(new MutableDisposable()); - - private _os: OperatingSystem = OS; - - constructor( - _extHostContext: IExtHostContext, - @ITerminalService private readonly _terminalService: ITerminalService, - @ITerminalLinkProviderService private readonly _terminalLinkProviderService: ITerminalLinkProviderService, - @ITerminalQuickFixService private readonly _terminalQuickFixService: ITerminalQuickFixService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService, - @ILogService private readonly _logService: ILogService, - @ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, - @ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService, - @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, - @ITerminalCompletionService private readonly _terminalCompletionService: ITerminalCompletionService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - ) { - super(); - this._proxy = _extHostContext.getProxy(ExtHostContext.ExtHostTerminalService); - - // ITerminalService listeners - this._register(_terminalService.onDidCreateInstance((instance) => { - this._onTerminalOpened(instance); - this._onInstanceDimensionsChanged(instance); - })); - - this._register(_terminalService.onDidDisposeInstance(instance => this._onTerminalDisposed(instance))); - this._register(_terminalService.onAnyInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance))); - this._register(_terminalService.onDidChangeInstanceDimensions(instance => this._onInstanceDimensionsChanged(instance))); - this._register(_terminalService.onAnyInstanceMaximumDimensionsChange(instance => this._onInstanceMaximumDimensionsChanged(instance))); - this._register(_terminalService.onDidRequestStartExtensionTerminal(e => this._onRequestStartExtensionTerminal(e))); - this._register(_terminalService.onDidChangeActiveInstance(instance => this._onActiveTerminalChanged(instance ? instance.instanceId : null))); - this._register(_terminalService.onAnyInstanceTitleChange(instance => instance && this._onTitleChanged(instance.instanceId, instance.title))); - this._register(_terminalService.onAnyInstanceDataInput(instance => this._proxy.$acceptTerminalInteraction(instance.instanceId))); - this._register(_terminalService.onAnyInstanceSelectionChange(instance => this._proxy.$acceptTerminalSelection(instance.instanceId, instance.selection))); - this._register(_terminalService.onAnyInstanceShellTypeChanged(instance => this._onShellTypeChanged(instance.instanceId))); +export class MainThreadTerminalService implements MainThreadTerminalServiceShape { + constructor(_extHostContext: IExtHostContext) { } - // Set initial ext host state - for (const instance of this._terminalService.instances) { - this._onTerminalOpened(instance); - instance.processReady.then(() => this._onTerminalProcessIdReady(instance)); - if (instance.shellType) { - this._proxy.$acceptTerminalShellType(instance.instanceId, instance.shellType); - } - } - const activeInstance = this._terminalService.activeInstance; - if (activeInstance) { - this._proxy.$acceptActiveTerminalChanged(activeInstance.instanceId); - } - if (this._environmentVariableService.collections.size > 0) { - const collectionAsArray = [...this._environmentVariableService.collections.entries()]; - const serializedCollections: [string, ISerializableEnvironmentVariableCollection][] = collectionAsArray.map(e => { - return [e[0], serializeEnvironmentVariableCollection(e[1].map)]; - }); - this._proxy.$initEnvironmentVariableCollections(serializedCollections); - } - - this._store.add(toDisposable(() => { - for (const e of this._terminalProcessProxies.values()) { - e.proxy.dispose(); - e.store.dispose(); - } - this._terminalProcessProxies.clear(); - })); - - remoteAgentService.getEnvironment().then(async env => { - this._os = env?.os || OS; - this._updateDefaultProfile(); - }); - this._register(this._terminalProfileService.onDidChangeAvailableProfiles(() => this._updateDefaultProfile())); - - this._register(toDisposable(() => { - for (const provider of this._profileProviders.values()) { - provider.dispose(); - } - for (const provider of this._quickFixProviders.values()) { - provider.dispose(); - } - })); - } - - private async _updateDefaultProfile() { - const remoteAuthority = this._environmentService.remoteAuthority; - const defaultProfile = this._terminalProfileResolverService.getDefaultProfile({ remoteAuthority, os: this._os }); - const defaultAutomationProfile = this._terminalProfileResolverService.getDefaultProfile({ remoteAuthority, os: this._os, allowAutomationShell: true }); - this._proxy.$acceptDefaultProfile(...await Promise.all([defaultProfile, defaultAutomationProfile])); - } - - private async _getTerminalInstance(id: ExtHostTerminalIdentifier): Promise { - if (typeof id === 'string') { - return this._extHostTerminals.get(id); - } - return this._terminalService.getInstanceFromId(id); - } + public dispose(): void { } public async $createTerminal(extHostTerminalId: string, launchConfig: TerminalLaunchConfig): Promise { - const shellLaunchConfig: IShellLaunchConfig = { - name: launchConfig.name, - executable: launchConfig.shellPath, - args: launchConfig.shellArgs, - cwd: typeof launchConfig.cwd === 'string' ? launchConfig.cwd : URI.revive(launchConfig.cwd), - icon: launchConfig.icon, - color: launchConfig.color, - initialText: launchConfig.initialText, - waitOnExit: launchConfig.waitOnExit, - ignoreConfigurationCwd: true, - env: launchConfig.env, - strictEnv: launchConfig.strictEnv, - hideFromUser: launchConfig.hideFromUser, - customPtyImplementation: launchConfig.isExtensionCustomPtyTerminal - ? (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService) - : undefined, - extHostTerminalId, - forceShellIntegration: launchConfig.forceShellIntegration, - isFeatureTerminal: launchConfig.isFeatureTerminal, - isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal, - useShellEnvironment: launchConfig.useShellEnvironment, - isTransient: launchConfig.isTransient, - shellIntegrationNonce: launchConfig.shellIntegrationNonce - }; - const terminal = Promises.withAsyncBody(async r => { - const terminal = await this._terminalService.createTerminal({ - config: shellLaunchConfig, - location: await this._deserializeParentTerminal(launchConfig.location) - }); - r(terminal); - }); - this._extHostTerminals.set(extHostTerminalId, terminal); - const terminalInstance = await terminal; - this._register(terminalInstance.onDisposed(() => { - this._extHostTerminals.delete(extHostTerminalId); - })); - } - - private async _deserializeParentTerminal(location?: TerminalLocation | TerminalEditorLocationOptions | { parentTerminal: ExtHostTerminalIdentifier } | { splitActiveTerminal: boolean; location?: TerminalLocation }): Promise { - if (typeof location === 'object' && hasKey(location, { parentTerminal: true })) { - const parentTerminal = await this._extHostTerminals.get(location.parentTerminal.toString()); - return parentTerminal ? { parentTerminal } : undefined; - } - return location; + throw new Error('Unsupported'); } public async $show(id: ExtHostTerminalIdentifier, preserveFocus: boolean): Promise { - const terminalInstance = await this._getTerminalInstance(id); - if (terminalInstance) { - this._terminalService.setActiveInstance(terminalInstance); - if (terminalInstance.target === TerminalLocation.Editor) { - await this._terminalEditorService.revealActiveEditor(preserveFocus); - } else { - await this._terminalGroupService.showPanel(!preserveFocus); - } - } + throw new Error('Unsupported'); } public async $hide(id: ExtHostTerminalIdentifier): Promise { - const instanceToHide = await this._getTerminalInstance(id); - const activeInstance = this._terminalService.activeInstance; - if (activeInstance && activeInstance.instanceId === instanceToHide?.instanceId && activeInstance.target !== TerminalLocation.Editor) { - this._terminalGroupService.hidePanel(); - } + throw new Error('Unsupported'); } public async $dispose(id: ExtHostTerminalIdentifier): Promise { - (await this._getTerminalInstance(id))?.dispose(TerminalExitReason.Extension); + throw new Error('Unsupported'); } public async $sendText(id: ExtHostTerminalIdentifier, text: string, shouldExecute: boolean): Promise { - const instance = await this._getTerminalInstance(id); - await instance?.sendText(text, shouldExecute); + throw new Error('Unsupported'); } public $sendProcessExit(terminalId: number, exitCode: number | undefined): void { - this._terminalProcessProxies.get(terminalId)?.proxy.emitExit(exitCode); + throw new Error('Unsupported'); } public $startSendingDataEvents(): void { - if (!this._dataEventTracker.value) { - this._dataEventTracker.value = this._instantiationService.createInstance(TerminalDataEventTracker, (id, data) => { - this._onTerminalData(id, data); - }); - // Send initial events if they exist - for (const instance of this._terminalService.instances) { - for (const data of instance.initialDataEvents || []) { - this._onTerminalData(instance.instanceId, data); - } - } - } + throw new Error('Unsupported'); } public $stopSendingDataEvents(): void { - this._dataEventTracker.clear(); + throw new Error('Unsupported'); } public $startSendingCommandEvents(): void { - if (this._sendCommandEventListener.value) { - return; - } - - const multiplexer = this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.CommandDetection, capability => capability.onCommandFinished); - const sub = multiplexer.event(e => { - this._onDidExecuteCommand(e.instance.instanceId, { - commandLine: e.data.command, - // TODO: Convert to URI if possible - cwd: e.data.cwd, - exitCode: e.data.exitCode, - output: e.data.getOutput() - }); - }); - this._sendCommandEventListener.value = combinedDisposable(multiplexer, sub); + throw new Error('Unsupported'); } public $stopSendingCommandEvents(): void { - this._sendCommandEventListener.clear(); + throw new Error('Unsupported'); } public $startLinkProvider(): void { - this._linkProvider.value = this._terminalLinkProviderService.registerLinkProvider(new ExtensionTerminalLinkProvider(this._proxy)); + throw new Error('Unsupported'); } public $stopLinkProvider(): void { - this._linkProvider.clear(); + throw new Error('Unsupported'); } public $registerProcessSupport(isSupported: boolean): void { - this._terminalService.registerProcessSupport(isSupported); - } - - public $registerCompletionProvider(id: string, extensionIdentifier: string, ...triggerCharacters: string[]): void { - this._completionProviders.set(id, this._terminalCompletionService.registerTerminalCompletionProvider(extensionIdentifier, id, { - id, - provideCompletions: async (commandLine, cursorIndex, token) => { - const completions = await this._proxy.$provideTerminalCompletions(id, { commandLine, cursorIndex }, token); - if (!completions) { - return undefined; - } - if (completions.resourceOptions) { - const { cwd, globPattern, ...rest } = completions.resourceOptions; - return { - items: completions.items?.map(c => ({ - provider: `ext:${id}`, - ...c, - })), - resourceOptions: { - ...rest, - cwd, - globPattern - } - }; - } - return completions.items?.map(c => ({ - provider: `ext:${id}`, - ...c, - })); - } - }, ...triggerCharacters)); - } - - public $unregisterCompletionProvider(id: string): void { - this._completionProviders.get(id)?.dispose(); - this._completionProviders.delete(id); + // Empty } public $registerProfileProvider(id: string, extensionIdentifier: string): void { - // Proxy profile provider requests through the extension host - this._profileProviders.set(id, this._terminalProfileService.registerTerminalProfileProvider(extensionIdentifier, id, { - createContributedTerminalProfile: async (options) => { - return this._proxy.$createContributedProfileTerminal(id, options); - } - })); + throw new Error('Unsupported'); } public $unregisterProfileProvider(id: string): void { - this._profileProviders.get(id)?.dispose(); - this._profileProviders.delete(id); + throw new Error('Unsupported'); } public async $registerQuickFixProvider(id: string, extensionId: string): Promise { - this._quickFixProviders.set(id, this._terminalQuickFixService.registerQuickFixProvider(id, { - provideTerminalQuickFixes: async (terminalCommand, lines, options, token) => { - if (token.isCancellationRequested) { - return; - } - if (options.outputMatcher?.length && options.outputMatcher.length > 40) { - options.outputMatcher.length = 40; - this._logService.warn('Cannot exceed output matcher length of 40'); - } - const commandLineMatch = terminalCommand.command.match(options.commandLineMatcher); - if (!commandLineMatch || !lines) { - return; - } - const outputMatcher = options.outputMatcher; - let outputMatch; - if (outputMatcher) { - outputMatch = getOutputMatchForLines(lines, outputMatcher); - } - if (!outputMatch) { - return; - } - const matchResult = { commandLineMatch, outputMatch, commandLine: terminalCommand.command }; - - if (matchResult) { - const result = await this._proxy.$provideTerminalQuickFixes(id, matchResult, token); - if (result && Array.isArray(result)) { - return result.map(r => parseQuickFix(id, extensionId, r)); - } else if (result) { - return parseQuickFix(id, extensionId, result); - } - } - return; - } - })); + throw new Error('Unsupported'); } public $unregisterQuickFixProvider(id: string): void { - this._quickFixProviders.get(id)?.dispose(); - this._quickFixProviders.delete(id); - } - - private _onActiveTerminalChanged(terminalId: number | null): void { - this._proxy.$acceptActiveTerminalChanged(terminalId); - } - - private _onTerminalData(terminalId: number, data: string): void { - this._proxy.$acceptTerminalProcessData(terminalId, data); - } - - private _onDidExecuteCommand(terminalId: number, command: ITerminalCommandDto): void { - this._proxy.$acceptDidExecuteCommand(terminalId, command); + throw new Error('Unsupported'); } - private _onTitleChanged(terminalId: number, name: string): void { - this._proxy.$acceptTerminalTitleChange(terminalId, name); + public $registerCompletionProvider(id: string, extensionId: string): void { + throw new Error('Unsupported'); } - private _onShellTypeChanged(terminalId: number): void { - const terminalInstance = this._terminalService.getInstanceFromId(terminalId); - if (terminalInstance) { - this._proxy.$acceptTerminalShellType(terminalId, terminalInstance.shellType); - } - } - - private _onTerminalDisposed(terminalInstance: ITerminalInstance): void { - this._proxy.$acceptTerminalClosed(terminalInstance.instanceId, terminalInstance.exitCode, terminalInstance.exitReason ?? TerminalExitReason.Unknown); - const proxy = this._terminalProcessProxies.get(terminalInstance.instanceId); - if (proxy) { - proxy.proxy.dispose(); - proxy.store.dispose(); - this._terminalProcessProxies.delete(terminalInstance.instanceId); - } - } - - private _onTerminalOpened(terminalInstance: ITerminalInstance): void { - const extHostTerminalId = terminalInstance.shellLaunchConfig.extHostTerminalId; - const shellLaunchConfigDto: IShellLaunchConfigDto = { - name: terminalInstance.shellLaunchConfig.name, - executable: terminalInstance.shellLaunchConfig.executable, - args: terminalInstance.shellLaunchConfig.args, - cwd: terminalInstance.shellLaunchConfig.cwd, - env: terminalInstance.shellLaunchConfig.env, - hideFromUser: terminalInstance.shellLaunchConfig.hideFromUser, - tabActions: terminalInstance.shellLaunchConfig.tabActions - }; - this._proxy.$acceptTerminalOpened(terminalInstance.instanceId, extHostTerminalId, terminalInstance.title, shellLaunchConfigDto); - } - - private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void { - if (terminalInstance.processId === undefined) { - return; - } - this._proxy.$acceptTerminalProcessId(terminalInstance.instanceId, terminalInstance.processId); - } - - private _onInstanceDimensionsChanged(instance: ITerminalInstance): void { - this._proxy.$acceptTerminalDimensions(instance.instanceId, instance.cols, instance.rows); - } - - private _onInstanceMaximumDimensionsChanged(instance: ITerminalInstance): void { - this._proxy.$acceptTerminalMaximumDimensions(instance.instanceId, instance.maxCols, instance.maxRows); - } - - private _onRequestStartExtensionTerminal(request: IStartExtensionTerminalRequest): void { - const proxy = request.proxy; - const store = new DisposableStore(); - this._terminalProcessProxies.set(proxy.instanceId, { proxy, store }); - - // Note that onResize is not being listened to here as it needs to fire when max dimensions - // change, excluding the dimension override - const initialDimensions: ITerminalDimensionsDto | undefined = request.cols && request.rows ? { - columns: request.cols, - rows: request.rows - } : undefined; - - this._proxy.$startExtensionTerminal( - proxy.instanceId, - initialDimensions - ).then(request.callback); - - store.add(proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.instanceId, data))); - store.add(proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.instanceId, immediate))); - store.add(proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.instanceId))); - store.add(proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.instanceId))); + public $unregisterCompletionProvider(id: string): void { + throw new Error('Unsupported'); } public $sendProcessData(terminalId: number, data: string): void { - this._terminalProcessProxies.get(terminalId)?.proxy.emitData(data); + throw new Error('Unsupported'); } public $sendProcessReady(terminalId: number, pid: number, cwd: string, windowsPty: IProcessReadyWindowsPty | undefined): void { - this._terminalProcessProxies.get(terminalId)?.proxy.emitReady(pid, cwd, windowsPty); + throw new Error('Unsupported'); } public $sendProcessProperty(terminalId: number, property: IProcessProperty): void { - if (property.type === ProcessPropertyType.Title) { - const instance = this._terminalService.getInstanceFromId(terminalId); - instance?.rename(property.value as IProcessPropertyMap[ProcessPropertyType.Title]); - } - this._terminalProcessProxies.get(terminalId)?.proxy.emitProcessProperty(property); + throw new Error('Unsupported'); } $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined, descriptionMap: ISerializableEnvironmentDescriptionMap): void { - if (collection) { - const translatedCollection = { - persistent, - map: deserializeEnvironmentVariableCollection(collection), - descriptionMap: deserializeEnvironmentDescriptionMap(descriptionMap) - }; - this._environmentVariableService.set(extensionIdentifier, translatedCollection); - } else { - this._environmentVariableService.delete(extensionIdentifier); - } - } -} - -/** - * Encapsulates temporary tracking of data events from terminal instances, once disposed all - * listeners are removed. - */ -class TerminalDataEventTracker extends Disposable { - private readonly _bufferer: TerminalDataBufferer; - - constructor( - private readonly _callback: (id: number, data: string) => void, - @ITerminalService private readonly _terminalService: ITerminalService - ) { - super(); - - this._register(this._bufferer = new TerminalDataBufferer(this._callback)); - - for (const instance of this._terminalService.instances) { - this._registerInstance(instance); - } - this._register(this._terminalService.onDidCreateInstance(instance => this._registerInstance(instance))); - this._register(this._terminalService.onDidDisposeInstance(instance => this._bufferer.stopBuffering(instance.instanceId))); - } - - private _registerInstance(instance: ITerminalInstance): void { - // Buffer data events to reduce the amount of messages going to the extension host - this._register(this._bufferer.startBuffering(instance.instanceId, instance.onData)); - } -} - -class ExtensionTerminalLinkProvider implements ITerminalExternalLinkProvider { - constructor( - private readonly _proxy: ExtHostTerminalServiceShape - ) { - } - - async provideLinks(instance: ITerminalInstance, line: string): Promise { - const proxy = this._proxy; - const extHostLinks = await proxy.$provideLinks(instance.instanceId, line); - return extHostLinks.map(dto => ({ - id: dto.id, - startIndex: dto.startIndex, - length: dto.length, - label: dto.label, - activate: () => proxy.$activateLink(instance.instanceId, dto.id) - })); + throw new Error('Unsupported'); } } @@ -539,14 +110,3 @@ export function getOutputMatchForLines(lines: string[], outputMatcher: ITerminal const match: RegExpMatchArray | null | undefined = lines.join('\n').match(outputMatcher.lineMatcher); return match ? { regexMatch: match, outputLines: lines } : undefined; } - -function parseQuickFix(id: string, source: string, fix: TerminalQuickFix): ITerminalQuickFix { - let type = TerminalQuickFixType.TerminalCommand; - if (hasKey(fix, { uri: true })) { - fix.uri = URI.revive(fix.uri); - type = TerminalQuickFixType.Opener; - } else if (hasKey(fix, { id: true })) { - type = TerminalQuickFixType.VscodeCommand; - } - return { id, type, source, ...fix }; -} diff --git a/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts b/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts index a2f60755c61240..296f56505d1dfb 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts @@ -3,140 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../../base/common/event.js'; -import { Disposable, toDisposable, type IDisposable } from '../../../base/common/lifecycle.js'; -import { TerminalCapability, type ITerminalCommand } from '../../../platform/terminal/common/capabilities/capabilities.js'; -import { ExtHostContext, MainContext, type ExtHostTerminalShellIntegrationShape, type MainThreadTerminalShellIntegrationShape } from '../common/extHost.protocol.js'; -import { ITerminalService, type ITerminalInstance } from '../../contrib/terminal/browser/terminal.js'; +import { Disposable } from '../../../base/common/lifecycle.js'; +import { MainContext, type MainThreadTerminalShellIntegrationShape } from '../common/extHost.protocol.js'; +import { ITerminalService } from '../../contrib/terminal/browser/terminal.js'; import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js'; import { extHostNamedCustomer, type IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; -import { TerminalShellExecutionCommandLineConfidence } from '../common/extHostTypes.js'; import { IExtensionService } from '../../services/extensions/common/extensions.js'; @extHostNamedCustomer(MainContext.MainThreadTerminalShellIntegration) export class MainThreadTerminalShellIntegration extends Disposable implements MainThreadTerminalShellIntegrationShape { - private readonly _proxy: ExtHostTerminalShellIntegrationShape; - constructor( - extHostContext: IExtHostContext, - @ITerminalService private readonly _terminalService: ITerminalService, - @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, - @IExtensionService private readonly _extensionService: IExtensionService + _extHostContext: IExtHostContext, + @ITerminalService _terminalService: ITerminalService, + @IWorkbenchEnvironmentService _workbenchEnvironmentService: IWorkbenchEnvironmentService, + @IExtensionService _extensionService: IExtensionService ) { super(); - - this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalShellIntegration); - - const instanceDataListeners: Map = new Map(); - this._register(toDisposable(() => { - for (const listener of instanceDataListeners.values()) { - listener.dispose(); - } - })); - - // onDidChangeTerminalShellIntegration initial state - for (const terminal of this._terminalService.instances) { - const cmdDetection = terminal.capabilities.get(TerminalCapability.CommandDetection); - if (cmdDetection) { - this._enableShellIntegration(terminal); - } - } - - // onDidChangeTerminalShellIntegration via command detection - const onDidAddCommandDetection = this._store.add(this._terminalService.createOnInstanceEvent(instance => { - return Event.map( - instance.capabilities.onDidAddCommandDetectionCapability, - () => instance - ); - })).event; - this._store.add(onDidAddCommandDetection(e => this._enableShellIntegration(e))); - - // onDidChangeTerminalShellIntegration via cwd - const cwdChangeEvent = this._store.add(this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.CwdDetection, e => e.onDidChangeCwd)); - this._store.add(cwdChangeEvent.event(e => { - this._proxy.$cwdChange(e.instance.instanceId, e.data); - })); - - // onDidChangeTerminalShellIntegration via env - const envChangeEvent = this._store.add(this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.ShellEnvDetection, e => e.onDidChangeEnv)); - this._store.add(envChangeEvent.event(e => { - if (e.data.value && typeof e.data.value === 'object') { - const envValue = e.data.value as { [key: string]: string | undefined }; - - // Extract keys and values - const keysArr = Object.keys(envValue); - const valuesArr = Object.values(envValue); - this._proxy.$shellEnvChange(e.instance.instanceId, keysArr, valuesArr as string[], e.data.isTrusted); - } - })); - - // onDidStartTerminalShellExecution - const commandDetectionStartEvent = this._store.add(this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.CommandDetection, e => e.onCommandExecuted)); - let currentCommand: ITerminalCommand | undefined; - this._store.add(commandDetectionStartEvent.event(e => { - // Prevent duplicate events from being sent in case command detection double fires the - // event - if (e.data === currentCommand) { - return; - } - // String paths are not exposed in the extension API - currentCommand = e.data; - const instanceId = e.instance.instanceId; - this._proxy.$shellExecutionStart(instanceId, instanceSupportsExecuteCommandApi(e.instance), e.data.command, convertToExtHostCommandLineConfidence(e.data), e.data.isTrusted, e.data.cwd); - - // TerminalShellExecution.createDataStream - // Debounce events to reduce the message count - when this listener is disposed the events will be flushed - instanceDataListeners.get(instanceId)?.dispose(); - instanceDataListeners.set(instanceId, Event.accumulate(e.instance.onData, 50, this._store)(events => { - this._proxy.$shellExecutionData(instanceId, events.join('')); - })); - })); - - // onDidEndTerminalShellExecution - const commandDetectionEndEvent = this._store.add(this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.CommandDetection, e => e.onCommandFinished)); - this._store.add(commandDetectionEndEvent.event(e => { - currentCommand = undefined; - const instanceId = e.instance.instanceId; - instanceDataListeners.get(instanceId)?.dispose(); - // Shell integration C (executed) and D (command finished) sequences should always be in - // their own events, so send this immediately. This means that the D sequence will not - // be included as it's currently being parsed when the command finished event fires. - this._proxy.$shellExecutionEnd(instanceId, e.data.command, convertToExtHostCommandLineConfidence(e.data), e.data.isTrusted, e.data.exitCode); - })); - - // Clean up after dispose - this._store.add(this._terminalService.onDidDisposeInstance(e => this._proxy.$closeTerminal(e.instanceId))); + // No-op: Terminal shell integration is not supported in web/membrane environment } - $executeCommand(terminalId: number, commandLine: string): void { - this._terminalService.getInstanceFromId(terminalId)?.runCommand(commandLine, true); + $executeCommand(_terminalId: number, _commandLine: string): void { + // No-op } - - private _enableShellIntegration(instance: ITerminalInstance): void { - this._extensionService.activateByEvent('onTerminalShellIntegration:*'); - if (instance.shellType) { - this._extensionService.activateByEvent(`onTerminalShellIntegration:${instance.shellType}`); - } - this._proxy.$shellIntegrationChange(instance.instanceId, instanceSupportsExecuteCommandApi(instance)); - const cwdDetection = instance.capabilities.get(TerminalCapability.CwdDetection); - if (cwdDetection) { - this._proxy.$cwdChange(instance.instanceId, cwdDetection.getCwd()); - } - } -} - -function convertToExtHostCommandLineConfidence(command: ITerminalCommand): TerminalShellExecutionCommandLineConfidence { - switch (command.commandLineConfidence) { - case 'high': - return TerminalShellExecutionCommandLineConfidence.High; - case 'medium': - return TerminalShellExecutionCommandLineConfidence.Medium; - case 'low': - default: - return TerminalShellExecutionCommandLineConfidence.Low; - } -} - -function instanceSupportsExecuteCommandApi(instance: ITerminalInstance): boolean { - return instance.shellLaunchConfig.type !== 'Task'; } diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index c0ff40a33138bd..6eade48f658920 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -21,7 +21,10 @@ import { PaneCompositeRegistry, Extensions as ViewletExtensions } from '../../br import { CustomTreeView, TreeViewPane } from '../../browser/parts/views/treeView.js'; import { ViewPaneContainer } from '../../browser/parts/views/viewPaneContainer.js'; import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../common/contributions.js'; -import { ICustomViewDescriptor, IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer, Extensions as ViewContainerExtensions, ViewContainerLocation } from '../../common/views.js'; +import { + ICustomViewDescriptor, IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer, Extensions as ViewContainerExtensions, + ViewContainerLocation +} from '../../common/views.js'; import { ChatContextKeyExprs } from '../../contrib/chat/common/chatContextKeys.js'; import { AGENT_SESSIONS_VIEWLET_ID as CHAT_SESSIONS } from '../../contrib/chat/common/constants.js'; import { VIEWLET_ID as DEBUG } from '../../contrib/debug/common/debug.js'; @@ -314,28 +317,26 @@ class ViewsExtensionHandler implements IWorkbenchContribution { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); let activityBarOrder = CUSTOM_VIEWS_START_ORDER + viewContainersRegistry.all.filter(v => !!v.extensionId && viewContainersRegistry.getViewContainerLocation(v) === ViewContainerLocation.Sidebar).length; let panelOrder = 5 + viewContainersRegistry.all.filter(v => !!v.extensionId && viewContainersRegistry.getViewContainerLocation(v) === ViewContainerLocation.Panel).length + 1; - // offset by 100 because the chat view container used to have order 100 (now 1). Due to caching, we still need to account for the original order value - let auxiliaryBarOrder = 100 + viewContainersRegistry.all.filter(v => !!v.extensionId && viewContainersRegistry.getViewContainerLocation(v) === ViewContainerLocation.AuxiliaryBar).length + 1; for (const { value, collector, description } of extensionPoints) { Object.entries(value).forEach(([key, value]) => { if (!this.isValidViewsContainer(value, collector)) { return; } switch (key) { - case 'activitybar': + case 'activitybar': { activityBarOrder = this.registerCustomViewContainers(value, description, activityBarOrder, existingViewContainers, ViewContainerLocation.Sidebar); break; - case 'panel': + } + case 'panel': { panelOrder = this.registerCustomViewContainers(value, description, panelOrder, existingViewContainers, ViewContainerLocation.Panel); break; - case 'secondarySidebar': - auxiliaryBarOrder = this.registerCustomViewContainers(value, description, auxiliaryBarOrder, existingViewContainers, ViewContainerLocation.AuxiliaryBar); - break; + } } }); } } + private removeCustomViewContainers(extensionPoints: readonly IExtensionPointUser[]): void { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const removedExtensions: ExtensionIdentifierSet = extensionPoints.reduce((result, e) => { result.add(e.description.identifier); return result; }, new ExtensionIdentifierSet()); @@ -386,7 +387,6 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number { containers.forEach(descriptor => { const themeIcon = ThemeIcon.fromString(descriptor.icon); - const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); const id = `workbench.view.extension.${descriptor.id}`; const title = descriptor.title || id; @@ -455,6 +455,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution { const { value, collector } = extension; Object.entries(value).forEach(([key, value]) => { + // MEMBRANE: Skip explorer views since we removed the explorer container + if (key === 'explorer') { + return; + } + if (!this.isValidViewDescriptors(value, collector)) { return; } diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index 5fcd3c613d088f..a0fcd8489e24b4 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -79,6 +79,14 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { // `options.correlate` is always `false`. const excludeUncorrelatedEvents = false; + // MEMBRANE: The `scheme` of globPattern is ignored by `parsedPattern` + // which was causing events in `tmpfs://` to trigger watchers for + // `memfs://`. To fix it, we use this function that also checks schemes. + const scheme = typeof globPattern !== 'string' ? globPattern.baseUri.scheme : null; + const matchesPattern = (uri: vscode.Uri) => { + return (!scheme || uri.scheme === scheme) && parsedPattern(uri.fsPath); + }; + const subscription = dispatcher(events => { if (typeof events.session === 'number' && events.session !== this.session) { return; // ignore events from other file watchers that are in correlation mode @@ -91,7 +99,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { if (!options.ignoreCreateEvents) { for (const created of events.created) { const uri = URI.revive(created); - if (parsedPattern(uri.fsPath) && (!excludeOutOfWorkspaceEvents || workspace.getWorkspaceFolder(uri))) { + if (matchesPattern(uri) && (!excludeOutOfWorkspaceEvents || workspace.getWorkspaceFolder(uri))) { this._onDidCreate.fire(uri); } } @@ -99,7 +107,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { if (!options.ignoreChangeEvents) { for (const changed of events.changed) { const uri = URI.revive(changed); - if (parsedPattern(uri.fsPath) && (!excludeOutOfWorkspaceEvents || workspace.getWorkspaceFolder(uri))) { + if (matchesPattern(uri) && (!excludeOutOfWorkspaceEvents || workspace.getWorkspaceFolder(uri))) { this._onDidChange.fire(uri); } } @@ -107,7 +115,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { if (!options.ignoreDeleteEvents) { for (const deleted of events.deleted) { const uri = URI.revive(deleted); - if (parsedPattern(uri.fsPath) && (!excludeOutOfWorkspaceEvents || workspace.getWorkspaceFolder(uri))) { + if (matchesPattern(uri) && (!excludeOutOfWorkspaceEvents || workspace.getWorkspaceFolder(uri))) { this._onDidDelete.fire(uri); } } diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index c7e94bcf411d52..843cec3205f3c2 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -23,7 +23,7 @@ import { IPaneCompositePartService } from '../../services/panecomposite/browser/ import { ToggleAuxiliaryBarAction } from '../parts/auxiliarybar/auxiliaryBarActions.js'; import { TogglePanelAction } from '../parts/panel/panelActions.js'; import { ICommandService } from '../../../platform/commands/common/commands.js'; -import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, IsMainWindowFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext, TitleBarStyleContext, IsAuxiliaryWindowContext } from '../../common/contextkeys.js'; +import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, IsMainWindowFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext, TitleBarStyleContext } from '../../common/contextkeys.js'; import { Codicon } from '../../../base/common/codicons.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; @@ -41,9 +41,9 @@ const menubarIcon = registerIcon('menuBar', Codicon.layoutMenubar, localize('men const activityBarLeftIcon = registerIcon('activity-bar-left', Codicon.layoutActivitybarLeft, localize('activityBarLeft', "Represents the activity bar in the left position")); const activityBarRightIcon = registerIcon('activity-bar-right', Codicon.layoutActivitybarRight, localize('activityBarRight', "Represents the activity bar in the right position")); const panelLeftIcon = registerIcon('panel-left', Codicon.layoutSidebarLeft, localize('panelLeft', "Represents a side bar in the left position")); -const panelLeftOffIcon = registerIcon('panel-left-off', Codicon.layoutSidebarLeftOff, localize('panelLeftOff', "Represents a side bar in the left position toggled off")); +// const panelLeftOffIcon = registerIcon('panel-left-off', Codicon.layoutSidebarLeftOff, localize('panelLeftOff', "Represents a side bar in the left position toggled off")); const panelRightIcon = registerIcon('panel-right', Codicon.layoutSidebarRight, localize('panelRight', "Represents side bar in the right position")); -const panelRightOffIcon = registerIcon('panel-right-off', Codicon.layoutSidebarRightOff, localize('panelRightOff', "Represents side bar in the right position toggled off")); +// const panelRightOffIcon = registerIcon('panel-right-off', Codicon.layoutSidebarRightOff, localize('panelRightOff', "Represents side bar in the right position toggled off")); const panelIcon = registerIcon('panel-bottom', Codicon.layoutPanel, localize('panelBottom', "Represents the bottom panel")); const statusBarIcon = registerIcon('statusBar', Codicon.layoutStatusbar, localize('statusBarIcon', "Represents the status bar")); @@ -169,16 +169,17 @@ export class ToggleSidebarPositionAction extends Action2 { registerAction2(ToggleSidebarPositionAction); const configureLayoutIcon = registerIcon('configure-layout-icon', Codicon.layout, localize('cofigureLayoutIcon', 'Icon represents workbench layout configuration.')); -MenuRegistry.appendMenuItem(MenuId.LayoutControlMenu, { - submenu: MenuId.LayoutControlMenuSubmenu, - title: localize('configureLayout', "Configure Layout"), - icon: configureLayoutIcon, - group: '1_workbench_layout', - when: ContextKeyExpr.and( - IsAuxiliaryWindowContext.negate(), - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'menu') - ) -}); +// MEMBRANE: Hide Configure Layout option +// MenuRegistry.appendMenuItem(MenuId.LayoutControlMenu, { +// submenu: MenuId.LayoutControlMenuSubmenu, +// title: localize('configureLayout', "Configure Layout"), +// icon: configureLayoutIcon, +// group: '1_workbench_layout', +// when: ContextKeyExpr.and( +// IsAuxiliaryWindowContext.negate(), +// ContextKeyExpr.equals('config.workbench.layoutControl.type', 'menu') +// ) +// }); MenuRegistry.appendMenuItems([{ @@ -346,46 +347,49 @@ MenuRegistry.appendMenuItems([ when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 2 } - }, { - id: MenuId.LayoutControlMenu, - item: { - group: '2_pane_toggles', - command: { - id: ToggleSidebarVisibilityAction.ID, - title: localize('toggleSideBar', "Toggle Primary Side Bar"), - icon: panelLeftOffIcon, - toggled: { condition: SideBarVisibleContext, icon: panelLeftIcon } - }, - when: ContextKeyExpr.and( - IsAuxiliaryWindowContext.negate(), - ContextKeyExpr.or( - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), - ContextKeyExpr.equals('config.workbench.sideBar.location', 'left') - ), - order: 0 - } - }, { - id: MenuId.LayoutControlMenu, - item: { - group: '2_pane_toggles', - command: { - id: ToggleSidebarVisibilityAction.ID, - title: localize('toggleSideBar', "Toggle Primary Side Bar"), - icon: panelRightOffIcon, - toggled: { condition: SideBarVisibleContext, icon: panelRightIcon } - }, - when: ContextKeyExpr.and( - IsAuxiliaryWindowContext.negate(), - ContextKeyExpr.or( - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), - ContextKeyExpr.equals('config.workbench.sideBar.location', 'right') - ), - order: 2 - } } ]); +// MEMBRANE: Hide Toggle Primary Side Bar +// , { +// id: MenuId.LayoutControlMenu, +// item: { +// group: '2_pane_toggles', +// command: { +// id: ToggleSidebarVisibilityAction.ID, +// title: localize('toggleSideBar', "Toggle Primary Side Bar"), +// icon: panelLeftOffIcon, +// toggled: { condition: SideBarVisibleContext, icon: panelLeftIcon } +// }, +// when: ContextKeyExpr.and( +// IsAuxiliaryWindowContext.negate(), +// ContextKeyExpr.or( +// ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), +// ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), +// ContextKeyExpr.equals('config.workbench.sideBar.location', 'left') +// ), +// order: 0 +// } +// }, { +// id: MenuId.LayoutControlMenu, +// item: { +// group: '2_pane_toggles', +// command: { +// id: ToggleSidebarVisibilityAction.ID, +// title: localize('toggleSideBar', "Toggle Primary Side Bar"), +// icon: panelRightOffIcon, +// toggled: { condition: SideBarVisibleContext, icon: panelRightIcon } +// }, +// when: ContextKeyExpr.and( +// IsAuxiliaryWindowContext.negate(), +// ContextKeyExpr.or( +// ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), +// ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), +// ContextKeyExpr.equals('config.workbench.sideBar.location', 'right') +// ), +// order: 2 +// } +// } +// ]); // --- Toggle Statusbar Visibility @@ -1449,20 +1453,21 @@ registerAction2(class CustomizeLayoutAction extends Action2 { title: localize2('customizeLayout', "Customize Layout..."), f1: true, icon: configureLayoutIcon, - menu: [ - { - id: MenuId.LayoutControlMenuSubmenu, - group: 'z_end', - }, - { - id: MenuId.LayoutControlMenu, - when: ContextKeyExpr.and( - IsAuxiliaryWindowContext.toNegated(), - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both') - ), - group: '1_layout' - } - ] + // MEMBRANE: Hide Customize Layout menu item + // menu: [ + // { + // id: MenuId.LayoutControlMenuSubmenu, + // group: 'z_end', + // }, + // { + // id: MenuId.LayoutControlMenu, + // when: ContextKeyExpr.and( + // IsAuxiliaryWindowContext.toNegated(), + // ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both') + // ), + // group: '1_layout' + // } + // ] }); } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 7d0fcca60ea059..731f38984c6f70 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -452,12 +452,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // The menu bar toggles the title bar in web because it does not need to be shown for window controls only if (isWeb && menuBarVisibility === 'toggle') { - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + // MEMBRANE: Always hide titlebar + this.workbenchGrid.setViewVisible(this.titleBarPartView, false); } // The menu bar toggles the title bar in full screen for toggle and classic settings else if (this.state.runtime.mainWindowFullscreen && (menuBarVisibility === 'toggle' || menuBarVisibility === 'classic')) { - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + // MEMBRANE: Always hide titlebar + this.workbenchGrid.setViewVisible(this.titleBarPartView, false); } // Move layout call to any time the menubar @@ -506,7 +508,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (hasCustomTitlebar(this.configurationService)) { // Propagate to grid - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + // MEMBRANE: Always hide titlebar + this.workbenchGrid.setViewVisible(this.titleBarPartView, false); // Indicate active window border this.updateWindowBorder(true); @@ -639,7 +642,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi resetLayout: Boolean(this.layoutOptions?.resetLayout) }); - this._register(this.stateModel.onDidChangeState(change => { + // MEMBRANE: hide sidebar and statusbar by default (overrides user preferences) + // These defaults are also set in `const LayoutStateKeys` in this same file + // Setting them here will reset them to hidden upon login if a user toggled them to visible + this.stateModel.setRuntimeValue(LayoutStateKeys.SIDEBAR_HIDDEN, true); + + this.stateModel.onDidChangeState(change => { if (change.key === LayoutStateKeys.ACTIVITYBAR_HIDDEN) { this.setActivityBarHidden(change.value as boolean); } @@ -661,7 +669,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } this.doUpdateLayoutConfiguration(); - })); + }); // Layout Initialization State const initialEditorsState = this.getInitialEditorsState(); @@ -702,6 +710,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi runtime: layoutRuntimeState, }; + // MEMBRANE: Always make search the active container in the primary sidebar + this.storageService.store(SidebarPart.activeViewletSettingsKey, 'workbench.view.search', StorageScope.WORKSPACE, StorageTarget.MACHINE); + // Sidebar View Container To Restore if (this.isVisible(Parts.SIDEBAR_PART)) { let viewContainerToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id); @@ -1470,7 +1481,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (!this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, true)) { - this.setActivityBarHidden(false); + this.setActivityBarHidden(true); } if (!this.stateModel.getRuntimeValue(LayoutStateKeys.STATUSBAR_HIDDEN, true)) { @@ -1570,6 +1581,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid = workbenchGrid; this.workbenchGrid.edgeSnapping = this.state.runtime.mainWindowFullscreen; + // MEMBRANE: Ensure titlebar is hidden from the start + this.workbenchGrid.setViewVisible(this.titleBarPartView, false); + for (const part of [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar, auxiliaryBarPart, bannerPart]) { this._register(part.onDidVisibilityChange(visible => { if (!this.inMaximizedAuxiliaryBarTransition) { @@ -2185,7 +2199,22 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid.setViewVisible(this.auxiliaryBarPartView, !hidden); } - setPartHidden(hidden: boolean, part: Parts): void { + setPartHidden(hidden: boolean, part: Exclude): void; + setPartHidden(hidden: boolean, part: Exclude, targetWindow: Window): void; + setPartHidden(hidden: boolean, part: Parts, targetWindow: Window = mainWindow): void { + // MEMBRANE: Force certain parts to always stay hidden + const MEMBRANE_FORCE_HIDDEN_PARTS = [ + // Parts.ACTIVITYBAR_PART, + // Parts.SIDEBAR_PART, + Parts.AUXILIARYBAR_PART, + Parts.PANEL_PART, + Parts.STATUSBAR_PART, + Parts.BANNER_PART + ]; + // If trying to show a part that should be force-hidden, ignore it + if (MEMBRANE_FORCE_HIDDEN_PARTS.includes(part) && !hidden) { + return; + } switch (part) { case Parts.ACTIVITYBAR_PART: return this.setActivityBarHidden(hidden); @@ -2219,14 +2248,16 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } updateMenubarVisibility(skipLayout: boolean): void { - const shouldShowTitleBar = shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled); + // MEMBRANE: Always hide titlebar + const shouldShowTitleBar = false; if (!skipLayout && this.workbenchGrid && shouldShowTitleBar !== this.isVisible(Parts.TITLEBAR_PART, mainWindow)) { this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowTitleBar); } } updateCustomTitleBarVisibility(): void { - const shouldShowTitleBar = shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled); + // MEMBRANE: Always hide titlebar + const shouldShowTitleBar = false; const titlebarVisible = this.isVisible(Parts.TITLEBAR_PART); if (shouldShowTitleBar !== titlebarVisible) { this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowTitleBar); @@ -2392,7 +2423,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid.moveView(this.bannerPartView, Sizing.Distribute, this.titleBarPartView, shouldBannerBeFirst ? Direction.Up : Direction.Down); } - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + // MEMBRANE: Always hide titlebar + this.workbenchGrid.setViewVisible(this.titleBarPartView, false); } private arrangeEditorNodes(nodes: { editor: ISerializedNode; sideBar?: ISerializedNode; auxiliaryBar?: ISerializedNode }, availableHeight: number, availableWidth: number): ISerializedNode { @@ -2523,7 +2555,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi type: 'leaf', data: { type: Parts.TITLEBAR_PART }, size: titleBarHeight, - visible: this.isVisible(Parts.TITLEBAR_PART, mainWindow) + // MEMBRANE: Always hide titlebar + visible: false }, { type: 'leaf', @@ -2733,13 +2766,15 @@ const LayoutStateKeys = { AUXILIARYBAR_EMPTY: new InitializationStateKey('auxiliaryBar.empty', StorageScope.PROFILE, StorageTarget.MACHINE, false), // Part Positions - SIDEBAR_POSITON: new RuntimeStateKey('sideBar.position', StorageScope.WORKSPACE, StorageTarget.MACHINE, Position.LEFT), + SIDEBAR_POSITON: new RuntimeStateKey('sideBar.position', StorageScope.WORKSPACE, StorageTarget.MACHINE, Position.RIGHT), PANEL_POSITION: new RuntimeStateKey('panel.position', StorageScope.WORKSPACE, StorageTarget.MACHINE, Position.BOTTOM), PANEL_ALIGNMENT: new RuntimeStateKey('panel.alignment', StorageScope.PROFILE, StorageTarget.USER, 'center'), // Part Visibility - ACTIVITYBAR_HIDDEN: new RuntimeStateKey('activityBar.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, false, true), - SIDEBAR_HIDDEN: new RuntimeStateKey('sideBar.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, false), + // MEMBRANE: Do not hide activitybar by default + ACTIVITYBAR_HIDDEN: new RuntimeStateKey('activityBar.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, true, true), + // MEMBRANE: Do not hide sidebar by default + SIDEBAR_HIDDEN: new RuntimeStateKey('sideBar.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, true), EDITOR_HIDDEN: new RuntimeStateKey('editor.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, false), PANEL_HIDDEN: new RuntimeStateKey('panel.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, true), AUXILIARYBAR_HIDDEN: new RuntimeStateKey('auxiliaryBar.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, true), diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 080b6871061ef5..1693f84cc8f999 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -11,16 +11,16 @@ import { Part } from '../../part.js'; import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts, Position } from '../../../services/layout/browser/layoutService.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { ToggleSidebarPositionAction, ToggleSidebarVisibilityAction } from '../../actions/layoutActions.js'; +import { ToggleSidebarPositionAction } from '../../actions/layoutActions.js'; import { IThemeService, IColorTheme, registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER } from '../../../common/theme.js'; import { activeContrastBorder, contrastBorder, focusBorder } from '../../../../platform/theme/common/colorRegistry.js'; -import { addDisposableListener, append, EventType, isAncestor, $, clearNode } from '../../../../base/browser/dom.js'; +import { addDisposableListener, append, EventType, $, clearNode } from '../../../../base/browser/dom.js'; import { assertReturnsDefined } from '../../../../base/common/types.js'; import { CustomMenubarControl } from '../titlebar/menubarControl.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { getMenuBarVisibility, MenuSettings } from '../../../../platform/window/common/window.js'; -import { IAction, Separator, SubmenuAction, toAction } from '../../../../base/common/actions.js'; +import { IAction, Separator, toAction } from '../../../../base/common/actions.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'; @@ -29,10 +29,9 @@ import { IPaneCompositePart } from '../paneCompositePart.js'; import { IPaneCompositeBarOptions, PaneCompositeBar } from '../paneCompositeBar.js'; import { GlobalCompositeBar } from '../globalCompositeBar.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; -import { Action2, IMenuService, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { getContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IViewDescriptorService, ViewContainerLocation, ViewContainerLocationToString } from '../../../common/views.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; @@ -214,7 +213,8 @@ export class ActivityBarCompositeBar extends PaneCompositeBar { @IContextKeyService contextKeyService: IContextKeyService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IMenuService private readonly menuService: IMenuService, + // MEMBRANE: Not used because we don't show menu option in context menu now + // @IMenuService private readonly menuService: IMenuService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, ) { super({ @@ -242,17 +242,18 @@ export class ActivityBarCompositeBar extends PaneCompositeBar { } private fillContextMenuActions(actions: IAction[], e?: MouseEvent | GestureEvent) { + // MEMBRANE: Don't show menu visibiity toggle // Menu - const menuBarVisibility = getMenuBarVisibility(this.configurationService); - if (menuBarVisibility === 'compact' || menuBarVisibility === 'hidden' || menuBarVisibility === 'toggle') { - actions.unshift(...[toAction({ id: 'toggleMenuVisibility', label: localize('menu', "Menu"), checked: menuBarVisibility === 'compact', run: () => this.configurationService.updateValue(MenuSettings.MenuBarVisibility, menuBarVisibility === 'compact' ? 'toggle' : 'compact') }), new Separator()]); - } + // const menuBarVisibility = getMenuBarVisibility(this.configurationService); + // if (menuBarVisibility === 'compact' || menuBarVisibility === 'hidden' || menuBarVisibility === 'toggle') { + // actions.unshift(...[toAction({ id: 'toggleMenuVisibility', label: localize('menu', "Menu"), checked: menuBarVisibility === 'compact', run: () => this.configurationService.updateValue('window.menuBarVisibility', menuBarVisibility === 'compact' ? 'toggle' : 'compact') }), new Separator()]); + // } - if (menuBarVisibility === 'compact' && this.menuBarContainer && e?.target) { - if (isAncestor(e.target as Node, this.menuBarContainer)) { - actions.unshift(...[toAction({ id: 'hideCompactMenu', label: localize('hideMenu', "Hide Menu"), run: () => this.configurationService.updateValue(MenuSettings.MenuBarVisibility, 'toggle') }), new Separator()]); - } - } + // if (menuBarVisibility === 'compact' && this.menuBarContainer && e?.target) { + // if (isAncestor(e.target as Node, this.menuBarContainer)) { + // actions.unshift(...[toAction({ id: 'hideCompactMenu', label: localize('hideMenu', "Hide Menu"), run: () => this.configurationService.updateValue('window.menuBarVisibility', 'toggle') }), new Separator()]); + // } + // } // Global Composite Bar if (this.globalCompositeBar) { @@ -366,19 +367,21 @@ export class ActivityBarCompositeBar extends PaneCompositeBar { super.layout(width, height); } + // MEMBRANE: Disable activity bar position menu getActivityBarContextMenuActions(): IAction[] { - const activityBarPositionMenu = this.menuService.getMenuActions(MenuId.ActivityBarPositionMenu, this.contextKeyService, { shouldForwardArgs: true, renderShortTitle: true }); - const positionActions = getContextMenuActions(activityBarPositionMenu).secondary; - const actions = [ - new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions), - toAction({ id: ToggleSidebarPositionAction.ID, label: ToggleSidebarPositionAction.getLabel(this.layoutService), run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarPositionAction().run(accessor)) }), + // const activityBarPositionMenu = this.menuService.createMenu(MenuId.ActivityBarPositionMenu, this.contextKeyService); + // const positionActions: IAction[] = []; + // createAndFillInContextMenuActions(activityBarPositionMenu, { shouldForwardArgs: true, renderShortTitle: true }, { primary: [], secondary: positionActions }); + // activityBarPositionMenu.dispose(); + // return [ + // new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions), + // toAction({ id: ToggleSidebarPositionAction.ID, label: ToggleSidebarPositionAction.getLabel(this.layoutService), run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarPositionAction().run(accessor)) }) + // ]; + + // Return only the sidebar position toggle, or empty array to remove all options + return [ + toAction({ id: ToggleSidebarPositionAction.ID, label: ToggleSidebarPositionAction.getLabel(this.layoutService), run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarPositionAction().run(accessor)) }) ]; - - if (this.part === Parts.SIDEBAR_PART) { - actions.push(toAction({ id: ToggleSidebarVisibilityAction.ID, label: ToggleSidebarVisibilityAction.LABEL, run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarVisibilityAction().run(accessor)) })); - } - - return actions; } } diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts index 15adc24dd63265..803799b9c059a0 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts @@ -10,7 +10,7 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { alert } from '../../../../base/browser/ui/aria/aria.js'; -import { AuxiliaryBarMaximizedContext, AuxiliaryBarVisibleContext, IsAuxiliaryWindowContext } from '../../../common/contextkeys.js'; +import { AuxiliaryBarMaximizedContext, AuxiliaryBarVisibleContext } from '../../../common/contextkeys.js'; import { ViewContainerLocation, ViewContainerLocationToString } from '../../../common/views.js'; import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts } from '../../../services/layout/browser/layoutService.js'; import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js'; @@ -23,10 +23,10 @@ import { closeIcon as panelCloseIcon } from '../panel/panelActions.js'; const maximizeIcon = registerIcon('auxiliarybar-maximize', Codicon.screenFull, localize('maximizeIcon', 'Icon to maximize the secondary side bar.')); const closeIcon = registerIcon('auxiliarybar-close', panelCloseIcon, localize('closeIcon', 'Icon to close the secondary side bar.')); -const auxiliaryBarRightIcon = registerIcon('auxiliarybar-right-layout-icon', Codicon.layoutSidebarRight, localize('toggleAuxiliaryIconRight', 'Icon to toggle the secondary side bar off in its right position.')); -const auxiliaryBarRightOffIcon = registerIcon('auxiliarybar-right-off-layout-icon', Codicon.layoutSidebarRightOff, localize('toggleAuxiliaryIconRightOn', 'Icon to toggle the secondary side bar on in its right position.')); -const auxiliaryBarLeftIcon = registerIcon('auxiliarybar-left-layout-icon', Codicon.layoutSidebarLeft, localize('toggleAuxiliaryIconLeft', 'Icon to toggle the secondary side bar in its left position.')); -const auxiliaryBarLeftOffIcon = registerIcon('auxiliarybar-left-off-layout-icon', Codicon.layoutSidebarLeftOff, localize('toggleAuxiliaryIconLeftOn', 'Icon to toggle the secondary side bar on in its left position.')); +// const auxiliaryBarRightIcon = registerIcon('auxiliarybar-right-layout-icon', Codicon.layoutSidebarRight, localize('toggleAuxiliaryIconRight', 'Icon to toggle the secondary side bar off in its right position.')); +// const auxiliaryBarRightOffIcon = registerIcon('auxiliarybar-right-off-layout-icon', Codicon.layoutSidebarRightOff, localize('toggleAuxiliaryIconRightOn', 'Icon to toggle the secondary side bar on in its right position.')); +// const auxiliaryBarLeftIcon = registerIcon('auxiliarybar-left-layout-icon', Codicon.layoutSidebarLeft, localize('toggleAuxiliaryIconLeft', 'Icon to toggle the secondary side bar in its left position.')); +// const auxiliaryBarLeftOffIcon = registerIcon('auxiliarybar-left-off-layout-icon', Codicon.layoutSidebarLeftOff, localize('toggleAuxiliaryIconLeftOn', 'Icon to toggle the secondary side bar on in its left position.')); export class ToggleAuxiliaryBarAction extends Action2 { @@ -140,45 +140,46 @@ registerAction2(class FocusAuxiliaryBarAction extends Action2 { }); MenuRegistry.appendMenuItems([ + // MEMBRANE: Hide Toggle Secondary Side Bar + // { + // id: MenuId.LayoutControlMenu, + // item: { + // group: '2_pane_toggles', + // command: { + // id: ToggleAuxiliaryBarAction.ID, + // title: localize('toggleSecondarySideBar', "Toggle Secondary Side Bar"), + // toggled: { condition: AuxiliaryBarVisibleContext, icon: auxiliaryBarLeftIcon }, + // icon: auxiliaryBarLeftOffIcon, + // }, + // when: ContextKeyExpr.and( + // IsAuxiliaryWindowContext.negate(), + // ContextKeyExpr.or( + // ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), + // ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), + // ContextKeyExpr.equals('config.workbench.sideBar.location', 'right') + // ), + // order: 0 + // } + // }, { + // id: MenuId.LayoutControlMenu, + // item: { + // group: '2_pane_toggles', + // command: { + // id: ToggleAuxiliaryBarAction.ID, + // title: localize('toggleSecondarySideBar', "Toggle Secondary Side Bar"), + // toggled: { condition: AuxiliaryBarVisibleContext, icon: auxiliaryBarRightIcon }, + // icon: auxiliaryBarRightOffIcon, + // }, + // when: ContextKeyExpr.and( + // IsAuxiliaryWindowContext.negate(), + // ContextKeyExpr.or( + // ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), + // ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), + // ContextKeyExpr.equals('config.workbench.sideBar.location', 'left') + // ), + // order: 2 + // } { - id: MenuId.LayoutControlMenu, - item: { - group: '2_pane_toggles', - command: { - id: ToggleAuxiliaryBarAction.ID, - title: localize('toggleSecondarySideBar', "Toggle Secondary Side Bar"), - toggled: { condition: AuxiliaryBarVisibleContext, icon: auxiliaryBarLeftIcon }, - icon: auxiliaryBarLeftOffIcon, - }, - when: ContextKeyExpr.and( - IsAuxiliaryWindowContext.negate(), - ContextKeyExpr.or( - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), - ContextKeyExpr.equals('config.workbench.sideBar.location', 'right') - ), - order: 0 - } - }, { - id: MenuId.LayoutControlMenu, - item: { - group: '2_pane_toggles', - command: { - id: ToggleAuxiliaryBarAction.ID, - title: localize('toggleSecondarySideBar', "Toggle Secondary Side Bar"), - toggled: { condition: AuxiliaryBarVisibleContext, icon: auxiliaryBarRightIcon }, - icon: auxiliaryBarRightOffIcon, - }, - when: ContextKeyExpr.and( - IsAuxiliaryWindowContext.negate(), - ContextKeyExpr.or( - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), - ContextKeyExpr.equals('config.workbench.sideBar.location', 'left') - ), - order: 2 - } - }, { id: MenuId.ViewContainerTitleContext, item: { group: '3_workbench_layout_move', @@ -187,7 +188,9 @@ MenuRegistry.appendMenuItems([ title: localize2('hideAuxiliaryBar', 'Hide Secondary Side Bar'), }, when: ContextKeyExpr.and(AuxiliaryBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar))), - order: 2 + // order: 2 + // } + // } } } ]); diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index 6791fd41d89ced..4db15c48d16850 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -98,7 +98,6 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { Parts.AUXILIARYBAR_PART, { hasTitle: true, - trailingSeparator: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0, }, AuxiliaryBarPart.activeViewSettingsKey, @@ -116,6 +115,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { hoverService, instantiationService, themeService, + commandService, viewDescriptorService, contextKeyService, extensionService, diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index e879ad7e21ee91..16930cc3faa4c0 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -37,6 +37,7 @@ import { IHoverDelegate } from '../../../base/browser/ui/hover/hoverDelegate.js' import { createInstantHoverDelegate, getDefaultHoverDelegate } from '../../../base/browser/ui/hover/hoverDelegateFactory.js'; import type { IHoverService } from '../../../platform/hover/browser/hover.js'; + export interface ICompositeTitleLabel { /** diff --git a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts index bba15b35fbba8e..893903a357bac5 100644 --- a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts @@ -11,7 +11,8 @@ import { ILogService } from '../../../../platform/log/common/log.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; import { IDialogsModel, IDialogViewItem } from '../../../common/dialogs.js'; -import { BrowserDialogHandler } from './dialogHandler.js'; +// MEMBRANE: Use our own dialog handler. +import { MembraneDialogHandler } from './membraneDialogHandler.js'; import { DialogService } from '../../../services/dialogs/common/dialogService.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -42,7 +43,9 @@ export class DialogHandlerContribution extends Disposable implements IWorkbenchC ) { super(); - this.impl = new Lazy(() => new BrowserDialogHandler(logService, layoutService, keybindingService, instantiationService, clipboardService, openerService, markdownRendererService)); + // MEMBRANE: Use our own dialog handler. + this.impl = new Lazy(() => new MembraneDialogHandler(logService, layoutService, productService, clipboardService)); + this.model = (this.dialogService as DialogService).model; this._register(this.model.onWillShowDialog(() => { diff --git a/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts b/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts new file mode 100644 index 00000000000000..9e7737093bb898 --- /dev/null +++ b/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from '../../../../nls.js'; +import { IConfirmation, IConfirmationResult, IInputResult, ICheckbox, IInputElement, ICustomDialogOptions, IInput, AbstractDialogHandler, DialogType, IPrompt, IAsyncPromptResult } from '../../../../platform/dialogs/common/dialogs.js'; +import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import Severity from '../../../../base/common/severity.js'; +import { MembraneDialog } from '../../../../base/browser/ui/dialog/membraneDialog.js'; +import { IDialogResult } from '../../../../base/browser/ui/dialog/dialog.js'; +import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; +import { fromNow } from '../../../../base/common/date.js'; + +export class MembraneDialogHandler extends AbstractDialogHandler { + + constructor( + @ILogService private readonly logService: ILogService, + @ILayoutService private readonly layoutService: ILayoutService, + @IProductService private readonly productService: IProductService, + @IClipboardService private readonly clipboardService: IClipboardService + ) { + super(); + } + + async prompt(prompt: IPrompt): Promise> { + this.logService.trace('MembraneDialogHandler#prompt', prompt.message); + + const buttons = this.getPromptButtons(prompt); + + const { button, checkboxChecked } = await this.doShow(prompt.type, prompt.message, buttons, prompt.detail, prompt.cancelButton ? buttons.length - 1 : -1 /* Disabled */, prompt.checkbox, undefined, typeof prompt?.custom === 'object' ? prompt.custom : undefined); + + return this.getPromptResult(prompt, button, checkboxChecked); + } + + async confirm(confirmation: IConfirmation): Promise { + this.logService.trace('MembraneDialogHandler#confirm', confirmation.message); + + const buttons = this.getConfirmationButtons(confirmation); + + const { button, checkboxChecked } = await this.doShow(confirmation.type ?? 'question', confirmation.message, buttons, confirmation.detail, buttons.length - 1, confirmation.checkbox, undefined, typeof confirmation?.custom === 'object' ? confirmation.custom : undefined); + + return { confirmed: button === 0, checkboxChecked }; + } + + async input(input: IInput): Promise { + this.logService.trace('MembraneDialogHandler#input', input.message); + + const buttons = this.getInputButtons(input); + + const { button, checkboxChecked, values } = await this.doShow(input.type ?? 'question', input.message, buttons, input.detail, buttons.length - 1, input?.checkbox, input.inputs, typeof input.custom === 'object' ? input.custom : undefined); + + return { confirmed: button === 0, checkboxChecked, values }; + } + + async about(): Promise { + const detailString = (useAgo: boolean): string => { + return localize('aboutDetail', + "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", + this.productService.version || 'Unknown', + this.productService.commit || 'Unknown', + this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown', + navigator.userAgent + ); + }; + + const detail = detailString(true); + const detailToCopy = detailString(false); + + const { button } = await this.doShow( + Severity.Info, + this.productService.nameLong, + [ + localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), + localize('ok', "OK") + ], + detail, + 1 + ); + + if (button === 0) { + this.clipboardService.writeText(detailToCopy); + } + } + + private async doShow(type: Severity | DialogType | undefined, message: string, buttons?: string[], detail?: string, cancelId?: number, checkbox?: ICheckbox, inputs?: IInputElement[], customOptions?: ICustomDialogOptions): Promise { + const dialogDisposables = new DisposableStore(); + + const dialog = new MembraneDialog( + this.layoutService.activeContainer, + message, + buttons, + { + detail, + cancelId, + type: this.getDialogType(type), + checkboxLabel: checkbox?.label, + checkboxChecked: checkbox?.checked, + inputs: inputs?.map(input => ({ + placeholder: input.placeholder, + type: input.type, + value: input.value + })), + disableCloseAction: customOptions?.disableCloseAction, + // Note: We're not handling all the styling options since we're delegating to Gaze + buttonStyles: { + buttonBackground: undefined, + buttonHoverBackground: undefined, + buttonForeground: undefined, + buttonSeparator: undefined, + buttonSecondaryBackground: undefined, + buttonSecondaryHoverBackground: undefined, + buttonSecondaryForeground: undefined, + buttonBorder: undefined + }, + checkboxStyles: { + checkboxBackground: undefined, + checkboxBorder: undefined, + checkboxForeground: undefined, + checkboxDisabledBackground: undefined, + checkboxDisabledForeground: undefined + }, + inputBoxStyles: { + inputBackground: undefined, + inputForeground: undefined, + inputBorder: undefined, + inputValidationInfoBorder: undefined, + inputValidationInfoBackground: undefined, + inputValidationInfoForeground: undefined, + inputValidationWarningBorder: undefined, + inputValidationWarningBackground: undefined, + inputValidationWarningForeground: undefined, + inputValidationErrorBorder: undefined, + inputValidationErrorBackground: undefined, + inputValidationErrorForeground: undefined + }, + dialogStyles: { + dialogForeground: undefined, + dialogBackground: undefined, + dialogShadow: undefined, + dialogBorder: undefined, + errorIconForeground: undefined, + warningIconForeground: undefined, + infoIconForeground: undefined, + textLinkForeground: undefined + } + } + ); + + dialogDisposables.add(dialog); + + const result = await dialog.show(); + dialogDisposables.dispose(); + + return result; + } +} diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 7fdea24ffcd9e7..0ddd8d14e846b5 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -8,9 +8,9 @@ import { localize, localize2 } from '../../../../nls.js'; import { IEditorPaneRegistry, EditorPaneDescriptor } from '../../editor.js'; import { IEditorFactoryRegistry, EditorExtensions } from '../../../common/editor.js'; import { - TextCompareEditorActiveContext, ActiveEditorPinnedContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext, + TextCompareEditorActiveContext, ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext, EditorPartMultipleEditorGroupsContext, ActiveEditorDirtyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, - EditorTabsVisibleContext, ActiveEditorLastInGroupContext, EditorPartMaximizedEditorGroupContext, MultipleEditorGroupsContext, InEditorZenModeContext, + EditorTabsVisibleContext, ActiveEditorLastInGroupContext, MultipleEditorGroupsContext, InEditorZenModeContext, IsAuxiliaryWindowContext, ActiveCompareEditorCanSwapContext, MultipleEditorsSelectedInGroupContext, SplitEditorsVertically } from '../../../common/contextkeys.js'; import { SideBySideEditorInput, SideBySideEditorInputSerializer } from '../../../common/editor/sideBySideEditorInput.js'; @@ -51,7 +51,7 @@ import { CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, KEEP_EDITOR_COMMAND_ID, PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID, TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, LOCK_GROUP_COMMAND_ID, - SPLIT_EDITOR, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, + SPLIT_EDITOR, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID, MOVE_EDITOR_INTO_RIGHT_GROUP, MOVE_EDITOR_INTO_LEFT_GROUP, MOVE_EDITOR_INTO_ABOVE_GROUP, MOVE_EDITOR_INTO_BELOW_GROUP } from './editorCommands.js'; import { GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, TOGGLE_DIFF_SIDE_BY_SIDE, DIFF_SWAP_SIDES } from './diffEditorCommands.js'; @@ -68,7 +68,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { UntitledTextEditorInputSerializer, UntitledTextEditorWorkingCopyEditorHandler } from '../../../services/untitled/common/untitledTextEditorHandler.js'; import { DynamicEditorConfigurations } from './editorConfiguration.js'; -import { ConfigureEditorAction, ConfigureEditorTabsAction, EditorActionsDefaultAction, EditorActionsTitleBarAction, HideEditorActionsAction, HideEditorTabsAction, ShowMultipleEditorTabsAction, ShowSingleEditorTabAction, ZenHideEditorTabsAction, ZenShowMultipleEditorTabsAction, ZenShowSingleEditorTabAction } from '../../actions/layoutActions.js'; +import { ConfigureEditorTabsAction, EditorActionsDefaultAction, EditorActionsTitleBarAction, HideEditorActionsAction, HideEditorTabsAction, ShowMultipleEditorTabsAction, ShowSingleEditorTabAction, ZenHideEditorTabsAction, ZenShowMultipleEditorTabsAction, ZenShowSingleEditorTabAction } from '../../actions/layoutActions.js'; import { ICommandAction } from '../../../../platform/action/common/action.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; import { getFontSnippets } from '../../../../base/browser/fonts.js'; @@ -380,7 +380,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsZenModeSubmenu, { comman MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsZenModeSubmenu, { command: { id: ZenShowSingleEditorTabAction.ID, title: localize('singleTab', "Single Tab"), toggled: ContextKeyExpr.equals('config.zenMode.showTabs', 'single') }, group: '1_config', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsZenModeSubmenu, { command: { id: ZenHideEditorTabsAction.ID, title: localize('hideTabs', "Hidden"), toggled: ContextKeyExpr.equals('config.zenMode.showTabs', 'none') }, group: '1_config', order: 30 }); -MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { submenu: MenuId.EditorActionsPositionSubmenu, title: localize('editorActionsPosition', "Editor Actions Position"), group: '4_config', order: 20 }); +// MEMBRANE: Hide this setting. +// MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { submenu: MenuId.EditorActionsPositionSubmenu, title: localize('editorActionsPosition', "Editor Actions Position"), group: '4_config', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: EditorActionsDefaultAction.ID, title: localize('tabBar', "Tab Bar"), toggled: ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'default') }, group: '1_config', order: 10, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate() }); MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: EditorActionsTitleBarAction.ID, title: localize('titleBar', "Title Bar"), toggled: ContextKeyExpr.or(ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'titleBar'), ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none'), ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'default'))) }, group: '1_config', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: HideEditorActionsAction.ID, title: localize('hidden', "Hidden"), toggled: ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'hidden') }, group: '1_config', order: 30 }); @@ -388,21 +389,24 @@ MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ConfigureEditorTabsAction.ID, title: localize('configureTabs', "Configure Tabs") }, group: '9_configure', order: 10 }); // Editor Title Context Menu -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: localize('close', "Close") }, group: '1_close', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeOthers', "Close Others"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: localize('closeRight', "Close to the Right"), precondition: ContextKeyExpr.and(ActiveEditorLastInGroupContext.toNegated(), MultipleEditorsSelectedInGroupContext.negate()) }, group: '1_close', order: 30, when: EditorTabsVisibleContext }); +// MEMBRANE: Only keep the 4 essential menu items +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: localize('close', "Close") }, group: '1_close', order: 10 }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeOthers', "Close Others"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 20 }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: localize('closeRight', "Close to the Right"), precondition: ContextKeyExpr.and(ActiveEditorLastInGroupContext.toNegated(), MultipleEditorsSelectedInGroupContext.negate()) }, group: '1_close', order: 30, when: EditorTabsVisibleContext }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SHOW_EDITORS_IN_GROUP, title: localize('showOpenedEditors', "Show Opened Editors") }, group: '0_show', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '1_close', order: 50 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: REOPEN_WITH_COMMAND_ID, title: localize('reopenWith', "Reopen Editor With...") }, group: '1_open', order: 10, when: ActiveEditorAvailableEditorIdsContext }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: KEEP_EDITOR_COMMAND_ID, title: localize('keepOpen', "Keep Open"), precondition: ActiveEditorPinnedContext.toNegated() }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: PIN_EDITOR_COMMAND_ID, title: localize('pin', "Pin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext.toNegated() }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: UNPIN_EDITOR_COMMAND_ID, title: localize('unpin', "Unpin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR, title: localize('splitRight', "Split Right") }, group: '5_split', order: 10, when: SplitEditorsVertically.negate() }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR, title: localize('splitDown', "Split Down") }, group: '5_split', order: 10, when: SplitEditorsVertically }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { submenu: MenuId.EditorSplitMoveSubmenu, title: localize('splitAndMoveEditor', "Split & Move"), group: '5_split', order: 15 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('moveToNewWindow', "Move into New Window") }, group: '7_new_window', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('copyToNewWindow', "Copy into New Window") }, group: '7_new_window', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { submenu: MenuId.EditorTitleContextShare, title: localize('share', "Share"), group: '11_share', order: -1, when: MultipleEditorsSelectedInGroupContext.negate() }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Enable Preview Editors"), toggled: ContextKeyExpr.has('config.workbench.editor.enablePreview') }, group: '9_settings', order: 10 }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: REOPEN_WITH_COMMAND_ID, title: localize('reopenWith', "Reopen Editor With...") }, group: '1_open', order: 10, when: ActiveEditorAvailableEditorIdsContext }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: KEEP_EDITOR_COMMAND_ID, title: localize('keepOpen', "Keep Open"), precondition: ActiveEditorPinnedContext.toNegated() }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: PIN_EDITOR_COMMAND_ID, title: localize('pin', "Pin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext.toNegated() }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: UNPIN_EDITOR_COMMAND_ID, title: localize('unpin', "Unpin") }, group: '3_preview', order: 20, when: ActiveEditorStickyContext }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR, title: localize('splitRight', "Split Right") }, group: '5_split', order: 10, when: SplitEditorsVertically.negate() }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR, title: localize('splitDown', "Split Down") }, group: '5_split', order: 10, when: SplitEditorsVertically }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { submenu: MenuId.EditorSplitMoveSubmenu, title: localize('splitAndMoveEditor', "Split & Move"), group: '5_split', order: 15 }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('moveToNewWindow', "Move into New Window") }, group: '7_new_window', order: 10 }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('copyToNewWindow', "Copy into New Window") }, group: '7_new_window', order: 20 }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { submenu: MenuId.EditorTitleContextShare, title: localize('share', "Share"), group: '11_share', order: -1, when: MultipleEditorsSelectedInGroupContext.negate() }); // Editor Title Context Menu: Split & Move Editor Submenu MenuRegistry.appendMenuItem(MenuId.EditorSplitMoveSubmenu, { command: { id: SPLIT_EDITOR_UP, title: localize('splitUp', "Split Up") }, group: '1_split', order: 10 }); @@ -422,10 +426,11 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Enable Preview Editors"), toggled: ContextKeyExpr.has('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('maximizeGroup', "Maximize Group") }, group: '8_group_operations', order: 5, when: ContextKeyExpr.and(EditorPartMaximizedEditorGroupContext.negate(), EditorPartMultipleEditorGroupsContext) }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('unmaximizeGroup', "Unmaximize Group") }, group: '8_group_operations', order: 5, when: EditorPartMaximizedEditorGroupContext }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_group_operations', order: 10, when: IsAuxiliaryWindowContext.toNegated() /* already a primary action for aux windows */ }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: ConfigureEditorAction.ID, title: localize('configureEditors', "Configure Editors") }, group: '9_configure', order: 10 }); +// MEMBRANE: Hide Maximize Group options +// MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('maximizeGroup', "Maximize Group") }, group: '8_group_operations', order: 5, when: ContextKeyExpr.and(EditorPartMaximizedEditorGroupContext.negate(), EditorPartMultipleEditorGroupsContext) }); +// MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('unmaximizeGroup', "Unmaximize Group") }, group: '8_group_operations', order: 5, when: EditorPartMaximizedEditorGroupContext }); +// MEMBRANE: Hide this setting. +// MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_group_operations', order: 10, when: IsAuxiliaryEditorPartContext.toNegated() /* already a primary action for aux windows */ }); function appendEditorToolItem(primary: ICommandAction, when: ContextKeyExpression | undefined, order: number, alternative?: ICommandAction, precondition?: ContextKeyExpression | undefined, enableInCompactMode?: boolean): void { const item: IMenuItem = { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index a1050be0194de8..680c7060c831fc 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -5,7 +5,11 @@ import './media/editorgroupview.css'; import { EditorGroupModel, IEditorOpenOptions, IGroupModelChangeEvent, ISerializedEditorGroupModel, isGroupEditorCloseEvent, isGroupEditorOpenEvent, isSerializedEditorGroupModel } from '../../../common/editor/editorGroupModel.js'; -import { GroupIdentifier, CloseDirection, IEditorCloseEvent, IEditorPane, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, EditorResourceAccessor, EditorInputCapabilities, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, SideBySideEditor, EditorCloseContext, IEditorWillMoveEvent, IEditorWillOpenEvent, IMatchEditorOptions, GroupModelChangeKind, IActiveEditorChangeEvent, IFindEditorOptions, TEXT_DIFF_EDITOR_ID } from '../../../common/editor.js'; +import { + GroupIdentifier, CloseDirection, IEditorCloseEvent, IEditorPane, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, EditorResourceAccessor, EditorInputCapabilities, IUntypedEditorInput, + // DEFAULT_EDITOR_ASSOCIATION, + SideBySideEditor, EditorCloseContext, IEditorWillMoveEvent, IEditorWillOpenEvent, IMatchEditorOptions, GroupModelChangeKind, IActiveEditorChangeEvent, IFindEditorOptions, TEXT_DIFF_EDITOR_ID +} from '../../../common/editor.js'; import { ActiveEditorGroupLockedContext, ActiveEditorDirtyContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorPinnedContext, ActiveEditorLastInGroupContext, ActiveEditorFirstInGroupContext, ResourceContextKey, applyAvailableEditorIds, ActiveEditorAvailableEditorIdsContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorContext, ActiveEditorReadonlyContext, ActiveEditorCanRevertContext, ActiveEditorCanToggleReadonlyContext, ActiveCompareEditorCanSwapContext, MultipleEditorsSelectedInGroupContext, TwoEditorsSelectedInGroupContext, SelectedEditorsInGroupFileOrUntitledResourceContextKey } from '../../../common/contextkeys.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { SideBySideEditorInput } from '../../../common/editor/sideBySideEditorInput.js'; @@ -28,7 +32,11 @@ import { DisposableStore, MutableDisposable, toDisposable } from '../../../../ba import { ITelemetryData, ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { DeferredPromise, Promises, RunOnceWorker } from '../../../../base/common/async.js'; import { EventType as TouchEventType, GestureEvent } from '../../../../base/browser/touch.js'; -import { IEditorGroupsView, IEditorGroupView, fillActiveEditorViewState, EditorServiceImpl, IEditorGroupTitleHeight, IInternalEditorOpenOptions, IInternalMoveCopyOptions, IInternalEditorCloseOptions, IInternalEditorTitleControlOptions, IEditorPartsView, IEditorGroupViewOptions } from './editor.js'; +import { + IEditorGroupsView, IEditorGroupView, fillActiveEditorViewState, + // EditorServiceImpl, + IEditorGroupTitleHeight, IInternalEditorOpenOptions, IInternalMoveCopyOptions, IInternalEditorCloseOptions, IInternalEditorTitleControlOptions, IEditorPartsView, IEditorGroupViewOptions +} from './editor.js'; import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { SubmenuAction } from '../../../../base/common/actions.js'; @@ -36,7 +44,7 @@ import { IMenuChangeEvent, IMenuService, MenuId } from '../../../../platform/act import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { getActionBarActions, PrimaryAndSecondaryActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; +// import { IEditorService } from '../../../services/editor/common/editorService.js'; import { hash } from '../../../../base/common/hash.js'; import { getMimeTypes } from '../../../../editor/common/services/languagesAssociations.js'; import { extname, isEqual } from '../../../../base/common/resources.js'; @@ -128,6 +136,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private readonly progressBar: ProgressBar; private readonly editorContainer: HTMLElement; + + // MEMBRANE: Inner shadow + private readonly innerShadow: HTMLElement; + private readonly editorPane: EditorPanes; private readonly disposedEditorsWorker = this._register(new RunOnceWorker(editors => this.handleDisposedEditors(editors), 0)); @@ -154,7 +166,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { @IMenuService private readonly menuService: IMenuService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @IFileDialogService private readonly fileDialogService: IFileDialogService, - @IEditorService private readonly editorService: EditorServiceImpl, + // MEMBRANE: Not needed because we disabled the listener for double clicks on empty editor + // @IEditorService private readonly editorService: EditorServiceImpl, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService, @@ -218,6 +231,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.editorContainer = $('.editor-container'); this.element.appendChild(this.editorContainer); + // MEMBRANE: Inner shadow + this.innerShadow = document.createElement('div'); + this.innerShadow.classList.add('membrane-inner-shadow'); + this.element.appendChild(this.innerShadow); + // Editor pane this.editorPane = this._register(this.scopedInstantiationService.createInstance(EditorPanes, this.element, this.editorContainer, this)); this._onDidChange.input = this.editorPane.onDidChangeSizeConstraints; @@ -378,19 +396,20 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private registerContainerListeners(): void { // Open new file via doubleclick on empty container - this._register(addDisposableListener(this.element, EventType.DBLCLICK, e => { - if (this.isEmpty) { - EventHelper.stop(e); - - this.editorService.openEditor({ - resource: undefined, - options: { - pinned: true, - override: DEFAULT_EDITOR_ASSOCIATION.id - } - }, this.id); - } - })); + // MEMBRANE: Disable double-click to create new untitled file + // this._register(addDisposableListener(this.element, EventType.DBLCLICK, e => { + // if (this.isEmpty) { + // EventHelper.stop(e); + + // this.editorService.openEditor({ + // resource: undefined, + // options: { + // pinned: true, + // override: DEFAULT_EDITOR_ASSOCIATION.id + // } + // }, this.id); + // } + // })); // Close empty editor group via middle mouse click this._register(addDisposableListener(this.element, EventType.AUXCLICK, e => { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index e3b1dd55c69a95..cc12e84822dbd6 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -7,11 +7,17 @@ import { $, append, clearNode, h } from '../../../../base/browser/dom.js'; import { KeybindingLabel } from '../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; import { coalesce, shuffle } from '../../../../base/common/arrays.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { isMacintosh, isWeb, OS } from '../../../../base/common/platform.js'; +import { + // isMacintosh, + isWeb, OS +} from '../../../../base/common/platform.js'; import { localize } from '../../../../nls.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { + // ContextKeyExpr, + ContextKeyExpression, IContextKeyService +} from '../../../../platform/contextkey/common/contextkey.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from '../../../../platform/storage/common/storage.js'; import { defaultKeybindingLabelStyles } from '../../../../platform/theme/browser/defaultStyles.js'; @@ -27,27 +33,28 @@ interface WatermarkEntry { }; } -const showCommands: WatermarkEntry = { text: localize('watermark.showCommands', "Show All Commands"), id: 'workbench.action.showCommands' }; -const gotoFile: WatermarkEntry = { text: localize('watermark.quickAccess', "Go to File"), id: 'workbench.action.quickOpen' }; -const openFile: WatermarkEntry = { text: localize('watermark.openFile', "Open File"), id: 'workbench.action.files.openFile' }; -const openFolder: WatermarkEntry = { text: localize('watermark.openFolder', "Open Folder"), id: 'workbench.action.files.openFolder' }; -const openFileOrFolder: WatermarkEntry = { text: localize('watermark.openFileFolder', "Open File or Folder"), id: 'workbench.action.files.openFileFolder' }; -const openRecent: WatermarkEntry = { text: localize('watermark.openRecent', "Open Recent"), id: 'workbench.action.openRecent' }; -const newUntitledFile: WatermarkEntry = { text: localize('watermark.newUntitledFile', "New Untitled Text File"), id: 'workbench.action.files.newUntitledFile' }; -const findInFiles: WatermarkEntry = { text: localize('watermark.findInFiles', "Find in Files"), id: 'workbench.action.findInFiles' }; -const toggleTerminal: WatermarkEntry = { text: localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), id: 'workbench.action.terminal.toggleTerminal', when: { web: ContextKeyExpr.equals('terminalProcessSupported', true) } }; -const startDebugging: WatermarkEntry = { text: localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: { web: ContextKeyExpr.equals('terminalProcessSupported', true) } }; -const openSettings: WatermarkEntry = { text: localize('watermark.openSettings', "Open Settings"), id: 'workbench.action.openSettings' }; - -const showChat = ContextKeyExpr.and(ContextKeyExpr.equals('chatSetupHidden', false), ContextKeyExpr.equals('chatSetupDisabled', false)); -const openChat: WatermarkEntry = { text: localize('watermark.openChat', "Open Chat"), id: 'workbench.action.chat.open', when: { native: showChat, web: showChat } }; +// MEMBRANE: rm watermark hotkeys +// const showCommands: WatermarkEntry = { text: localize('watermark.showCommands', "Show All Commands"), id: 'workbench.action.showCommands' }; +// const gotoFile: WatermarkEntry = { text: localize('watermark.quickAccess', "Go to File"), id: 'workbench.action.quickOpen' }; +// const openFile: WatermarkEntry = { text: localize('watermark.openFile', "Open File"), id: 'workbench.action.files.openFile' }; +// const openFolder: WatermarkEntry = { text: localize('watermark.openFolder', "Open Folder"), id: 'workbench.action.files.openFolder' }; +// const openFileOrFolder: WatermarkEntry = { text: localize('watermark.openFileFolder', "Open File or Folder"), id: 'workbench.action.files.openFileFolder' }; +// const openRecent: WatermarkEntry = { text: localize('watermark.openRecent', "Open Recent"), id: 'workbench.action.openRecent' }; +// const newUntitledFile: WatermarkEntry = { text: localize('watermark.newUntitledFile', "New Untitled Text File"), id: 'workbench.action.files.newUntitledFile' }; +// const findInFiles: WatermarkEntry = { text: localize('watermark.findInFiles', "Find in Files"), id: 'workbench.action.findInFiles' }; +// const toggleTerminal: WatermarkEntry = { text: localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), id: 'workbench.action.terminal.toggleTerminal', when: { web: ContextKeyExpr.equals('terminalProcessSupported', true) } }; +// const startDebugging: WatermarkEntry = { text: localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: { web: ContextKeyExpr.equals('terminalProcessSupported', true) } }; +// const openSettings: WatermarkEntry = { text: localize('watermark.openSettings', "Open Settings"), id: 'workbench.action.openSettings' }; + +// const showChat = ContextKeyExpr.and(ContextKeyExpr.equals('chatSetupHidden', false), ContextKeyExpr.equals('chatSetupDisabled', false)); +// const openChat: WatermarkEntry = { text: localize('watermark.openChat', "Open Chat"), id: 'workbench.action.chat.open', when: { native: showChat, web: showChat } }; const emptyWindowEntries: WatermarkEntry[] = coalesce([ - showCommands, - ...(isMacintosh && !isWeb ? [openFileOrFolder] : [openFile, openFolder]), - openRecent, - isMacintosh && !isWeb ? newUntitledFile : undefined, // fill in one more on macOS to get to 5 entries - openChat + // showCommands, + // ...(isMacintosh && !isWeb ? [openFileOrFolder] : [openFile, openFolder]), + // openRecent, + // isMacintosh && !isWeb ? newUntitledFile : undefined, // fill in one more on macOS to get to 5 entries + // openChat ]); const randomEmptyWindowEntries: WatermarkEntry[] = [ @@ -55,16 +62,16 @@ const randomEmptyWindowEntries: WatermarkEntry[] = [ ]; const workspaceEntries: WatermarkEntry[] = [ - showCommands, - gotoFile, - openChat + // showCommands, + // gotoFile, + // openChat ]; const randomWorkspaceEntries: WatermarkEntry[] = [ - findInFiles, - startDebugging, - toggleTerminal, - openSettings, + // findInFiles, + // startDebugging, + // toggleTerminal, + // openSettings, ]; export class EditorGroupWatermark extends Disposable { @@ -92,12 +99,34 @@ export class EditorGroupWatermark extends Disposable { this.cachedWhen = this.storageService.getObject(EditorGroupWatermark.CACHED_WHEN, StorageScope.PROFILE, Object.create(null)); this.workbenchState = this.contextService.getWorkbenchState(); - const elements = h('.editor-group-watermark', [ h('.letterpress'), h('.shortcuts@shortcuts'), ]); + // MEMBRANE: show aux bar toggle button (no longer needed) + // 1. IF the aux bar is not visible + // 2. AND there are not multiple open editor groups + // (when there are multiple open editor groups, there can be a watermark empty editor in some cases) + // (and in that^ case, the watermark has an x icon to close it) + // (and the other open editors will handle aux bar toggle button via the editor tab bar actions) + // See also: editorTabsControl.ts + // const hasMultipleEditorGroups = this.contextKeyService.contextMatchesRules(MultipleEditorGroupsContext); + // const isAuxBarVisible = this.contextKeyService.contextMatchesRules(AuxiliaryBarVisibleContext); + // const toggleButton = $('.toggle-aux-bar', { + // title: 'Show Brane (AI) & Program Info', + // onclick: () => this.commandService.executeCommand('workbench.action.toggleAuxiliaryBar'), + // ['data-hide']: isAuxBarVisible || hasMultipleEditorGroups, + // }, renderIcon(Codicon.chevronLeft)); + // this._register(this.contextKeyService.onDidChangeContext(e => { + // if (e.affectsSome(new Set([AuxiliaryBarVisibleContext.key, MultipleEditorGroupsContext.key]))) { + // const isAuxBarVisible = this.contextKeyService.contextMatchesRules(AuxiliaryBarVisibleContext); + // const hasMultipleEditorGroups = this.contextKeyService.contextMatchesRules(MultipleEditorGroupsContext); + // toggleButton.setAttribute('data-hide', (isAuxBarVisible || hasMultipleEditorGroups).toString()); + // } + // })); + // append(elements.root, toggleButton); + append(container, elements.root); this.shortcuts = elements.shortcuts; @@ -190,4 +219,4 @@ export class EditorGroupWatermark extends Disposable { } } -registerColor('editorWatermark.foreground', { dark: transparent(editorForeground, 0.6), light: transparent(editorForeground, 0.68), hcDark: editorForeground, hcLight: editorForeground }, localize('editorLineHighlight', 'Foreground color for the labels in the editor watermark.')); +registerColor('editorWatermark.foreground', { dark: transparent(editorForeground, 0.6), light: transparent(editorForeground, 0.68), hcDark: editorForeground, hcLight: editorForeground }, localize('editorLineHighlight', 'Foreground color for the labels in the editor watermark.')); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index b0a44e2c23af45..0b7ee491a2f91a 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -6,9 +6,9 @@ import './media/editortabscontrol.css'; import { localize } from '../../../../nls.js'; import { DataTransfers } from '../../../../base/browser/dnd.js'; -import { $, Dimension, getActiveWindow, getWindow, isMouseEvent } from '../../../../base/browser/dom.js'; +import { Dimension, getActiveWindow, getWindow, isMouseEvent } from '../../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; -import { ActionsOrientation, IActionViewItem, prepareActions } from '../../../../base/browser/ui/actionbar/actionbar.js'; +import { ActionsOrientation, IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IAction, ActionRunner } from '../../../../base/common/actions.js'; import { ResolvedKeybinding } from '../../../../base/common/keybindings.js'; import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; @@ -24,9 +24,9 @@ import { IThemeService, Themable } from '../../../../platform/theme/common/theme import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillEditorsDragData, isWindowDraggedOver } from '../../dnd.js'; import { EditorPane } from './editorPane.js'; import { IEditorGroupsView, IEditorGroupView, IEditorPartsView, IInternalEditorOpenOptions } from './editor.js'; -import { IEditorCommandsContext, EditorResourceAccessor, IEditorPartOptions, SideBySideEditor, EditorsOrder, EditorInputCapabilities, IToolbarActions, GroupIdentifier, Verbosity } from '../../../common/editor.js'; +import { IEditorCommandsContext, EditorResourceAccessor, IEditorPartOptions, SideBySideEditor, EditorsOrder, EditorInputCapabilities, IToolbarActions, GroupIdentifier } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; -import { ResourceContextKey, ActiveEditorPinnedContext, ActiveEditorStickyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, ActiveEditorFirstInGroupContext, ActiveEditorAvailableEditorIdsContext, applyAvailableEditorIds, ActiveEditorLastInGroupContext } from '../../../common/contextkeys.js'; +import { ResourceContextKey, ActiveEditorPinnedContext, ActiveEditorStickyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, ActiveEditorFirstInGroupContext, ActiveEditorAvailableEditorIdsContext, applyAvailableEditorIds, ActiveEditorLastInGroupContext, AuxiliaryBarVisibleContext } from '../../../common/contextkeys.js'; import { AnchorAlignment } from '../../../../base/browser/ui/contextview/contextview.js'; import { assertReturnsDefined } from '../../../../base/common/types.js'; import { isFirefox } from '../../../../base/browser/browser.js'; @@ -39,15 +39,19 @@ import { IEditorResolverService } from '../../../services/editor/common/editorRe import { IEditorTitleControlDimensions } from './editorTitleControl.js'; import { IReadonlyEditorGroupModel } from '../../../common/editor/editorGroupModel.js'; import { EDITOR_CORE_NAVIGATION_COMMANDS } from './editorCommands.js'; -import { IAuxiliaryEditorPart, MergeGroupMode } from '../../../services/editor/common/editorGroupsService.js'; +import { IAuxiliaryEditorPart, IEditorGroupsService, MergeGroupMode } from '../../../services/editor/common/editorGroupsService.js'; import { isMacintosh } from '../../../../base/common/platform.js'; import { IHostService } from '../../../services/host/browser/host.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; -import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; -import { MarkdownString } from '../../../../base/common/htmlContent.js'; -import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; + import { applyDragImage } from '../../../../base/browser/ui/dnd/dnd.js'; +// MEMBRANE: see membraneActionsToolbar initialization below +// import { MenuItemAction } from 'vs/platform/actions/common/actions'; +// import { Separator } from 'vs/base/common/actions'; +// import { AuxiliaryBarVisibleContext } from '../../../common/contextkeys.js'; +// import { Codicon } from 'vs/base/common/codicons'; +// import { ICommandService } from 'vs/platform/commands/common/commands'; export class EditorCommandsContextActionRunner extends ActionRunner { constructor( @@ -100,12 +104,14 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC protected readonly treeItemsTransfer = LocalSelectionTransfer.getInstance(); private static readonly EDITOR_TAB_HEIGHT = { - normal: 35 as const, + // MEMBRANE: thinner editor tabs + normal: 30 as const, compact: 22 as const }; protected editorActionsToolbarContainer: HTMLElement | undefined; private editorActionsToolbar: WorkbenchToolBar | undefined; + private membraneActionsToolbar: WorkbenchToolBar | undefined; private readonly editorActionsToolbarDisposables = this._register(new DisposableStore()); private readonly editorActionsDisposables = this._register(new DisposableStore()); @@ -140,18 +146,16 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC @IThemeService themeService: IThemeService, @IEditorResolverService private readonly editorResolverService: IEditorResolverService, @IHostService private readonly hostService: IHostService, + // MEMBRANE: inject editor group service and command service + @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService, + // @ICommandService private readonly commandService: ICommandService ) { super(themeService); - this.renderDropdownAsChildElement = false; - - const container = this.create(parent); - - // Context Keys - this.contextMenuContextKeyService = this._register(this.contextKeyService.createScoped(container)); - const scopedInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection( + this.contextMenuContextKeyService = this._register(this.contextKeyService.createScoped(parent)); + const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( [IContextKeyService, this.contextMenuContextKeyService], - ))); + )); this.resourceContext = this._register(scopedInstantiationService.createInstance(ResourceContextKey)); @@ -165,11 +169,14 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC this.sideBySideEditorContext = SideBySideEditorActiveContext.bindTo(this.contextMenuContextKeyService); this.groupLockedContext = ActiveEditorGroupLockedContext.bindTo(this.contextMenuContextKeyService); + + this.renderDropdownAsChildElement = false; + + this.create(parent); } - protected create(parent: HTMLElement): HTMLElement { + protected create(parent: HTMLElement): void { this.updateTabHeight(); - return parent; } private get editorActionsEnabled(): boolean { @@ -177,7 +184,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC } protected createEditorActionsToolBar(parent: HTMLElement, classes: string[]): void { - this.editorActionsToolbarContainer = $('div'); + this.editorActionsToolbarContainer = document.createElement('div'); this.editorActionsToolbarContainer.classList.add(...classes); parent.appendChild(this.editorActionsToolbarContainer); @@ -208,7 +215,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC // Toolbar Widget this.editorActionsToolbar = this.editorActionsToolbarDisposables.add(this.instantiationService.createInstance(WorkbenchToolBar, container, { - actionViewItemProvider: (action, options) => this.actionViewItemProvider(action, options), + actionViewItemProvider: action => this.actionViewItemProvider(action), orientation: ActionsOrientation.HORIZONTAL, ariaLabel: localize('ariaLabelEditorActions', "Editor actions"), getKeyBinding: action => this.getKeybinding(action), @@ -232,14 +239,77 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC this.notificationService.error(e.error); } })); + + // MEMBRANE: Additional toolbar items that appear in the top right of the editor. This is basically a copy + // of the above code for editorActionsToolbar but using MembraneEditorTitle instead of EditorTitle. + this.membraneActionsToolbar = this.editorActionsToolbarDisposables.add(this.instantiationService.createInstance(WorkbenchToolBar, container, { + actionViewItemProvider: action => this.actionViewItemProvider(action), + orientation: ActionsOrientation.HORIZONTAL, + ariaLabel: 'Additional Editor actions', // IMPORTANT: we match on this in css + actionRunner: this.editorActionsToolbarDisposables.add(new EditorCommandsContextActionRunner(context)), + anchorAlignmentProvider: () => AnchorAlignment.RIGHT, + })); + this.membraneActionsToolbar.context = context; + this.editorActionsToolbarDisposables.add(this.membraneActionsToolbar.actionRunner.onDidRun(e => { + if (e.error && !isCancellationError(e.error)) { + this.notificationService.error(e.error); + } + })); + // Update when the auxiliary bar is opened/closed + + this.editorActionsToolbarDisposables.add(this.getEditorPaneAwareContextKeyService().onDidChangeContext((e) => { + if (e.affectsSome(new Set([AuxiliaryBarVisibleContext.key]))) { + this.updateMembraneActions(); + } + })); + // Update when the active group changes (i.e. rearranging grid/split view) + // because the toprightmost tab may have changed + this.editorActionsToolbarDisposables.add(this.editorGroupsService.onDidChangeActiveGroup(() => { + this.updateMembraneActions(); + })); + // Update when a file is opened + this.editorActionsToolbarDisposables.add(this.editorGroupsService.onDidActivateGroup(() => { + this.updateMembraneActions(); + })); + + this.updateMembraneActions(); } - private actionViewItemProvider(action: IAction, options: IBaseActionViewItemOptions): IActionViewItem | undefined { + // MEMBRANE: see membraneActionsToolbar initialization above (unused after moving gaze to /ide) + private updateMembraneActions() { + const membraneActions: IAction[] = []; + + // const isAuxBarHidden = this.getEditorPaneAwareContextKeyService().contextMatchesRules(AuxiliaryBarVisibleContext.toNegated()); + + // let groupAbove; + // let groupRight; + // let isTopRight; + // try { + // // findGroup can throw an error in multiple places when some editor parts are not instantiated + // groupAbove = this.editorGroupsService.findGroup({ direction: GroupDirection.UP }, this.groupView); + // groupRight = this.editorGroupsService.findGroup({ direction: GroupDirection.RIGHT }, this.groupView); + // isTopRight = !groupAbove && !groupRight; + // } catch (error) { + // } + + // if (isAuxBarHidden && isTopRight) { + // membraneActions.push(new Separator()); + // membraneActions.push(new MenuItemAction({ + // id: 'workbench.action.toggleAuxiliaryBar', + // title: 'Show Brane (AI) & Program Info', + // icon: Codicon.chevronLeft, + // }, undefined, undefined, undefined, this.getEditorPaneAwareContextKeyService(), this.commandService)); + // } + + this.membraneActionsToolbar?.setActions(membraneActions, []); + } + + private actionViewItemProvider(action: IAction): IActionViewItem | undefined { const activeEditorPane = this.groupView.activeEditorPane; // Check Active Editor if (activeEditorPane instanceof EditorPane) { - const result = activeEditorPane.getActionViewItem(action, options); + const result = activeEditorPane.getActionViewItem(action, {}); if (result) { return result; @@ -247,7 +317,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC } // Check extensions - return createActionViewItem(this.instantiationService, action, { ...options, menuAsChild: this.renderDropdownAsChildElement }); + return createActionViewItem(this.instantiationService, action, { menuAsChild: this.renderDropdownAsChildElement }); } protected updateEditorActionsToolbar(): void { @@ -260,9 +330,11 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC const editorActions = this.groupView.createEditorActions(this.editorActionsDisposables); this.editorActionsDisposables.add(editorActions.onDidChange(() => this.updateEditorActionsToolbar())); - const editorActionsToolbar = assertReturnsDefined(this.editorActionsToolbar); - const { primary, secondary } = this.prepareEditorActions(editorActions.actions); - editorActionsToolbar.setActions(prepareActions(primary), prepareActions(secondary)); + // MEMBRANE: hide editor tab actions (e.g. Split Editor, More Actions) + const editorActionsToolbar = this.editorActionsToolbar; + assertReturnsDefined(editorActionsToolbar); + // const { primary, secondary } = this.prepareEditorActions(editorActions.actions); + // editorActionsToolbar.setActions(prepareActions(primary), prepareActions(secondary)); } protected abstract prepareEditorActions(editorActions: IToolbarActions): IToolbarActions; @@ -317,7 +389,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC label = localize('draggedEditorGroup', "{0} (+{1})", label, this.groupView.count - 1); } - applyDragImage(e, element, label); + applyDragImage(e, this.parent, label, ['monaco-editor-group-drag-image']); } return isNewWindowOperation; @@ -450,19 +522,6 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC return this.groupsView.partOptions.tabHeight !== 'compact' ? EditorTabsControl.EDITOR_TAB_HEIGHT.normal : EditorTabsControl.EDITOR_TAB_HEIGHT.compact; } - protected getHoverTitle(editor: EditorInput): string | IManagedHoverTooltipMarkdownString { - const title = editor.getTitle(Verbosity.LONG); - if (!this.tabsModel.isPinned(editor)) { - return { - markdown: new MarkdownString('', { supportThemeIcons: true, isTrusted: true }). - appendText(title). - appendMarkdown(' (_preview_ [$(gear)](command:workbench.action.openSettings?%5B%22workbench.editor.enablePreview%22%5D "Configure Preview Mode"))'), - markdownNotSupportedFallback: title + ' (preview)' - }; - } - return title; - } - protected updateTabHeight(): void { this.parent.style.setProperty('--editor-group-tab-height', `${this.tabHeight}px`); } @@ -515,4 +574,4 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC abstract layout(dimensions: IEditorTitleControlDimensions): Dimension; abstract getHeight(): number; -} +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts index 00f7bf67590d61..60bc895125291c 100644 --- a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts @@ -10,6 +10,7 @@ import { IThemeService, Themable } from '../../../../platform/theme/common/theme import { BreadcrumbsControl, BreadcrumbsControlFactory } from './breadcrumbsControl.js'; import { IEditorGroupsView, IEditorGroupTitleHeight, IEditorGroupView, IEditorPartsView, IInternalEditorOpenOptions } from './editor.js'; import { IEditorTabsControl } from './editorTabsControl.js'; +import { NoEditorTabsControl } from './noEditorTabsControl.js'; import { MultiEditorTabsControl } from './multiEditorTabsControl.js'; import { SingleEditorTabsControl } from './singleEditorTabsControl.js'; import { IEditorPartOptions } from '../../../common/editor.js'; @@ -17,7 +18,6 @@ import { EditorInput } from '../../../common/editor/editorInput.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { MultiRowEditorControl } from './multiRowEditorTabsControl.js'; import { IReadonlyEditorGroupModel } from '../../../common/editor/editorGroupModel.js'; -import { NoEditorTabsControl } from './noEditorTabsControl.js'; export interface IEditorTitleControlDimensions { diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index 0974b591f16c05..651b7ee3242b31 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -9,6 +9,13 @@ height: 100%; } +.monaco-workbench .part.editor > .content .editor-group-container .membrane-inner-shadow { + position: absolute; + inset: 0; + box-shadow: #11111199 0 0px 16px 0px inset; + pointer-events: none; +} + .monaco-workbench .part.editor > .content .editor-group-container.empty { opacity: 0.5; /* dimmed to indicate inactive state */ } @@ -50,19 +57,23 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .letterpress { - width: 100%; - max-height: 100%; + width: 50%; + max-height: 50%; aspect-ratio: 1/1; - background-image: url('./letterpress-light.svg'); + /* MEMRBANE: customize letterpress watermark */ + background-image: url('./membrane-letterpress-light.svg'); background-size: contain; background-position-x: center; background-repeat: no-repeat; } - .monaco-workbench.vs-dark .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress { - background-image: url('./letterpress-dark.svg'); + /* MEMRBANE: customize letterpress watermark */ + background-image: url('./membrane-letterpress-dark.svg'); } +/* + * MEMBRANE: rm hc letterpress watermark + .monaco-workbench.hc-light .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress { background-image: url('./letterpress-hcLight.svg'); } @@ -71,6 +82,8 @@ background-image: url('./letterpress-hcDark.svg'); } +*/ + .monaco-workbench .part.editor > .content:not(.empty) .editor-group-container > .editor-group-watermark > .shortcuts, .monaco-workbench .part.editor > .content.auxiliary .editor-group-container > .editor-group-watermark > .shortcuts, .monaco-workbench .part.editor > .content .editor-group-container.max-height-478px > .editor-group-watermark > .shortcuts { diff --git a/src/vs/workbench/browser/parts/editor/media/membrane-letterpress-dark.svg b/src/vs/workbench/browser/parts/editor/media/membrane-letterpress-dark.svg new file mode 100644 index 00000000000000..5d2fc30777bf56 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/media/membrane-letterpress-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/workbench/browser/parts/editor/media/membrane-letterpress-light.svg b/src/vs/workbench/browser/parts/editor/media/membrane-letterpress-light.svg new file mode 100644 index 00000000000000..da40a9c693bd81 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/media/membrane-letterpress-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css index 924d9b336078e4..e58cc413219f1d 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -558,3 +558,8 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:first-child.drop-target-right::before { width: 2px; } + +/* MEMBRANE: Always show close button (X) for active tabs */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-actions .action-label { + opacity: 1 !important; +} diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 332fcb391911cf..0e5eb2ef7c80be 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -31,7 +31,7 @@ import { activeContrastBorder, contrastBorder, editorBackground } from '../../.. import { ResourcesDropHandler, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, extractTreeDropData, isWindowDraggedOver } from '../../dnd.js'; import { Color } from '../../../../base/common/color.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; -import { MergeGroupMode, IMergeGroupOptions } from '../../../services/editor/common/editorGroupsService.js'; +import { MergeGroupMode, IMergeGroupOptions, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent, getWindow, $ } from '../../../../base/browser/dom.js'; import { localize } from '../../../../nls.js'; import { IEditorGroupsView, EditorServiceImpl, IEditorGroupView, IInternalEditorOpenOptions, IEditorPartsView, prepareMoveCopyEditors } from './editor.js'; @@ -152,8 +152,11 @@ export class MultiEditorTabsControl extends EditorTabsControl { @ITreeViewsDnDService private readonly treeViewsDragAndDropService: ITreeViewsDnDService, @IEditorResolverService editorResolverService: IEditorResolverService, @IHostService hostService: IHostService, + // MEMBRANE: inject editor groups and command services for membrane tab actions bar + @IEditorGroupsService editorGroupsService: IEditorGroupsService, + // @ICommandService commandService: ICommandService ) { - super(parent, editorPartsView, groupsView, groupView, tabsModel, contextMenuService, instantiationService, contextKeyService, keybindingService, notificationService, quickInputService, themeService, editorResolverService, hostService); + super(parent, editorPartsView, groupsView, groupView, tabsModel, contextMenuService, instantiationService, contextKeyService, keybindingService, notificationService, quickInputService, themeService, editorResolverService, hostService, editorGroupsService); // Resolve the correct path library for the OS we are on // If we are connected to remote, this accounts for the @@ -191,7 +194,8 @@ export class MultiEditorTabsControl extends EditorTabsControl { this.registerTabsContainerListeners(this.tabsContainer, this.tabsScrollbar); // Create Editor Toolbar - this.createEditorActionsToolBar(this.tabsAndActionsContainer, ['editor-actions']); + // MEMBRANE: Don't create editor actions toolbar (hides split, close, settings icons) + // this.createEditorActionsToolBar(this.tabsAndActionsContainer, ['editor-actions']); // Set tabs control visibility this.updateTabsControlVisibility(); @@ -1633,7 +1637,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { tabLabelWidget.setResource( { name, description, resource: EditorResourceAccessor.getOriginalUri(editor, { supportSideBySide: SideBySideEditor.BOTH }) }, { - title: this.getHoverTitle(editor), + title: editor.getTitle(Verbosity.LONG), extraClasses: coalesce(['tab-label', fileDecorationBadges ? 'tab-label-has-badge' : undefined].concat(editor.getLabelExtraClasses())), italic: !this.tabsModel.isPinned(editor), forceLabel, @@ -1868,7 +1872,9 @@ export class MultiEditorTabsControl extends EditorTabsControl { } private doLayoutTabsWrapping(dimensions: IEditorTitleControlDimensions): boolean { - const [tabsAndActionsContainer, tabsContainer, editorToolbarContainer, tabsScrollbar] = assertReturnsAllDefined(this.tabsAndActionsContainer, this.tabsContainer, this.editorActionsToolbarContainer, this.tabsScrollbar); + // MEMBRANE: Handle case where editorActionsToolbarContainer doesn't exist + const [tabsAndActionsContainer, tabsContainer, tabsScrollbar] = assertReturnsAllDefined(this.tabsAndActionsContainer, this.tabsContainer, this.tabsScrollbar); + const editorToolbarContainer = this.editorActionsToolbarContainer; // Handle wrapping tabs according to setting: // - enabled: only add class if tabs wrap and don't exceed available dimensions @@ -1886,7 +1892,8 @@ export class MultiEditorTabsControl extends EditorTabsControl { // Update `last-tab-margin-right` CSS variable to account for the absolute // positioned editor actions container when tabs wrap. The margin needs to // be the width of the editor actions container to avoid screen cheese. - tabsContainer.style.setProperty('--last-tab-margin-right', tabsWrapMultiLine ? `${editorToolbarContainer.offsetWidth}px` : '0'); + // MEMBRANE: Only set margin if toolbar exists + tabsContainer.style.setProperty('--last-tab-margin-right', tabsWrapMultiLine && editorToolbarContainer ? `${editorToolbarContainer.offsetWidth}px` : '0'); // Remove old css classes that are not needed anymore for (const tab of tabsContainer.children) { @@ -1904,7 +1911,8 @@ export class MultiEditorTabsControl extends EditorTabsControl { return true; // no tab always fits } - const lastTabOverlapWithToolbarWidth = lastTab.offsetWidth + editorToolbarContainer.offsetWidth - dimensions.available.width; + // MEMBRANE: Handle case where toolbar doesn't exist + const lastTabOverlapWithToolbarWidth = lastTab.offsetWidth + (editorToolbarContainer?.offsetWidth || 0) - dimensions.available.width; if (lastTabOverlapWithToolbarWidth > 1) { // Allow for slight rounding errors related to zooming here // https://github.com/microsoft/vscode/issues/116385 diff --git a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts index 0ef439569e1e54..4d961408d185eb 100644 --- a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts @@ -311,7 +311,7 @@ export class SingleEditorTabsControl extends EditorTabsControl { description }, { - title: this.getHoverTitle(editor), + title: editor.getTitle(Verbosity.LONG), italic: !isEditorPinned, extraClasses: ['single-tab', 'title-label'].concat(editor.getLabelExtraClasses()), fileDecorations: { diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index 48220d63dc3d5c..0a1c0725112239 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -724,6 +724,11 @@ export class SimpleGlobalActivityActionViewItem extends GlobalActivityActionView compact: true, }, () => undefined, userDataProfileService, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService, instantiationService, activityService); } + + protected override async resolveMainMenuActions(_menu: IMenu, _disposable: DisposableStore): Promise { + // Return empty array to remove all options from the settings icon menu + return []; + } } function simpleActivityContextMenuActions(storageService: IStorageService, isAccount: boolean): IAction[] { diff --git a/src/vs/workbench/browser/parts/media/compositepart.css b/src/vs/workbench/browser/parts/media/compositepart.css index c539ce2d5ac61a..d063df116716a3 100644 --- a/src/vs/workbench/browser/parts/media/compositepart.css +++ b/src/vs/workbench/browser/parts/media/compositepart.css @@ -12,6 +12,10 @@ display: flex; } +.monaco-workbench .part:not(.sidebar) > .composite.title > .back-to-membrane-navigator { + display: none; +} + .monaco-workbench .part > .composite.title > .title-actions { flex: 1; padding-left: 8px; diff --git a/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts new file mode 100644 index 00000000000000..9f7524274d11b8 --- /dev/null +++ b/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts @@ -0,0 +1,330 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { INotificationsModel, NotificationChangeType, INotificationChangeEvent, INotificationViewItem } from '../../../common/notifications.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { Dimension } from '../../../../base/browser/dom.js'; +import { INotificationsToastController } from './notificationsCommands.js'; +import { Event, Emitter } from '../../../../base/common/event.js'; +import { ILifecycleService, LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; +import { NotificationsFilter, NotificationPriority, Severity } from '../../../../platform/notification/common/notification.js'; +import { IntervalCounter } from '../../../../base/common/async.js'; +import { NotificationsToastsVisibleContext } from '../../../common/contextkeys.js'; +import { IContextKeyService, IContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { MembranePortManager } from '../../../../base/browser/ui/dialog/membranePortManager.js'; + +declare global { + interface Window { + membraneNotificationActionHandler?: (response: MembraneNotificationActionResponse) => void; + } +} + +interface MembraneNotificationActionResponse { + notificationId: string; + actionId?: string; + dismissed?: boolean; +} + +export class MembraneNotificationsToasts extends Disposable implements INotificationsToastController { + + private static readonly MAX_NOTIFICATIONS = 3; + private static readonly SPAM_PROTECTION = { + interval: 800, + limit: this.MAX_NOTIFICATIONS + }; + + private readonly _onDidChangeVisibility = this._register(new Emitter()); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; + + private _isVisible = false; + get isVisible(): boolean { return this._isVisible; } + + private isNotificationsCenterVisible: boolean | undefined; + private readonly addedToastsIntervalCounter = new IntervalCounter(MembraneNotificationsToasts.SPAM_PROTECTION.interval); + private readonly notificationsToastsVisibleContextKey: IContextKey; + + private readonly activeNotifications = new Map(); + private readonly disposables = this._register(new DisposableStore()); + + constructor( + private readonly model: INotificationsModel, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(); + this.notificationsToastsVisibleContextKey = NotificationsToastsVisibleContext.bindTo(contextKeyService); + this.registerListeners(); + + // Initialize port manager to set up notification response listener + MembranePortManager.ensureInitialized(); + // Register this instance as the notification response handler + MembranePortManager.setNotificationResponseHandler((response: unknown) => { + this.handleNotificationAction(response as MembraneNotificationActionResponse); + }); + } + + private registerListeners(): void { + this.lifecycleService.when(LifecyclePhase.Restored).then(() => { + // Show toast for initial notifications if any + this.model.notifications.forEach(notification => this.addToast(notification)); + + // Update toasts on notification changes + this.disposables.add(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e))); + }); + + // Filter handling + this.disposables.add(this.model.onDidChangeFilter(({ global, sources }) => { + if (global === NotificationsFilter.ERROR) { + this.hide(); + } else if (sources) { + for (const [, notification] of this.activeNotifications) { + if (typeof notification.sourceId === 'string' && sources.get(notification.sourceId) === NotificationsFilter.ERROR && notification.severity !== Severity.Error && notification.priority !== NotificationPriority.URGENT) { + this.removeToast(notification); + } + } + } + })); + } + + private onDidChangeNotification(e: INotificationChangeEvent): void { + switch (e.kind) { + case NotificationChangeType.ADD: + return this.addToast(e.item); + case NotificationChangeType.REMOVE: + return this.removeToast(e.item); + } + } + + private addToast(item: INotificationViewItem): void { + if (this.isNotificationsCenterVisible) { + return; // do not show toasts while notification center is visible + } + + if (item.priority === NotificationPriority.SILENT) { + return; // do not show toasts for silenced notifications + } + + // Filter out unwanted notifications + if (this.shouldFilterNotification(item)) { + return; + } + + // Spam protection - same as original + if (this.addedToastsIntervalCounter.increment() > MembraneNotificationsToasts.SPAM_PROTECTION.limit) { + return; + } + + // Generate a unique ID if none exists and store it on the item + if (!item.id) { + (item as { id?: string }).id = `notification-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + } + + // At this point we know item.id exists + const notificationId = item.id!; + + // Forward notification to Gaze instead of creating DOM toast + this.forwardNotificationToGaze(item); + + // Track the notification using the guaranteed ID + this.activeNotifications.set(notificationId, item); + + // Update visibility state + if (!this._isVisible) { + this._isVisible = true; + this.notificationsToastsVisibleContextKey.set(true); + this._onDidChangeVisibility.fire(); + } + + // Mark as visible in the item + item.updateVisibility(true); + + // Handle item close event + Event.once(item.onDidClose)(() => { + this.removeToast(item); + }); + } + + private forwardNotificationToGaze(item: INotificationViewItem): void { + const notificationId = item.id!; + + const notificationData = { + id: notificationId, + severity: this.severityToString(item.severity), + message: item.message.raw, + source: item.source, + sticky: item.sticky, + timestamp: Date.now(), + actions: item.actions?.primary?.map(action => ({ + id: action.id, + label: action.label, + enabled: action.enabled + })) || [] + }; + + // Send notification via MembranePortManager + + MembranePortManager.ensureInitialized(); + MembranePortManager.sendMessage('membraneNotification', { + type: 'toast', + id: `notification-${notificationId}`, + notification: notificationData + }); + } + + private severityToString(severity: Severity): string { + switch (severity) { + case Severity.Info: return 'info'; + case Severity.Warning: return 'warning'; + case Severity.Error: return 'error'; + default: return 'info'; + } + } + + private shouldFilterNotification(item: INotificationViewItem): boolean { + const message = item.message.raw.toLowerCase(); + + // Filter out extension activation notifications + if (message.includes('activating extension')) { + return true; + } + + return false; + } + + private removeToast(item: INotificationViewItem): void { + // Remove from tracking + if (item.id) { + this.activeNotifications.delete(item.id); + + // Notify Gaze to hide the notification via MembranePortManager + MembranePortManager.sendMessage('membraneNotification', { + type: 'hide', + id: `notification-${item.id}` + }); + } + + // Update visibility if no more notifications + if (this.activeNotifications.size === 0) { + this._isVisible = false; + this.notificationsToastsVisibleContextKey.set(false); + this._onDidChangeVisibility.fire(); + } + + // Mark as not visible in the item + item.updateVisibility(false); + } + + // Required interface methods + hide(): void { + // Hide all active notifications + for (const [, item] of this.activeNotifications) { + this.removeToast(item); + } + } + + focus(): boolean { + // For keyboard navigation send focus request to Gaze via MembranePortManager + if (this.activeNotifications.size > 0) { + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'first' + }); + return true; + } + return false; + } + + focusNext(): boolean { + if (this.activeNotifications.size > 0) { + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'next' + }); + return true; + } + return false; + } + + focusPrevious(): boolean { + if (this.activeNotifications.size > 0) { + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'previous' + }); + return true; + } + return false; + } + + focusFirst(): boolean { + if (this.activeNotifications.size > 0) { + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'first' + }); + return true; + } + return false; + } + + focusLast(): boolean { + if (this.activeNotifications.size > 0) { + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'last' + }); + return true; + } + return false; + } + + update(isCenterVisible: boolean): void { + if (this.isNotificationsCenterVisible !== isCenterVisible) { + this.isNotificationsCenterVisible = isCenterVisible; + + // Hide all toasts when the notification center gets visible + if (this.isNotificationsCenterVisible) { + this.hide(); + } + } + } + + layout(_dimension: Dimension | undefined): void { + // No-op for Membrane - Gaze handles its own layout + } + + private handleNotificationAction(response: MembraneNotificationActionResponse): void { + if (!response || !response.notificationId) { + return; + } + + const notificationId = response.notificationId; + const actionId = response.actionId; + const dismissed = response.dismissed; + + // Find the notification by ID + for (const [, item] of this.activeNotifications) { + if (item.id === notificationId) { + if (dismissed) { + // User dismissed the notification + item.close(); + } else if (actionId && item.actions?.primary) { + // User clicked an action + const action = item.actions.primary.find(a => a.id === actionId); + if (action) { + // Execute the action + action.run(); + } + } + break; + } + } + } + + override dispose(): void { + this.disposables.dispose(); + super.dispose(); + } +} diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index a452575cf9e991..53d5469cc75e90 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -21,6 +21,7 @@ import { INotificationService } from '../../../platform/notification/common/noti import { IStorageService } from '../../../platform/storage/common/storage.js'; import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js'; import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js'; +import { ICommandService } from '../../../platform/commands/common/commands.js'; import { IThemeService } from '../../../platform/theme/common/themeService.js'; import { IContextKey, IContextKeyService } from '../../../platform/contextkey/common/contextkey.js'; import { IExtensionService } from '../../services/extensions/common/extensions.js'; @@ -150,6 +151,7 @@ export abstract class AbstractPaneCompositePart extends CompositePart) { diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 585bc16cde2238..43ecbefa424933 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -84,7 +84,7 @@ export class PanelPart extends AbstractPaneCompositePart { ) { super( Parts.PANEL_PART, - { hasTitle: true, trailingSeparator: true }, + { hasTitle: true }, PanelPart.activePanelSettingsKey, ActivePanelContext.bindTo(contextKeyService), PanelFocusContext.bindTo(contextKeyService), @@ -100,6 +100,7 @@ export class PanelPart extends AbstractPaneCompositePart { hoverService, instantiationService, themeService, + commandService, viewDescriptorService, contextKeyService, extensionService, diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 470b69dea83489..7506460ef50271 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -10,6 +10,7 @@ import { SidebarFocusContext, ActiveViewletContext } from '../../../common/conte import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { contrastBorder } from '../../../../platform/theme/common/colorRegistry.js'; @@ -39,7 +40,8 @@ export class SidebarPart extends AbstractPaneCompositePart { //#region IView - readonly minimumWidth: number = 170; + // MEMBRANE: adjust min width of sidebar (this could be smaller) + readonly minimumWidth: number = 252; readonly maximumWidth: number = Number.POSITIVE_INFINITY; readonly minimumHeight: number = 0; readonly maximumHeight: number = Number.POSITIVE_INFINITY; @@ -75,6 +77,7 @@ export class SidebarPart extends AbstractPaneCompositePart { @IHoverService hoverService: IHoverService, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, + @ICommandService commandService: ICommandService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IExtensionService extensionService: IExtensionService, @@ -83,7 +86,7 @@ export class SidebarPart extends AbstractPaneCompositePart { ) { super( Parts.SIDEBAR_PART, - { hasTitle: true, trailingSeparator: false, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 }, + { hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 }, SidebarPart.activeViewletSettingsKey, ActiveViewletContext.bindTo(contextKeyService), SidebarFocusContext.bindTo(contextKeyService), @@ -99,6 +102,7 @@ export class SidebarPart extends AbstractPaneCompositePart { hoverService, instantiationService, themeService, + commandService, viewDescriptorService, contextKeyService, extensionService, diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 743f9e6ee8bbae..d4093e962450ae 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -18,7 +18,7 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER, WORKBENCH_BACKGROUND } from '../../../common/theme.js'; import { isMacintosh, isWindows, isLinux, isWeb, isNative, platformLocale } from '../../../../base/common/platform.js'; import { Color } from '../../../../base/common/color.js'; -import { EventType, EventHelper, Dimension, append, $, addDisposableListener, prepend, reset, getWindow, getWindowId, isAncestor, getActiveDocument, isHTMLElement } from '../../../../base/browser/dom.js'; +import { Dimension, append, $, prepend, reset, getWindow, getWindowId, getActiveDocument } from '../../../../base/browser/dom.js'; import { CustomMenubarControl } from './menubarControl.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; @@ -47,7 +47,7 @@ import { ResolvedKeybinding } from '../../../../base/common/keybindings.js'; import { EditorCommandsContextActionRunner } from '../editor/editorTabsControl.js'; import { IEditorCommandsContext, IEditorPartOptionsChangeEvent, IToolbarActions } from '../../../common/editor.js'; import { CodeWindow, mainWindow } from '../../../../base/browser/window.js'; -import { ACCOUNTS_ACTIVITY_TILE_ACTION, GLOBAL_ACTIVITY_TITLE_ACTION } from './titlebarActions.js'; +import { ACCOUNTS_ACTIVITY_TILE_ACTION } from './titlebarActions.js'; import { IView } from '../../../../base/browser/ui/grid/grid.js'; import { createInstantHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; @@ -524,30 +524,31 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Windows / Linux: we only support the overall context menu on the title bar // macOS: we support both the overall context menu and the title context menu. // in addition, we allow Cmd+click to bring up the title context menu. - { - this._register(addDisposableListener(this.rootContainer, EventType.CONTEXT_MENU, e => { - EventHelper.stop(e); - - let targetMenu: MenuId; - if (isMacintosh && isHTMLElement(e.target) && isAncestor(e.target, this.title)) { - targetMenu = MenuId.TitleBarTitleContext; - } else { - targetMenu = MenuId.TitleBarContext; - } - - this.onContextMenu(e, targetMenu); - })); - - if (isMacintosh) { - this._register(addDisposableListener(this.title, EventType.MOUSE_DOWN, e => { - if (e.metaKey) { - EventHelper.stop(e, true /* stop bubbling to prevent command center from opening */); - - this.onContextMenu(e, MenuId.TitleBarTitleContext); - } - }, true /* capture phase to prevent command center from opening */)); - } - } + // MEMBRANE: Disabled context menu on titlebar to remove the settings menu + // { + // this._register(addDisposableListener(this.rootContainer, EventType.CONTEXT_MENU, e => { + // EventHelper.stop(e); + + // let targetMenu: MenuId; + // if (isMacintosh && isHTMLElement(e.target) && isAncestor(e.target, this.title)) { + // targetMenu = MenuId.TitleBarTitleContext; + // } else { + // targetMenu = MenuId.TitleBarContext; + // } + + // this.onContextMenu(e, targetMenu); + // })); + + // if (isMacintosh) { + // this._register(addDisposableListener(this.title, EventType.MOUSE_DOWN, e => { + // if (e.metaKey) { + // EventHelper.stop(e, true /* stop bubbling to prevent command center from opening */); + + // this.onContextMenu(e, MenuId.TitleBarTitleContext); + // } + // }, true /* capture phase to prevent command center from opening */)); + // } + // } this.updateStyles(); @@ -685,7 +686,8 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { actions.primary.push(ACCOUNTS_ACTIVITY_TILE_ACTION); } - actions.primary.push(GLOBAL_ACTIVITY_TITLE_ACTION); + // MEMBRANE: Don't add global activity action (hides settings icon) + // actions.primary.push(GLOBAL_ACTIVITY_TITLE_ACTION); } this.actionToolBar.setActions(prepareActions(actions.primary), prepareActions(actions.secondary)); diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index 6266395de8dc51..b2f96cd94f1476 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -4,24 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from '../../../../nls.js'; -import { dirname, basename } from '../../../../base/common/resources.js'; import { ITitleProperties, ITitleVariable } from './titlebarPart.js'; import { IConfigurationService, IConfigurationChangeEvent } from '../../../../platform/configuration/common/configuration.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { EditorResourceAccessor, Verbosity, SideBySideEditor } from '../../../common/editor.js'; +import { Verbosity } from '../../../common/editor.js'; import { IBrowserWorkbenchEnvironmentService } from '../../../services/environment/browser/environmentService.js'; -import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { isWindows, isWeb, isMacintosh, isNative } from '../../../../base/common/platform.js'; -import { URI } from '../../../../base/common/uri.js'; -import { trim } from '../../../../base/common/strings.js'; -import { template } from '../../../../base/common/labels.js'; -import { ILabelService, Verbosity as LabelVerbosity } from '../../../../platform/label/common/label.js'; +import { ILabelService } from '../../../../platform/label/common/label.js'; import { Emitter } from '../../../../base/common/event.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; -import { Schemas } from '../../../../base/common/network.js'; -import { getVirtualWorkspaceLocation } from '../../../../platform/workspace/common/virtualWorkspace.js'; import { IUserDataProfileService } from '../../../services/userDataProfile/common/userDataProfile.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; @@ -93,7 +87,8 @@ export class WindowTitle extends Disposable { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ILabelService private readonly labelService: ILabelService, @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, - @IProductService private readonly productService: IProductService, + // @ts-ignore - Unused due to MEMBRANE customization (commented out code) + @IProductService private readonly _productService: IProductService, @IViewsService private readonly viewsService: IViewsService, @IDecorationsService private readonly decorationsService: IDecorationsService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService @@ -187,26 +182,9 @@ export class WindowTitle extends Disposable { private doUpdateTitle(): void { const title = this.getFullWindowTitle(); if (title !== this.title) { - - // Always set the native window title to identify us properly to the OS - let nativeTitle = title; - if (!trim(nativeTitle)) { - nativeTitle = this.productService.nameLong; - } - + // MEMBRANE: Set window title to empty string const window = getWindowById(this.windowId, true).window; - if (!window.document.title && isMacintosh && nativeTitle === this.productService.nameLong) { - // TODO@electron macOS: if we set a window title for - // the first time and it matches the one we set in - // `windowImpl.ts` somehow the window does not appear - // in the "Windows" menu. As such, we set the title - // briefly to something different to ensure macOS - // recognizes we have a window. - // See: https://github.com/microsoft/vscode/issues/191288 - window.document.title = `${this.productService.nameLong} ${WindowTitle.TITLE_DIRTY}`; - } - - window.document.title = nativeTitle; + window.document.title = ''; this.title = title; this.onDidChangeEmitter.fire(); @@ -214,19 +192,8 @@ export class WindowTitle extends Disposable { } private getFullWindowTitle(): string { - const { prefix, suffix } = this.getTitleDecorations(); - - let title = this.getWindowTitle() || this.productService.nameLong; - if (prefix) { - title = `${prefix} ${title}`; - } - - if (suffix) { - title = `${title} ${suffix}`; - } - - // Replace non-space whitespace - return title.replace(/[^\S ]/g, ' '); + // MEMBRANE: Don't show anything in the window title + return ''; } getTitleDecorations() { @@ -301,105 +268,108 @@ export class WindowTitle extends Disposable { * {activeEditorState}: e.g. Modified */ getWindowTitle(): string { - const editor = this.editorService.activeEditor; - const workspace = this.contextService.getWorkspace(); - - // Compute root - let root: URI | undefined; - if (workspace.configuration) { - root = workspace.configuration; - } else if (workspace.folders.length) { - root = workspace.folders[0].uri; - } - - // Compute active editor folder - const editorResource = EditorResourceAccessor.getOriginalUri(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); - let editorFolderResource = editorResource ? dirname(editorResource) : undefined; - if (editorFolderResource?.path === '.') { - editorFolderResource = undefined; - } - - // Compute folder resource - // Single Root Workspace: always the root single workspace in this case - // Otherwise: root folder of the currently active file if any - let folder: IWorkspaceFolder | undefined = undefined; - if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { - folder = workspace.folders[0]; - } else if (editorResource) { - folder = this.contextService.getWorkspaceFolder(editorResource) ?? undefined; - } - - // Compute remote - // vscode-remtoe: use as is - // otherwise figure out if we have a virtual folder opened - let remoteName: string | undefined = undefined; - if (this.environmentService.remoteAuthority && !isWeb) { - remoteName = this.labelService.getHostLabel(Schemas.vscodeRemote, this.environmentService.remoteAuthority); - } else { - const virtualWorkspaceLocation = getVirtualWorkspaceLocation(workspace); - if (virtualWorkspaceLocation) { - remoteName = this.labelService.getHostLabel(virtualWorkspaceLocation.scheme, virtualWorkspaceLocation.authority); - } - } - - // Variables - const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : ''; - const activeEditorMedium = editor ? editor.getTitle(Verbosity.MEDIUM) : activeEditorShort; - const activeEditorLong = editor ? editor.getTitle(Verbosity.LONG) : activeEditorMedium; - const activeFolderShort = editorFolderResource ? basename(editorFolderResource) : ''; - const activeFolderMedium = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource, { relative: true }) : ''; - const activeFolderLong = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource) : ''; - const rootName = this.labelService.getWorkspaceLabel(workspace); - const rootNameShort = this.labelService.getWorkspaceLabel(workspace, { verbose: LabelVerbosity.SHORT }); - const rootPath = root ? this.labelService.getUriLabel(root) : ''; - const folderName = folder ? folder.name : ''; - const folderPath = folder ? this.labelService.getUriLabel(folder.uri) : ''; - const dirty = editor?.isDirty() && !editor.isSaving() ? WindowTitle.TITLE_DIRTY : ''; - const appName = this.productService.nameLong; - const profileName = this.userDataProfileService.currentProfile.isDefault ? '' : this.userDataProfileService.currentProfile.name; - const focusedView: string = this.viewsService.getFocusedViewName(); - const activeEditorState = editorResource ? this.decorationsService.getDecoration(editorResource, false)?.tooltip : undefined; - - const variables: Record = {}; - for (const [contextKey, name] of this.variables) { - variables[name] = this.contextKeyService.getContextKeyValue(contextKey) ?? ''; - } - - let titleTemplate = this.configurationService.getValue(WindowSettingNames.title); - if (typeof titleTemplate !== 'string') { - titleTemplate = defaultWindowTitle; - } - - if (!this.titleIncludesEditorState && this.accessibilityService.isScreenReaderOptimized() && this.configurationService.getValue('accessibility.windowTitleOptimized')) { - titleTemplate += '${separator}${activeEditorState}'; - } - - let separator = this.configurationService.getValue(WindowSettingNames.titleSeparator); - if (typeof separator !== 'string') { - separator = defaultWindowTitleSeparator; - } - - return template(titleTemplate, { - ...variables, - activeEditorShort, - activeEditorLong, - activeEditorMedium, - activeFolderShort, - activeFolderMedium, - activeFolderLong, - rootName, - rootPath, - rootNameShort, - folderName, - folderPath, - dirty, - appName, - remoteName, - profileName, - focusedView, - activeEditorState, - separator: { label: separator } - }); + // MEMBRANE: Don't show filename in window title, return empty string + return ''; + + // Original implementation (commented out for MEMBRANE customization): + // const workspace = this.contextService.getWorkspace(); + // + // // Compute root + // let root: URI | undefined; + // if (workspace.configuration) { + // root = workspace.configuration; + // } else if (workspace.folders.length) { + // root = workspace.folders[0].uri; + // } + // + // // Compute active editor folder + // const editorResource = EditorResourceAccessor.getOriginalUri(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); + // let editorFolderResource = editorResource ? dirname(editorResource) : undefined; + // if (editorFolderResource?.path === '.') { + // editorFolderResource = undefined; + // } + // + // // Compute folder resource + // // Single Root Workspace: always the root single workspace in this case + // // Otherwise: root folder of the currently active file if any + // let folder: IWorkspaceFolder | undefined = undefined; + // if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + // folder = workspace.folders[0]; + // } else if (editorResource) { + // folder = this.contextService.getWorkspaceFolder(editorResource) ?? undefined; + // } + // + // // Compute remote + // // vscode-remtoe: use as is + // // otherwise figure out if we have a virtual folder opened + // let remoteName: string | undefined = undefined; + // if (this.environmentService.remoteAuthority && !isWeb) { + // remoteName = this.labelService.getHostLabel(Schemas.vscodeRemote, this.environmentService.remoteAuthority); + // } else { + // const virtualWorkspaceLocation = getVirtualWorkspaceLocation(workspace); + // if (virtualWorkspaceLocation) { + // remoteName = this.labelService.getHostLabel(virtualWorkspaceLocation.scheme, virtualWorkspaceLocation.authority); + // } + // } + // + // // Variables + // const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : ''; + // const activeEditorMedium = editor ? editor.getTitle(Verbosity.MEDIUM) : activeEditorShort; + // const activeEditorLong = editor ? editor.getTitle(Verbosity.LONG) : activeEditorMedium; + // const activeFolderShort = editorFolderResource ? basename(editorFolderResource) : ''; + // const activeFolderMedium = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource, { relative: true }) : ''; + // const activeFolderLong = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource) : ''; + // const rootName = this.labelService.getWorkspaceLabel(workspace); + // const rootNameShort = this.labelService.getWorkspaceLabel(workspace, { verbose: LabelVerbosity.SHORT }); + // const rootPath = root ? this.labelService.getUriLabel(root) : ''; + // const folderName = folder ? folder.name : ''; + // const folderPath = folder ? this.labelService.getUriLabel(folder.uri) : ''; + // const dirty = editor?.isDirty() && !editor.isSaving() ? WindowTitle.TITLE_DIRTY : ''; + // const appName = this.productService.nameLong; + // const profileName = this.userDataProfileService.currentProfile.isDefault ? '' : this.userDataProfileService.currentProfile.name; + // const focusedView: string = this.viewsService.getFocusedViewName(); + // const activeEditorState = editorResource ? this.decorationsService.getDecoration(editorResource, false)?.tooltip : undefined; + // + // const variables: Record = {}; + // for (const [contextKey, name] of this.variables) { + // variables[name] = this.contextKeyService.getContextKeyValue(contextKey) ?? ''; + // } + // + // let titleTemplate = this.configurationService.getValue(WindowSettingNames.title); + // if (typeof titleTemplate !== 'string') { + // titleTemplate = defaultWindowTitle; + // } + // + // if (!this.titleIncludesEditorState && this.accessibilityService.isScreenReaderOptimized() && this.configurationService.getValue('accessibility.windowTitleOptimized')) { + // titleTemplate += '${separator}${activeEditorState}'; + // } + // + // let separator = this.configurationService.getValue(WindowSettingNames.titleSeparator); + // if (typeof separator !== 'string') { + // separator = defaultWindowTitleSeparator; + // } + // + // return template(titleTemplate, { + // ...variables, + // activeEditorShort, + // activeEditorLong, + // activeEditorMedium, + // activeFolderShort, + // activeFolderMedium, + // activeFolderLong, + // rootName, + // rootPath, + // rootNameShort, + // folderName, + // folderPath, + // dirty, + // appName, + // remoteName, + // profileName, + // focusedView, + // activeEditorState, + // separator: { label: separator } + // }); } isCustomTitleFormat(): boolean { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index b0db8565d0c48c..65fbb5e408b250 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -138,6 +138,25 @@ export class BrowserMain extends Disposable { // Logging services.logService.trace('workbench#open with configuration', safeStringify(this.configuration)); + // instantiationService.invokeFunction(accessor => { + // const telemetryService = accessor.get(ITelemetryService); + // for (const indexedDbFileSystemProvider of this.indexedDBFileSystemProviders) { + // this._register(indexedDbFileSystemProvider.onReportError(e => telemetryService.publicLog2('indexedDBFileSystemProviderError', e))); + // } + // }); + + // MEMBRANE: We listen here instead of workbench.ts (or elsewhere) because we have access to both: + // (1) window, and (2) the command service. + instantiationService.invokeFunction(accessor => { + const commandService = accessor.get(ICommandService); + // MEMBRANE: Listen for event emitted by product tour to show package installer. + // eslint-disable-next-line no-restricted-globals + window.addEventListener('tour:show-learn-membrane', async () => { + await commandService.executeCommand('membrane.installPackage', 'membrane/learn-membrane'); + }); + // MEMBRANE: gazeToExtension and gazeToWorkbench handling moved to MessagePort in workbench.ts + }); + // Return API Facade return instantiationService.invokeFunction(accessor => { const commandService = accessor.get(ICommandService); diff --git a/src/vs/workbench/browser/window.ts b/src/vs/workbench/browser/window.ts index 63dfbb43d35696..5fd4aea1a134e0 100644 --- a/src/vs/workbench/browser/window.ts +++ b/src/vs/workbench/browser/window.ts @@ -350,6 +350,12 @@ export class BrowserWindow extends BaseWindow { } } + // MEMBRANE: special case for logout + if (href === 'https://membrane.io/?logout=true') { + mainWindow.location.href = '/?logout=true'; + return true; + } + // HTTP(s): open in new window and deal with potential popup blockers if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) { if (isSafari) { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 4850bf415869c7..96b7a3127b3dbe 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -65,7 +65,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('workbench.editor.showTabs.none', "The editor title area is not displayed."), ], 'description': localize('showEditorTabs', "Controls whether opened editors should show as individual tabs, one single large tab or if the title area should not be shown."), - 'default': 'multiple' + 'default': 'none' }, [LayoutSettings.EDITOR_ACTIONS_LOCATION]: { 'type': 'string', @@ -536,7 +536,8 @@ const registry = Registry.as(ConfigurationExtensions.Con 'workbench.sideBar.location': { 'type': 'string', 'enum': ['left', 'right'], - 'default': 'left', + // MEMBRANE: Set default to right + 'default': 'right', 'description': localize('sideBarLocation', "Controls the location of the primary side bar and activity bar. They can either show on the left or right of the workbench. The secondary side bar will show on the opposite side of the workbench.") }, 'workbench.panel.showLabels': { @@ -590,19 +591,18 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'workbench.statusBar.visible': { 'type': 'boolean', - 'default': true, + // MEMBRANE: Set default to false + 'default': false, 'description': localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.") }, [LayoutSettings.ACTIVITY_BAR_LOCATION]: { 'type': 'string', - 'enum': ['default', 'top', 'bottom', 'hidden'], - 'default': 'default', - 'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the Activity Bar relative to the Primary and Secondary Side Bars."), + 'enum': ['top'], + // MEMBRANE: Set default to top + 'default': 'top', + 'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the Activity Bar. It can only show to the `top` of the Primary Side Bar."), 'enumDescriptions': [ - localize('workbench.activityBar.location.default', "Show the Activity Bar on the side of the Primary Side Bar and on top of the Secondary Side Bar."), - localize('workbench.activityBar.location.top', "Show the Activity Bar on top of the Primary and Secondary Side Bars."), - localize('workbench.activityBar.location.bottom', "Show the Activity Bar at the bottom of the Primary and Secondary Side Bars."), - localize('workbench.activityBar.location.hide', "Hide the Activity Bar in the Primary and Secondary Side Bars.") + localize('workbench.activityBar.location.top', "Show the Activity Bar on top of the Primary Side Bar."), ], }, 'workbench.activityBar.iconClickBehavior': { diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index afe48b84b2e263..bddcd8182c064c 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -27,7 +27,6 @@ import { NotificationsCenter } from './parts/notifications/notificationsCenter.j import { NotificationsAlerts } from './parts/notifications/notificationsAlerts.js'; import { NotificationsStatus } from './parts/notifications/notificationsStatus.js'; import { registerNotificationCommands } from './parts/notifications/notificationsCommands.js'; -import { NotificationsToasts } from './parts/notifications/notificationsToasts.js'; import { setARIAContainer } from '../../base/browser/ui/aria/aria.js'; import { FontMeasurements } from '../../editor/browser/config/fontMeasurements.js'; import { createBareFontInfoFromRawSettings } from '../../editor/common/config/fontInfoFromSettings.js'; @@ -50,6 +49,7 @@ import { AccessibleViewRegistry } from '../../platform/accessibility/browser/acc import { NotificationAccessibleView } from './parts/notifications/notificationAccessibleView.js'; import { IMarkdownRendererService } from '../../platform/markdown/browser/markdownRenderer.js'; import { EditorMarkdownCodeBlockRenderer } from '../../editor/browser/widget/markdownRenderer/browser/editorMarkdownCodeBlockRenderer.js'; +import { MembraneNotificationsToasts } from './parts/notifications/membraneNotificationsToasts.js'; export interface IWorkbenchOptions { @@ -378,7 +378,9 @@ export class Workbench extends Layout { // Instantiate Notification components const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.mainContainer, notificationService.model)); - const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.mainContainer, notificationService.model)); + // MEMBRANE: Using our own toasts. + const notificationsToasts: MembraneNotificationsToasts = instantiationService.createInstance(MembraneNotificationsToasts, notificationService.model); + this._register(notificationsToasts); this._register(instantiationService.createInstance(NotificationsAlerts, notificationService.model)); const notificationsStatus = instantiationService.createInstance(NotificationsStatus, notificationService.model); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 921a24197d5c4f..d0ec9918361cd7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -3,86 +3,109 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction } from '../../../../base/common/actions.js'; +// import { IAction } from '../../../../base/common/actions.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IStringDictionary } from '../../../../base/common/collections.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; -import { Event } from '../../../../base/common/event.js'; +// import { Event } from '../../../../base/common/event.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; -import { mnemonicButtonLabel } from '../../../../base/common/labels.js'; -import { Disposable, DisposableStore, IDisposable, isDisposable } from '../../../../base/common/lifecycle.js'; -import { Schemas } from '../../../../base/common/network.js'; -import { isNative, isWeb } from '../../../../base/common/platform.js'; -import { PolicyCategory } from '../../../../base/common/policy.js'; +// import { mnemonicButtonLabel } from '../../../../base/common/labels.js'; +// import { Disposable, DisposableStore, IDisposable, isDisposable } from '../../../../base/common/lifecycle.js'; +// import { Schemas } from '../../../../base/common/network.js'; +// import { isNative, isWeb } from '../../../../base/common/platform.js'; +import { isWeb } from '../../../../base/common/platform.js'; +// import { PolicyCategory } from '../../../../base/common/policy.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; import { MultiCommand } from '../../../../editor/browser/editorExtensions.js'; import { CopyAction, CutAction, PasteAction } from '../../../../editor/contrib/clipboard/browser/clipboard.js'; import { localize, localize2 } from '../../../../nls.js'; -import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { Action2, IAction2Options, IMenuItem, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; +// import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +import { + // Action2, IAction2Options, IMenuItem, MenuId, MenuRegistry, + registerAction2 +} from '../../../../platform/actions/common/actions.js'; +// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js'; import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; -import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { IDialogService, IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { ExtensionGalleryManifestStatus, ExtensionGalleryResourceType, ExtensionGalleryServiceUrlConfigKey, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; -import { EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, ExtensionRequestsTimeoutConfigKey, ExtensionsLocalizedLabel, FilterType, IExtensionGalleryService, IExtensionManagementService, PreferencesLocalizedLabel, SortBy, VerifyExtensionSignatureConfigKey } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { + // ContextKeyExpr, IContextKeyService, + RawContextKey +} from '../../../../platform/contextkey/common/contextkey.js'; +// import { IDialogService, IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +// import { ExtensionGalleryManifestStatus, ExtensionGalleryResourceType, ExtensionGalleryServiceUrlConfigKey, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; +import { + // EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, ExtensionRequestsTimeoutConfigKey, ExtensionsLocalizedLabel, FilterType, IExtensionGalleryService, + IExtensionManagementService, + InstallOptions, + // PreferencesLocalizedLabel, SortBy, VerifyExtensionSignatureConfigKey +} from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { areSameExtensions, getIdAndVersion } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; import { ExtensionStorageService } from '../../../../platform/extensionManagement/common/extensionStorage.js'; + import { IExtensionRecommendationNotificationService } from '../../../../platform/extensionRecommendations/common/extensionRecommendations.js'; -import { EXTENSION_CATEGORIES, ExtensionType } from '../../../../platform/extensions/common/extensions.js'; +// import { EXTENSION_CATEGORIES, ExtensionType } from '../../../../platform/extensions/common/extensions.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +// import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import * as jsonContributionRegistry from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; -import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; -import product from '../../../../platform/product/common/product.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; -import { ProgressLocation } from '../../../../platform/progress/common/progress.js'; +// import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; +// import product from '../../../../platform/product/common/product.js'; +// import { IProductService } from '../../../../platform/product/common/productService.js'; +// import { ProgressLocation } from '../../../../platform/progress/common/progress.js'; import { Extensions, IQuickAccessRegistry } from '../../../../platform/quickinput/common/quickAccess.js'; -import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; +// import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; -import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; +import { IStorageService } from '../../../../platform/storage/common/storage.js'; +// import { StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +// import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +// import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; -import { Extensions as ConfigurationMigrationExtensions, IConfigurationMigrationRegistry } from '../../../common/configuration.js'; -import { ResourceContextKey, WorkbenchStateContext } from '../../../common/contextkeys.js'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js'; +// import { Extensions as ConfigurationMigrationExtensions, IConfigurationMigrationRegistry } from '../../../common/configuration.js'; +// import { ResourceContextKey, WorkbenchStateContext } from '../../../common/contextkeys.js'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js'; +// import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; import { EditorExtensions } from '../../../common/editor.js'; -import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation } from '../../../common/views.js'; -import { DEFAULT_ACCOUNT_SIGN_IN_COMMAND } from '../../../services/accounts/common/defaultAccount.js'; +import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, ViewContainer } from '../../../common/views.js'; +import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js'; +// import { DEFAULT_ACCOUNT_SIGN_IN_COMMAND } from '../../../services/accounts/common/defaultAccount.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { EnablementState, IExtensionManagementServerService, IPublisherInfo, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js'; -import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; -import { IWorkspaceExtensionsConfigService } from '../../../services/extensionRecommendations/common/workspaceExtensionsConfig.js'; -import { IHostService } from '../../../services/host/browser/host.js'; +import { EnablementState, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js'; +// import { IExtensionManagementServerService, IPublisherInfo, IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js'; +import { IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; +// import { IExtensionIgnoredRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; +// import { IWorkspaceExtensionsConfigService } from '../../../services/extensionRecommendations/common/workspaceExtensionsConfig.js'; +// import { IHostService } from '../../../services/host/browser/host.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; -import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; -import { CONTEXT_SYNC_ENABLEMENT } from '../../../services/userDataSync/common/userDataSync.js'; -import { IViewsService } from '../../../services/views/common/viewsService.js'; +// import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; +// import { CONTEXT_SYNC_ENABLEMENT } from '../../../services/userDataSync/common/userDataSync.js'; +// import { IViewsService } from '../../../services/views/common/viewsService.js'; import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from '../../../services/workspaces/common/workspaceTrust.js'; -import { ILanguageModelToolsService } from '../../chat/common/languageModelToolsService.js'; -import { CONTEXT_KEYBINDINGS_EDITOR } from '../../preferences/common/preferences.js'; +// import { ILanguageModelToolsService } from '../../chat/common/languageModelToolsService.js'; +// import { CONTEXT_KEYBINDINGS_EDITOR } from '../../preferences/common/preferences.js'; import { IWebview } from '../../webview/browser/webview.js'; -import { Query } from '../common/extensionQuery.js'; -import { AutoRestartConfigurationKey, AutoUpdateConfigurationKey, CONTEXT_EXTENSIONS_GALLERY_STATUS, CONTEXT_HAS_GALLERY, DefaultViewsContext, ExtensionEditorTab, ExtensionRuntimeActionType, EXTENSIONS_CATEGORY, extensionsFilterSubMenu, extensionsSearchActionsMenu, HasOutdatedExtensionsContext, IExtensionArg, IExtensionsViewPaneContainer, IExtensionsWorkbenchService, INSTALL_ACTIONS_GROUP, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, IWorkspaceRecommendedExtensionsView, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, OUTDATED_EXTENSIONS_VIEW_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, TOGGLE_IGNORE_EXTENSION_ACTION_ID, UPDATE_ACTIONS_GROUP, VIEWLET_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID } from '../common/extensions.js'; +// import { Query } from '../common/extensionQuery.js'; +import { ExtensionEditorTab, IExtensionsViewPaneContainer, IExtensionsWorkbenchService, VIEWLET_ID } from '../common/extensions.js'; +// import { AutoRestartConfigurationKey, AutoUpdateConfigurationKey, CONTEXT_EXTENSIONS_GALLERY_STATUS, CONTEXT_HAS_GALLERY, DefaultViewsContext, ExtensionRuntimeActionType, EXTENSIONS_CATEGORY, extensionsFilterSubMenu, extensionsSearchActionsMenu, HasOutdatedExtensionsContext, IExtensionArg, INSTALL_ACTIONS_GROUP, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, IWorkspaceRecommendedExtensionsView, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, OUTDATED_EXTENSIONS_VIEW_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, TOGGLE_IGNORE_EXTENSION_ACTION_ID, UPDATE_ACTIONS_GROUP, WORKSPACE_RECOMMENDATIONS_VIEW_ID } from '../common/extensions.js'; import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from '../common/extensionsFileTemplate.js'; import { ExtensionsInput } from '../common/extensionsInput.js'; import { KeymapExtensions } from '../common/extensionsUtils.js'; -import { SearchExtensionsTool, SearchExtensionsToolData } from '../common/searchExtensionsTool.js'; +// import { SearchExtensionsTool, SearchExtensionsToolData } from '../common/searchExtensionsTool.js'; import { ShowRuntimeExtensionsAction } from './abstractRuntimeExtensionsEditor.js'; import { ExtensionEditor } from './extensionEditor.js'; import { ExtensionEnablementWorkspaceTrustTransitionParticipant } from './extensionEnablementWorkspaceTrustTransitionParticipant.js'; import { ExtensionRecommendationNotificationService } from './extensionRecommendationNotificationService.js'; import { ExtensionRecommendationsService } from './extensionRecommendationsService.js'; -import { ClearLanguageAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction, InstallAction, InstallAnotherVersionAction, InstallSpecificVersionOfExtensionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction } from './extensionsActions.js'; +// import { ClearLanguageAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction, InstallAction, InstallAnotherVersionAction, InstallSpecificVersionOfExtensionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction } from './extensionsActions.js'; import { ExtensionActivationProgress } from './extensionsActivationProgress.js'; import { ExtensionsCompletionItemsProvider } from './extensionsCompletionItemsProvider.js'; -import { ExtensionDependencyChecker } from './extensionsDependencyChecker.js'; -import { clearSearchResultsIcon, configureRecommendedIcon, extensionsViewIcon, filterIcon, installWorkspaceRecommendedIcon, refreshIcon } from './extensionsIcons.js'; -import { InstallExtensionQuickAccessProvider, ManageExtensionsQuickAccessProvider } from './extensionsQuickAccess.js'; -import { BuiltInExtensionsContext, ExtensionMarketplaceStatusUpdater, ExtensionsSearchValueContext, ExtensionsSortByContext, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, MaliciousExtensionChecker, RecommendedExtensionsContext, SearchHasTextContext, SearchMarketplaceExtensionsContext, StatusUpdater } from './extensionsViewlet.js'; +// import { ExtensionDependencyChecker } from './extensionsDependencyChecker.js'; +import { extensionsViewIcon } from './extensionsIcons.js'; +// import { clearSearchResultsIcon, configureRecommendedIcon, filterIcon, installWorkspaceRecommendedIcon, refreshIcon } from './extensionsIcons.js'; +import { ManageExtensionsQuickAccessProvider } from './extensionsQuickAccess.js'; +// import { InstallExtensionQuickAccessProvider } from './extensionsQuickAccess.js'; +import { ExtensionsViewPaneContainer, MaliciousExtensionChecker, StatusUpdater } from './extensionsViewlet.js'; +// import { BuiltInExtensionsContext, ExtensionMarketplaceStatusUpdater, ExtensionsSearchValueContext, ExtensionsSortByContext, ExtensionsViewletViewsContribution, RecommendedExtensionsContext, SearchHasTextContext, SearchMarketplaceExtensionsContext } from './extensionsViewlet.js'; import { ExtensionsWorkbenchService } from './extensionsWorkbenchService.js'; import './media/extensionManagement.css'; import { UnsupportedExtensionsMigrationContrib } from './unsupportedExtensionsMigrationContribution.js'; @@ -111,7 +134,7 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane new SyncDescriptor(ExtensionsInput) ]); -export const VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( +export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( { id: VIEWLET_ID, title: localize2('extensions', "Extensions"), @@ -128,6 +151,7 @@ export const VIEW_CONTAINER = Registry.as(ViewContainer alwaysUseContainerInfo: true, }, ViewContainerLocation.Sidebar); + Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ id: 'extensions', @@ -136,15 +160,17 @@ Registry.as(ConfigurationExtensions.Configuration) type: 'object', properties: { 'extensions.autoUpdate': { - enum: [true, 'onlyEnabledExtensions', false,], + enum: [true, 'onlyEnabledExtensions', 'onlySelectedExtensions', false,], enumItemLabels: [ localize('all', "All Extensions"), localize('enabled', "Only Enabled Extensions"), + localize('selected', "Only Selected Extensions"), localize('none', "None"), ], enumDescriptions: [ - localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions.'), - localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions.'), + localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions except for those updates are ignored.'), + localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions except for those updates are ignored. Disabled extensions are not updated automatically.'), + localize('extensions.autoUpdate.selected', 'Download and install updates automatically only for selected extensions.'), localize('extensions.autoUpdate.false', 'Extensions are not automatically updated.'), ], description: localize('extensions.autoUpdate', "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service."), @@ -259,56 +285,7 @@ Registry.as(ConfigurationExtensions.Configuration) type: 'boolean', description: localize('extensionsDeferredStartupFinishedActivation', "When enabled, extensions which declare the `onStartupFinished` activation event will be activated after a timeout."), default: false - }, - 'extensions.experimental.issueQuickAccess': { - type: 'boolean', - description: localize('extensionsInQuickAccess', "When enabled, extensions can be searched for via Quick Access and report issues from there."), - default: true - }, - [VerifyExtensionSignatureConfigKey]: { - type: 'boolean', - description: localize('extensions.verifySignature', "When enabled, extensions are verified to be signed before getting installed."), - default: true, - scope: ConfigurationScope.APPLICATION, - included: isNative - }, - [AutoRestartConfigurationKey]: { - type: 'boolean', - description: localize('autoRestart', "If activated, extensions will automatically restart following an update if the window is not in focus. There can be a data loss if you have open Notebooks or Custom Editors."), - default: false, - included: product.quality !== 'stable' - }, - [ExtensionGalleryServiceUrlConfigKey]: { - type: 'string', - description: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"), - default: '', - scope: ConfigurationScope.APPLICATION, - tags: ['usesOnlineServices'], - included: false, - policy: { - name: 'ExtensionGalleryServiceUrl', - category: PolicyCategory.Extensions, - minimumVersion: '1.99', - localization: { - description: { - key: 'extensions.gallery.serviceUrl', - value: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"), - } - } - }, - }, - 'extensions.supportNodeGlobalNavigator': { - type: 'boolean', - description: localize('extensionsSupportNodeGlobalNavigator', "When enabled, Node.js navigator object is exposed on the global scope."), - default: false, - }, - [ExtensionRequestsTimeoutConfigKey]: { - type: 'number', - description: localize('extensionsRequestTimeout', "Controls the timeout in milliseconds for HTTP requests made when fetching extensions from the Marketplace"), - default: 60_000, - scope: ConfigurationScope.APPLICATION, - tags: ['advanced', 'usesOnlineServices'] - }, + } } }); @@ -316,26 +293,26 @@ const jsonRegistry = Registr jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigurationSchema); // Register Commands -CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string) => { +CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean) => { const extensionService = accessor.get(IExtensionsWorkbenchService); const extension = extensionService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); if (extension) { - extensionService.open(extension, { tab, preserveFocus, feature }); + extensionService.open(extension, { tab, preserveFocus }); } else { throw new Error(localize('notFound', "Extension '{0}' not found.", extensionId)); } }); -CommandsRegistry.registerCommand('extension.open', async (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string, sideByside?: boolean) => { +CommandsRegistry.registerCommand('extension.open', async (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean) => { const extensionService = accessor.get(IExtensionsWorkbenchService); const commandService = accessor.get(ICommandService); const [extension] = await extensionService.getExtensions([{ id: extensionId }], CancellationToken.None); if (extension) { - return extensionService.open(extension, { tab, preserveFocus, feature, sideByside }); + return extensionService.open(extension, { tab, preserveFocus }); } - return commandService.executeCommand('_extensions.manage', extensionId, tab, preserveFocus, feature); + return commandService.executeCommand('_extensions.manage', extensionId, tab, preserveFocus); }); CommandsRegistry.registerCommand({ @@ -371,15 +348,6 @@ CommandsRegistry.registerCommand({ 'description': localize('workbench.extensions.installExtension.option.donotSync', "When enabled, VS Code do not sync this extension when Settings Sync is on."), default: false }, - 'justification': { - 'type': ['string', 'object'], - 'description': localize('workbench.extensions.installExtension.option.justification', "Justification for installing the extension. This is a string or an object that can be used to pass any information to the installation handlers. i.e. `{reason: 'This extension wants to open a URI', action: 'Open URI'}` will show a message box with the reason and action upon install."), - }, - 'enable': { - 'type': 'boolean', - 'description': localize('workbench.extensions.installExtension.option.enable', "When enabled, the extension will be enabled if it is installed but disabled. If the extension is already enabled, this has no effect."), - default: false - }, 'context': { 'type': 'object', 'description': localize('workbench.extensions.installExtension.option.context', "Context for the installation. This is a JSON object that can be used to pass any information to the installation handlers. i.e. `{skipWalkthrough: true}` will skip opening the walkthrough upon install."), @@ -389,48 +357,33 @@ CommandsRegistry.registerCommand({ } ] }, - handler: async ( - accessor, - arg: string | UriComponents, - options?: { - installOnlyNewlyAddedFromExtensionPackVSIX?: boolean; - installPreReleaseVersion?: boolean; - donotSync?: boolean; - justification?: string | { reason: string; action: string }; - enable?: boolean; - context?: IStringDictionary; - }) => { + handler: async (accessor, arg: string | UriComponents, options?: { installOnlyNewlyAddedFromExtensionPackVSIX?: boolean; installPreReleaseVersion?: boolean; donotSync?: boolean; context?: IStringDictionary }) => { const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService); - const extensionGalleryService = accessor.get(IExtensionGalleryService); try { if (typeof arg === 'string') { const [id, version] = getIdAndVersion(arg); - const extension = extensionsWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id, uuid: version })); - if (extension?.enablementState === EnablementState.DisabledByExtensionKind) { - const [gallery] = await extensionGalleryService.getExtensions([{ id, preRelease: options?.installPreReleaseVersion }], CancellationToken.None); - if (!gallery) { - throw new Error(localize('notFound', "Extension '{0}' not found.", arg)); - } - await extensionManagementService.installFromGallery(gallery, { + const [extension] = await extensionsWorkbenchService.getExtensions([{ id, preRelease: options?.installPreReleaseVersion }], CancellationToken.None); + if (extension) { + const installOptions: InstallOptions = { isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */ installPreReleaseVersion: options?.installPreReleaseVersion, installGivenVersion: !!version, - context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND }, - }); + context: options?.context + }; + if (extension.gallery && extension.enablementState === EnablementState.DisabledByExtensionKind) { + await extensionManagementService.installFromGallery(extension.gallery, installOptions); + return; + } + // Note: installVersion method doesn't exist, using install instead + await extensionsWorkbenchService.install(extension, installOptions); } else { - await extensionsWorkbenchService.install(arg, { - version, - installPreReleaseVersion: options?.installPreReleaseVersion, - context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND }, - justification: options?.justification, - enable: options?.enable, - isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */ - }, ProgressLocation.Notification); + throw new Error(localize('notFound', "Extension '{0}' not found.", arg)); } } else { const vsix = URI.revive(arg); - await extensionsWorkbenchService.install(vsix, { installGivenVersion: true }); + // Note: installOnlyNewlyAddedFromExtensionPack is not available in InstallExtensionOptions + await extensionsWorkbenchService.install(vsix); } } catch (e) { onUnexpectedError(e); @@ -463,7 +416,7 @@ CommandsRegistry.registerCommand({ throw new Error(localize('notInstalled', "Extension '{0}' is not installed. Make sure you use the full extension ID, including the publisher, e.g.: ms-dotnettools.csharp.", id)); } if (extensionToUninstall.isBuiltin) { - throw new Error(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be uninstalled", id)); + throw new Error(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be installed", id)); } try { @@ -487,7 +440,15 @@ CommandsRegistry.registerCommand({ ] }, handler: async (accessor, query: string = '') => { - return accessor.get(IExtensionsWorkbenchService).openSearch(query); + const paneCompositeService = accessor.get(IPaneCompositePartService); + const viewlet = await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); + + if (!viewlet) { + return; + } + + (viewlet.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query); + viewlet.focus(); } }); @@ -513,1461 +474,1254 @@ overrideActionForActiveExtensionEditorWebview(PasteAction, webview => webview.pa export const CONTEXT_HAS_LOCAL_SERVER = new RawContextKey('hasLocalServer', false); export const CONTEXT_HAS_REMOTE_SERVER = new RawContextKey('hasRemoteServer', false); export const CONTEXT_HAS_WEB_SERVER = new RawContextKey('hasWebServer', false); -const CONTEXT_GALLERY_SORT_CAPABILITIES = new RawContextKey('gallerySortCapabilities', ''); -const CONTEXT_GALLERY_FILTER_CAPABILITIES = new RawContextKey('galleryFilterCapabilities', ''); -const CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED = new RawContextKey('galleryAllPublicRepositorySigned', false); -const CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED = new RawContextKey('galleryAllPrivateRepositorySigned', false); -const CONTEXT_GALLERY_HAS_EXTENSION_LINK = new RawContextKey('galleryHasExtensionLink', false); - -async function runAction(action: IAction): Promise { - try { - await action.run(); - } finally { - if (isDisposable(action)) { - action.dispose(); - } - } -} - -type IExtensionActionOptions = IAction2Options & { - menuTitles?: { [id: string]: string }; - run(accessor: ServicesAccessor, ...args: unknown[]): Promise; -}; - -class ExtensionsContributions extends Disposable implements IWorkbenchContribution { - constructor( - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IViewsService private readonly viewsService: IViewsService, - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IDialogService private readonly dialogService: IDialogService, - @ICommandService private readonly commandService: ICommandService, - @IProductService private readonly productService: IProductService, - ) { - super(); - const hasLocalServerContext = CONTEXT_HAS_LOCAL_SERVER.bindTo(contextKeyService); - if (this.extensionManagementServerService.localExtensionManagementServer) { - hasLocalServerContext.set(true); - } - - const hasRemoteServerContext = CONTEXT_HAS_REMOTE_SERVER.bindTo(contextKeyService); - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - hasRemoteServerContext.set(true); - } - - const hasWebServerContext = CONTEXT_HAS_WEB_SERVER.bindTo(contextKeyService); - if (this.extensionManagementServerService.webExtensionManagementServer) { - hasWebServerContext.set(true); - } - - this.updateExtensionGalleryStatusContexts(); - this._register(extensionGalleryManifestService.onDidChangeExtensionGalleryManifestStatus(() => this.updateExtensionGalleryStatusContexts())); - extensionGalleryManifestService.getExtensionGalleryManifest() - .then(extensionGalleryManifest => { - this.updateGalleryCapabilitiesContexts(extensionGalleryManifest); - this._register(extensionGalleryManifestService.onDidChangeExtensionGalleryManifest(extensionGalleryManifest => this.updateGalleryCapabilitiesContexts(extensionGalleryManifest))); - }); - this.registerGlobalActions(); - this.registerContextMenuActions(); - this.registerQuickAccessProvider(); - } - - private async updateExtensionGalleryStatusContexts(): Promise { - CONTEXT_HAS_GALLERY.bindTo(this.contextKeyService).set(this.extensionGalleryManifestService.extensionGalleryManifestStatus === ExtensionGalleryManifestStatus.Available); - CONTEXT_EXTENSIONS_GALLERY_STATUS.bindTo(this.contextKeyService).set(this.extensionGalleryManifestService.extensionGalleryManifestStatus); - } - - private async updateGalleryCapabilitiesContexts(extensionGalleryManifest: IExtensionGalleryManifest | null): Promise { - CONTEXT_GALLERY_SORT_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.sorting?.map(s => s.name)?.join('_')}_UpdateDate_`); - CONTEXT_GALLERY_FILTER_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.filtering?.map(s => s.name)?.join('_')}_`); - CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED.bindTo(this.contextKeyService).set(!!extensionGalleryManifest?.capabilities?.signing?.allPublicRepositorySigned); - CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED.bindTo(this.contextKeyService).set(!!extensionGalleryManifest?.capabilities?.signing?.allPrivateRepositorySigned); - CONTEXT_GALLERY_HAS_EXTENSION_LINK.bindTo(this.contextKeyService).set(!!(extensionGalleryManifest && getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionDetailsViewUri))); - } - - private registerQuickAccessProvider(): void { - if (this.extensionManagementServerService.localExtensionManagementServer - || this.extensionManagementServerService.remoteExtensionManagementServer - || this.extensionManagementServerService.webExtensionManagementServer - ) { - Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({ - ctor: InstallExtensionQuickAccessProvider, - prefix: InstallExtensionQuickAccessProvider.PREFIX, - placeholder: localize('installExtensionQuickAccessPlaceholder', "Type the name of an extension to install or search."), - helpEntries: [{ description: localize('installExtensionQuickAccessHelp', "Install or Search Extensions") }] - }); - } - } - - // Global actions - private registerGlobalActions(): void { - this._register(MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - command: { - id: VIEWLET_ID, - title: localize({ key: 'miPreferencesExtensions', comment: ['&& denotes a mnemonic'] }, "&&Extensions") - }, - group: '2_configuration', - order: 3 - })); - this._register(MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - command: { - id: VIEWLET_ID, - title: localize('showExtensions', "Extensions") - }, - group: '2_configuration', - order: 3 - })); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.focusExtensionsView', - title: localize2('focusExtensions', 'Focus on Extensions View'), - category: ExtensionsLocalizedLabel, - f1: true, - run: async (accessor: ServicesAccessor) => { - await accessor.get(IExtensionsWorkbenchService).openSearch(''); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installExtensions', - title: localize2('installExtensions', 'Install Extensions'), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) - }, - run: async (accessor: ServicesAccessor) => { - accessor.get(IViewsService).openViewContainer(VIEWLET_ID, true); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showRecommendedKeymapExtensions', - title: localize2('showRecommendedKeymapExtensionsShort', 'Keymaps'), - category: PreferencesLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: CONTEXT_HAS_GALLERY - }, { - id: MenuId.EditorTitle, - when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_HAS_GALLERY), - group: '2_keyboard_discover_actions' - }], - menuTitles: { - [MenuId.EditorTitle.id]: localize('importKeyboardShortcutsFroms', "Migrate Keyboard Shortcuts from...") - }, - run: () => this.extensionsWorkbenchService.openSearch('@recommended:keymaps ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showLanguageExtensions', - title: localize2('showLanguageExtensionsShort', 'Language Extensions'), - category: PreferencesLocalizedLabel, - menu: { - id: MenuId.CommandPalette, - when: CONTEXT_HAS_GALLERY - }, - run: () => this.extensionsWorkbenchService.openSearch('@recommended:languages ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.checkForUpdates', - title: localize2('checkForUpdates', 'Check for Extension Updates'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) - }, { - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY), - group: '1_updates', - order: 1 - }], - run: async () => { - await this.extensionsWorkbenchService.checkForUpdates(); - const outdated = this.extensionsWorkbenchService.outdated; - if (outdated.length) { - return this.extensionsWorkbenchService.openSearch('@outdated '); - } else { - return this.dialogService.info(localize('noUpdatesAvailable', "All extensions are up to date.")); - } - } - }); - - const enableAutoUpdateWhenCondition = ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false); - this.registerExtensionAction({ - id: 'workbench.extensions.action.enableAutoUpdate', - title: localize2('enableAutoUpdate', 'Enable Auto Update for All Extensions'), - category: ExtensionsLocalizedLabel, - precondition: enableAutoUpdateWhenCondition, - menu: [{ - id: MenuId.ViewContainerTitle, - order: 5, - group: '1_updates', - when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), enableAutoUpdateWhenCondition) - }, { - id: MenuId.CommandPalette, - }], - run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateForAllExtensions(true) - }); - - const disableAutoUpdateWhenCondition = ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, false); - this.registerExtensionAction({ - id: 'workbench.extensions.action.disableAutoUpdate', - title: localize2('disableAutoUpdate', 'Disable Auto Update for All Extensions'), - precondition: disableAutoUpdateWhenCondition, - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.ViewContainerTitle, - order: 5, - group: '1_updates', - when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), disableAutoUpdateWhenCondition) - }, { - id: MenuId.CommandPalette, - }], - run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateForAllExtensions(false) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.updateAllExtensions', - title: localize2('updateAll', 'Update All Extensions'), - category: ExtensionsLocalizedLabel, - precondition: HasOutdatedExtensionsContext, - menu: [ - { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) - }, { - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`).negate(), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'))), - group: '1_updates', - order: 2 - }, { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', OUTDATED_EXTENSIONS_VIEW_ID), - group: 'navigation', - order: 1 - } - ], - icon: installWorkspaceRecommendedIcon, - run: async () => { - await this.extensionsWorkbenchService.updateAll(); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.enableAll', - title: localize2('enableAll', 'Enable All Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) - }, { - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), - group: '2_enablement', - order: 1 - }], - run: async () => { - const extensionsToEnable = this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); - if (extensionsToEnable.length) { - await this.extensionsWorkbenchService.setEnablement(extensionsToEnable, EnablementState.EnabledGlobally); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.enableAllWorkspace', - title: localize2('enableAllWorkspace', 'Enable All Extensions for this Workspace'), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) - }, - run: async () => { - const extensionsToEnable = this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); - if (extensionsToEnable.length) { - await this.extensionsWorkbenchService.setEnablement(extensionsToEnable, EnablementState.EnabledWorkspace); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.disableAll', - title: localize2('disableAll', 'Disable All Installed Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) - }, { - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), - group: '2_enablement', - order: 2 - }], - run: async () => { - const extensionsToDisable = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); - if (extensionsToDisable.length) { - await this.extensionsWorkbenchService.setEnablement(extensionsToDisable, EnablementState.DisabledGlobally); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.disableAllWorkspace', - title: localize2('disableAllWorkspace', 'Disable All Installed Extensions for this Workspace'), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) - }, - run: async () => { - const extensionsToDisable = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); - if (extensionsToDisable.length) { - await this.extensionsWorkbenchService.setEnablement(extensionsToDisable, EnablementState.DisabledWorkspace); - } - } - }); - - this.registerExtensionAction({ - id: SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, - title: localize2('InstallFromVSIX', 'Install from VSIX...'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER) - }, { - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)), - group: '3_install', - order: 1 - }], - run: async (accessor: ServicesAccessor) => { - const fileDialogService = accessor.get(IFileDialogService); - const commandService = accessor.get(ICommandService); - const vsixPaths = await fileDialogService.showOpenDialog({ - title: localize('installFromVSIX', "Install from VSIX"), - filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }], - canSelectFiles: true, - canSelectMany: true, - openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install")) - }); - if (vsixPaths) { - await commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths); - } - } - }); - - this.registerExtensionAction({ - id: INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, - title: localize('installVSIX', "Install Extension VSIX"), - menu: [{ - id: MenuId.ExplorerContext, - group: 'extensions', - when: ContextKeyExpr.and(ResourceContextKey.Extension.isEqualTo('.vsix'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)), - }], - run: async (accessor: ServicesAccessor, resources: URI[] | URI) => { - const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const hostService = accessor.get(IHostService); - const notificationService = accessor.get(INotificationService); - - const vsixs = Array.isArray(resources) ? resources : [resources]; - const result = await Promise.allSettled(vsixs.map(async (vsix) => await extensionsWorkbenchService.install(vsix, { installGivenVersion: true }))); - let error: Error | undefined, requireReload = false, requireRestart = false; - for (const r of result) { - if (r.status === 'rejected') { - error = new Error(r.reason); - break; - } - requireReload = requireReload || r.value.runtimeState?.action === ExtensionRuntimeActionType.ReloadWindow; - requireRestart = requireRestart || r.value.runtimeState?.action === ExtensionRuntimeActionType.RestartExtensions; - } - if (error) { - throw error; - } - if (requireReload) { - notificationService.prompt( - Severity.Info, - vsixs.length > 1 ? localize('InstallVSIXs.successReload', "Completed installing extensions. Please reload Visual Studio Code to enable them.") - : localize('InstallVSIXAction.successReload', "Completed installing extension. Please reload Visual Studio Code to enable it."), - [{ - label: localize('InstallVSIXAction.reloadNow', "Reload Now"), - run: () => hostService.reload() - }] - ); - } - else if (requireRestart) { - notificationService.prompt( - Severity.Info, - vsixs.length > 1 ? localize('InstallVSIXs.successRestart', "Completed installing extensions. Please restart extensions to enable them.") - : localize('InstallVSIXAction.successRestart', "Completed installing extension. Please restart extensions to enable it."), - [{ - label: localize('InstallVSIXAction.restartExtensions', "Restart Extensions"), - run: () => extensionsWorkbenchService.updateRunningExtensions() - }] - ); - } - else { - notificationService.prompt( - Severity.Info, - vsixs.length > 1 ? localize('InstallVSIXs.successNoReload', "Completed installing extensions.") : localize('InstallVSIXAction.successNoReload', "Completed installing extension."), - [] - ); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installExtensionFromLocation', - title: localize2('installExtensionFromLocation', 'Install Extension from Location...'), - category: Categories.Developer, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_WEB_SERVER, CONTEXT_HAS_LOCAL_SERVER) - }], - run: async (accessor: ServicesAccessor) => { - const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService); - if (isWeb) { - return new Promise((c, e) => { - const quickInputService = accessor.get(IQuickInputService); - const disposables = new DisposableStore(); - const quickPick = disposables.add(quickInputService.createQuickPick()); - quickPick.title = localize('installFromLocation', "Install Extension from Location"); - quickPick.customButton = true; - quickPick.customLabel = localize('install button', "Install"); - quickPick.placeholder = localize('installFromLocationPlaceHolder', "Location of the web extension"); - quickPick.ignoreFocusOut = true; - disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(async () => { - quickPick.hide(); - if (quickPick.value) { - try { - await extensionManagementService.installFromLocation(URI.parse(quickPick.value)); - } catch (error) { - e(error); - return; - } - } - c(); - })); - disposables.add(quickPick.onDidHide(() => disposables.dispose())); - quickPick.show(); - }); - } else { - const fileDialogService = accessor.get(IFileDialogService); - const extensionLocation = await fileDialogService.showOpenDialog({ - canSelectFolders: true, - canSelectFiles: false, - canSelectMany: false, - title: localize('installFromLocation', "Install Extension from Location"), - }); - if (extensionLocation?.[0]) { - await extensionManagementService.installFromLocation(extensionLocation[0]); - } - } - } - }); - - MenuRegistry.appendMenuItem(extensionsSearchActionsMenu, { - submenu: extensionsFilterSubMenu, - title: localize('filterExtensions', "Filter Extensions..."), - group: 'navigation', - order: 2, - icon: filterIcon, - }); - - const showFeaturedExtensionsId = 'extensions.filter.featured'; - const featuresExtensionsWhenContext = ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Featured}_`))); - this.registerExtensionAction({ - id: showFeaturedExtensionsId, - title: localize2('showFeaturedExtensions', 'Show Featured Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: featuresExtensionsWhenContext - }, { - id: extensionsFilterSubMenu, - when: featuresExtensionsWhenContext, - group: '1_predefined', - order: 1, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('featured filter', "Featured") - }, - run: () => this.extensionsWorkbenchService.openSearch('@featured ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showPopularExtensions', - title: localize2('showPopularExtensions', 'Show Popular Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: CONTEXT_HAS_GALLERY - }, { - id: extensionsFilterSubMenu, - when: CONTEXT_HAS_GALLERY, - group: '1_predefined', - order: 2, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('most popular filter', "Most Popular") - }, - run: () => this.extensionsWorkbenchService.openSearch('@popular ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showRecommendedExtensions', - title: localize2('showRecommendedExtensions', 'Show Recommended Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: CONTEXT_HAS_GALLERY - }, { - id: extensionsFilterSubMenu, - when: CONTEXT_HAS_GALLERY, - group: '1_predefined', - order: 2, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('most popular recommended', "Recommended") - }, - run: () => this.extensionsWorkbenchService.openSearch('@recommended ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.recentlyPublishedExtensions', - title: localize2('recentlyPublishedExtensions', 'Show Recently Published Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: CONTEXT_HAS_GALLERY - }, { - id: extensionsFilterSubMenu, - when: CONTEXT_HAS_GALLERY, - group: '1_predefined', - order: 2, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('recently published filter', "Recently Published") - }, - run: () => this.extensionsWorkbenchService.openSearch('@recentlyPublished ') - }); - - const extensionsCategoryFilterSubMenu = new MenuId('extensionsCategoryFilterSubMenu'); - MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { - submenu: extensionsCategoryFilterSubMenu, - title: localize('filter by category', "Category"), - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Category}_`))), - group: '2_categories', - order: 1, - }); - - EXTENSION_CATEGORIES.forEach((category, index) => { - this.registerExtensionAction({ - id: `extensions.actions.searchByCategory.${category}`, - title: category, - menu: [{ - id: extensionsCategoryFilterSubMenu, - when: CONTEXT_HAS_GALLERY, - order: index, - }], - run: () => this.extensionsWorkbenchService.openSearch(`@category:"${category.toLowerCase()}"`) - }); - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installedExtensions', - title: localize2('installedExtensions', 'Show Installed Extensions'), - category: ExtensionsLocalizedLabel, - f1: true, - menu: [{ - id: extensionsFilterSubMenu, - group: '3_installed', - order: 1, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('installed filter', "Installed") - }, - run: () => this.extensionsWorkbenchService.openSearch('@installed ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.listBuiltInExtensions', - title: localize2('showBuiltInExtensions', 'Show Built-in Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) - }, { - id: extensionsFilterSubMenu, - group: '3_installed', - order: 3, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('builtin filter', "Built-in") - }, - run: () => this.extensionsWorkbenchService.openSearch('@builtin ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.extensionUpdates', - title: localize2('extensionUpdates', 'Show Extension Updates'), - category: ExtensionsLocalizedLabel, - precondition: CONTEXT_HAS_GALLERY, - f1: true, - menu: [{ - id: extensionsFilterSubMenu, - group: '3_installed', - when: CONTEXT_HAS_GALLERY, - order: 2, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('extension updates filter', "Updates") - }, - run: () => this.extensionsWorkbenchService.openSearch('@updates') - }); - - this.registerExtensionAction({ - id: LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, - title: localize2('showWorkspaceUnsupportedExtensions', 'Show Extensions Unsupported By Workspace'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER), - }, { - id: extensionsFilterSubMenu, - group: '3_installed', - order: 6, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER), - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('workspace unsupported filter', "Workspace Unsupported") - }, - run: () => this.extensionsWorkbenchService.openSearch('@workspaceUnsupported') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showEnabledExtensions', - title: localize2('showEnabledExtensions', 'Show Enabled Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) - }, { - id: extensionsFilterSubMenu, - group: '3_installed', - order: 4, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('enabled filter', "Enabled") - }, - run: () => this.extensionsWorkbenchService.openSearch('@enabled ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showDisabledExtensions', - title: localize2('showDisabledExtensions', 'Show Disabled Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) - }, { - id: extensionsFilterSubMenu, - group: '3_installed', - order: 5, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('disabled filter', "Disabled") - }, - run: () => this.extensionsWorkbenchService.openSearch('@disabled ') - }); - - const extensionsSortSubMenu = new MenuId('extensionsSortSubMenu'); - MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { - submenu: extensionsSortSubMenu, - title: localize('sorty by', "Sort By"), - when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext)), - group: '4_sort', - order: 1, - }); - - [ - { id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.InstallCount }, - { id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.WeightedRating }, - { id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.Title }, - { id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.PublishedDate }, - { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()), sortCapability: 'UpdateDate' }, - ].map(({ id, title, precondition, sortCapability }, index) => { - const sortCapabilityContext = ContextKeyExpr.regex(CONTEXT_GALLERY_SORT_CAPABILITIES.key, new RegExp(`_${sortCapability}_`)); - this.registerExtensionAction({ - id: `extensions.sort.${id}`, - title, - precondition: ContextKeyExpr.and(precondition, ContextKeyExpr.regex(ExtensionsSearchValueContext.key, /^@feature:/).negate(), sortCapabilityContext), - menu: [{ - id: extensionsSortSubMenu, - when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), sortCapabilityContext), - order: index, - }], - toggled: ExtensionsSortByContext.isEqualTo(id), - run: async () => { - const extensionsViewPaneContainer = ((await this.viewsService.openViewContainer(VIEWLET_ID, true))?.getViewPaneContainer()) as IExtensionsViewPaneContainer | undefined; - const currentQuery = Query.parse(extensionsViewPaneContainer?.searchValue ?? ''); - extensionsViewPaneContainer?.search(new Query(currentQuery.value, id).toString()); - extensionsViewPaneContainer?.focus(); - } - }); - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.clearExtensionsSearchResults', - title: localize2('clearExtensionsSearchResults', 'Clear Extensions Search Results'), - category: ExtensionsLocalizedLabel, - icon: clearSearchResultsIcon, - f1: true, - precondition: SearchHasTextContext, - menu: { - id: extensionsSearchActionsMenu, - group: 'navigation', - order: 1, - }, - run: async (accessor: ServicesAccessor) => { - const viewPaneContainer = accessor.get(IViewsService).getActiveViewPaneContainerWithId(VIEWLET_ID); - if (viewPaneContainer) { - const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer; - extensionsViewPaneContainer.search(''); - extensionsViewPaneContainer.focus(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.refreshExtension', - title: localize2('refreshExtension', 'Refresh'), - category: ExtensionsLocalizedLabel, - icon: refreshIcon, - f1: true, - menu: { - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), - group: 'navigation', - order: 2 - }, - run: async (accessor: ServicesAccessor) => { - const viewPaneContainer = accessor.get(IViewsService).getActiveViewPaneContainerWithId(VIEWLET_ID); - if (viewPaneContainer) { - await (viewPaneContainer as IExtensionsViewPaneContainer).refresh(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installWorkspaceRecommendedExtensions', - title: localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), - icon: installWorkspaceRecommendedIcon, - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', WORKSPACE_RECOMMENDATIONS_VIEW_ID), - group: 'navigation', - order: 1 - }, - run: async (accessor: ServicesAccessor) => { - const view = accessor.get(IViewsService).getActiveViewWithId(WORKSPACE_RECOMMENDATIONS_VIEW_ID) as IWorkspaceRecommendedExtensionsView; - return view.installWorkspaceRecommendations(); - } - }); - - this.registerExtensionAction({ - id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, - title: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, - icon: configureRecommendedIcon, - menu: [{ - id: MenuId.CommandPalette, - when: WorkbenchStateContext.notEqualsTo('empty'), - }, { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', WORKSPACE_RECOMMENDATIONS_VIEW_ID), - group: 'navigation', - order: 2 - }], - run: () => runAction(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL)) - }); - - this.registerExtensionAction({ - id: InstallSpecificVersionOfExtensionAction.ID, - title: { value: InstallSpecificVersionOfExtensionAction.LABEL, original: 'Install Specific Version of Extension...' }, - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) - }, - run: () => runAction(this.instantiationService.createInstance(InstallSpecificVersionOfExtensionAction, InstallSpecificVersionOfExtensionAction.ID, InstallSpecificVersionOfExtensionAction.LABEL)) - }); - } - - // Extension Context Menu - private registerContextMenuActions(): void { - - this.registerExtensionAction({ - id: SetColorThemeAction.ID, - title: SetColorThemeAction.TITLE, - menu: { - id: MenuId.ExtensionContext, - group: THEME_ACTIONS_GROUP, - order: 0, - when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasColorThemes')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const instantiationService = accessor.get(IInstantiationService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); - if (extension) { - const action = instantiationService.createInstance(SetColorThemeAction); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: SetFileIconThemeAction.ID, - title: SetFileIconThemeAction.TITLE, - menu: { - id: MenuId.ExtensionContext, - group: THEME_ACTIONS_GROUP, - order: 0, - when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasFileIconThemes')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const instantiationService = accessor.get(IInstantiationService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); - if (extension) { - const action = instantiationService.createInstance(SetFileIconThemeAction); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: SetProductIconThemeAction.ID, - title: SetProductIconThemeAction.TITLE, - menu: { - id: MenuId.ExtensionContext, - group: THEME_ACTIONS_GROUP, - order: 0, - when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasProductIconThemes')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const instantiationService = accessor.get(IInstantiationService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); - if (extension) { - const action = instantiationService.createInstance(SetProductIconThemeAction); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showPreReleaseVersion', - title: localize2('show pre-release version', 'Show Pre-Release Version'), - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 0, - when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = (await extensionWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - extensionWorkbenchService.open(extension, { showPreReleaseVersion: true }); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showReleasedVersion', - title: localize2('show released version', 'Show Release Version'), - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 1, - when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = (await extensionWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - extensionWorkbenchService.open(extension, { showPreReleaseVersion: false }); - } - }); - - this.registerExtensionAction({ - id: ToggleAutoUpdateForExtensionAction.ID, - title: ToggleAutoUpdateForExtensionAction.LABEL, - category: ExtensionsLocalizedLabel, - precondition: ContextKeyExpr.and(ContextKeyExpr.or(ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.equals('isExtensionEnabled', true)), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isExtensionAllowed')), - menu: { - id: MenuId.ExtensionContext, - group: UPDATE_ACTIONS_GROUP, - order: 1, - when: ContextKeyExpr.and( - ContextKeyExpr.not('inExtensionEditor'), - ContextKeyExpr.equals('extensionStatus', 'installed'), - ContextKeyExpr.not('isBuiltinExtension'), - ) - }, - run: async (accessor: ServicesAccessor, id: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { - const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: ToggleAutoUpdatesForPublisherAction.ID, - title: { value: ToggleAutoUpdatesForPublisherAction.LABEL, original: 'Auto Update (Publisher)' }, - category: ExtensionsLocalizedLabel, - precondition: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false), - menu: { - id: MenuId.ExtensionContext, - group: UPDATE_ACTIONS_GROUP, - order: 2, - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, id: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { - const action = instantiationService.createInstance(ToggleAutoUpdatesForPublisherAction); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.switchToPreRlease', - title: localize('enablePreRleaseLabel', "Switch to Pre-Release Version"), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 2, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, id: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { - const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.switchToRelease', - title: localize('disablePreRleaseLabel', "Switch to Release Version"), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 2, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, id: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { - const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: ClearLanguageAction.ID, - title: ClearLanguageAction.TITLE, - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 0, - when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.has('canSetLanguage'), ContextKeyExpr.has('isActiveLanguagePackExtension')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = (await extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - const action = instantiationService.createInstance(ClearLanguageAction); - action.extension = extension; - return action.run(); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installUnsigned', - title: localize('install', "Install"), - menu: { - id: MenuId.ExtensionContext, - group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('extensionIsUnsigned'), - ContextKeyExpr.or(ContextKeyExpr.and(CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED, ContextKeyExpr.not('extensionIsPrivate')), ContextKeyExpr.and(CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED, ContextKeyExpr.has('extensionIsPrivate')))), - order: 1 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] - || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - if (extension) { - const action = instantiationService.createInstance(InstallAction, { installPreReleaseVersion: this.extensionManagementService.preferPreReleases }); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installAndDonotSync', - title: localize('install installAndDonotSync', "Install (Do not Sync)"), - menu: { - id: MenuId.ExtensionContext, - group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT), - order: 1 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] - || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - if (extension) { - const action = instantiationService.createInstance(InstallAction, { - installPreReleaseVersion: this.extensionManagementService.preferPreReleases, - isMachineScoped: true, - }); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installPrereleaseAndDonotSync', - title: localize('installPrereleaseAndDonotSync', "Install Pre-Release (Do not Sync)"), - menu: { - id: MenuId.ExtensionContext, - group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT), - order: 2 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] - || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - if (extension) { - const action = instantiationService.createInstance(InstallAction, { - isMachineScoped: true, - preRelease: true - }); - action.extension = extension; - return action.run(); - } - } - }); - - this.registerExtensionAction({ - id: InstallAnotherVersionAction.ID, - title: InstallAnotherVersionAction.LABEL, - menu: { - id: MenuId.ExtensionContext, - group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall')), - order: 3 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] - || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - if (extension) { - return instantiationService.createInstance(InstallAnotherVersionAction, extension, false).run(); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.copyExtension', - title: localize2('workbench.extensions.action.copyExtension', 'Copy'), - menu: { - id: MenuId.ExtensionContext, - group: '1_copy' - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const clipboardService = accessor.get(IClipboardService); - const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] - || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - if (extension) { - const name = localize('extensionInfoName', 'Name: {0}', extension.displayName); - const id = localize('extensionInfoId', 'Id: {0}', extensionId); - const description = localize('extensionInfoDescription', 'Description: {0}', extension.description); - const verision = localize('extensionInfoVersion', 'Version: {0}', extension.version); - const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', extension.publisherDisplayName); - const link = extension.url ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', `${extension.url}`) : null; - const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`; - await clipboardService.writeText(clipboardStr); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.copyExtensionId', - title: localize2('workbench.extensions.action.copyExtensionId', 'Copy Extension ID'), - menu: { - id: MenuId.ExtensionContext, - group: '1_copy' - }, - run: async (accessor: ServicesAccessor, id: string) => accessor.get(IClipboardService).writeText(id) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.copyLink', - title: localize2('workbench.extensions.action.copyLink', 'Copy Link'), - menu: { - id: MenuId.ExtensionContext, - group: '1_copy', - when: ContextKeyExpr.and(ContextKeyExpr.has('isGalleryExtension'), CONTEXT_GALLERY_HAS_EXTENSION_LINK), - }, - run: async (accessor: ServicesAccessor, _, extension: IExtensionArg) => { - const clipboardService = accessor.get(IClipboardService); - if (extension.galleryLink) { - await clipboardService.writeText(extension.galleryLink); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.configure', - title: localize2('workbench.extensions.action.configure', 'Settings'), - menu: { - id: MenuId.ExtensionContext, - group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')), - order: 1 - }, - run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openSettings({ jsonEditor: false, query: `@ext:${id}` }) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.download', - title: localize('download VSIX', "Download VSIX"), - menu: { - id: MenuId.ExtensionContext, - when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')), - order: this.productService.quality === 'stable' ? 0 : 1 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'release'); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.downloadPreRelease', - title: localize('download pre-release', "Download Pre-Release VSIX"), - menu: { - id: MenuId.ExtensionContext, - when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion')), - order: this.productService.quality === 'stable' ? 1 : 0 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'prerelease'); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.downloadSpecificVersion', - title: localize('download specific version', "Download Specific Version VSIX..."), - menu: { - id: MenuId.ExtensionContext, - when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')), - order: 2 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'any'); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.manageAccountPreferences', - title: localize2('workbench.extensions.action.changeAccountPreference', "Account Preferences"), - menu: { - id: MenuId.ExtensionContext, - group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasAccountPreferences')), - order: 2, - }, - run: (accessor: ServicesAccessor, id: string) => accessor.get(ICommandService).executeCommand('_manageAccountPreferencesForExtension', id) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.configureKeybindings', - title: localize2('workbench.extensions.action.configureKeybindings', 'Keyboard Shortcuts'), - menu: { - id: MenuId.ExtensionContext, - group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasKeybindings')), - order: 2 - }, - run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query: `@ext:${id}` }) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.toggleApplyToAllProfiles', - title: localize2('workbench.extensions.action.toggleApplyToAllProfiles', "Apply Extension to all Profiles"), - toggled: ContextKeyExpr.has('isApplicationScopedExtension'), - menu: { - id: MenuId.ExtensionContext, - group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('isDefaultApplicationScopedExtension').negate(), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.equals('isWorkspaceScopedExtension', false)), - order: 3 - }, - run: async (accessor: ServicesAccessor, _: string, extensionArg: IExtensionArg) => { - const uriIdentityService = accessor.get(IUriIdentityService); - const extension = extensionArg.location ? this.extensionsWorkbenchService.installed.find(e => uriIdentityService.extUri.isEqual(e.local?.location, extensionArg.location)) : undefined; - if (extension) { - return this.extensionsWorkbenchService.toggleApplyExtensionToAllProfiles(extension); - } - } - }); - - this.registerExtensionAction({ - id: TOGGLE_IGNORE_EXTENSION_ACTION_ID, - title: localize2('workbench.extensions.action.toggleIgnoreExtension', "Sync This Extension"), - menu: { - id: MenuId.ExtensionContext, - group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), CONTEXT_SYNC_ENABLEMENT, ContextKeyExpr.equals('isWorkspaceScopedExtension', false)), - order: 4 - }, - run: async (accessor: ServicesAccessor, id: string) => { - const extension = this.extensionsWorkbenchService.local.find(e => areSameExtensions({ id }, e.identifier)); - if (extension) { - return this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(extension); - } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.ignoreRecommendation', - title: localize2('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', - when: ContextKeyExpr.has('isExtensionRecommended'), - order: 1 - }, - run: async (accessor: ServicesAccessor, id: string) => accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, true) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.undoIgnoredRecommendation', - title: localize2('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', - when: ContextKeyExpr.has('isUserIgnoredRecommendation'), - order: 1 - }, - run: async (accessor: ServicesAccessor, id: string) => accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, false) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations', - title: localize2('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', - when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate(), ContextKeyExpr.notEquals('extensionSource', 'resource')), - order: 2 - }, - run: (accessor: ServicesAccessor, id: string) => accessor.get(IWorkspaceExtensionsConfigService).toggleRecommendation(id) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', - title: localize2('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', - when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended')), - order: 2 - }, - run: (accessor: ServicesAccessor, id: string) => accessor.get(IWorkspaceExtensionsConfigService).toggleRecommendation(id) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceRecommendations', - title: localize2('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), - category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), - }, - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const workspaceExtensionsConfigService = accessor.get(IWorkspaceExtensionsConfigService); - if (!(editorService.activeEditor instanceof ExtensionsInput)) { - return; - } - const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); - const recommendations = await workspaceExtensionsConfigService.getRecommendations(); - if (recommendations.includes(extensionId)) { - return; - } - await workspaceExtensionsConfigService.toggleRecommendation(extensionId); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations', - title: localize2('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), - category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), - }, - run: () => this.commandService.executeCommand('workbench.extensions.action.addToWorkspaceRecommendations') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations', - title: localize2('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), - category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), - }, - async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - const workspaceExtensionsConfigService = accessor.get(IWorkspaceExtensionsConfigService); - if (!(editorService.activeEditor instanceof ExtensionsInput)) { - return; - } - const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); - const unwantedRecommendations = await workspaceExtensionsConfigService.getUnwantedRecommendations(); - if (unwantedRecommendations.includes(extensionId)) { - return; - } - await workspaceExtensionsConfigService.toggleUnwantedRecommendation(extensionId); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', - title: localize2('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), - category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), - }, - run: () => this.commandService.executeCommand('workbench.extensions.action.addToWorkspaceIgnoredRecommendations') - }); - - this.registerExtensionAction({ - id: ConfigureWorkspaceRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, - category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: WorkbenchStateContext.isEqualTo('workspace'), - }, - run: () => runAction(this.instantiationService.createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL)) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.manageTrustedPublishers', - title: localize2('workbench.extensions.action.manageTrustedPublishers', "Manage Trusted Extension Publishers"), - category: EXTENSIONS_CATEGORY, - f1: true, - run: async (accessor: ServicesAccessor) => { - const quickInputService = accessor.get(IQuickInputService); - const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService); - const trustedPublishers = extensionManagementService.getTrustedPublishers(); - const trustedPublisherItems = trustedPublishers.map(publisher => ({ - id: publisher.publisher, - label: publisher.publisherDisplayName, - description: publisher.publisher, - picked: true, - })).sort((a, b) => a.label.localeCompare(b.label)); - const result = await quickInputService.pick(trustedPublisherItems, { - canPickMany: true, - title: localize('trustedPublishers', "Manage Trusted Extension Publishers"), - placeHolder: localize('trustedPublishersPlaceholder', "Choose which publishers to trust"), - }); - if (result) { - const untrustedPublishers = []; - for (const { publisher } of trustedPublishers) { - if (!result.some(r => r.id === publisher)) { - untrustedPublishers.push(publisher); - } - } - trustedPublishers.filter(publisher => !result.some(r => r.id === publisher.publisher)); - extensionManagementService.untrustPublishers(...untrustedPublishers); - } - } - }); - - } - - private registerExtensionAction(extensionActionOptions: IExtensionActionOptions): IDisposable { - const menus = extensionActionOptions.menu ? Array.isArray(extensionActionOptions.menu) ? extensionActionOptions.menu : [extensionActionOptions.menu] : []; - let menusWithOutTitles: ({ id: MenuId } & Omit)[] = []; - const menusWithTitles: { id: MenuId; item: IMenuItem }[] = []; - if (extensionActionOptions.menuTitles) { - for (let index = 0; index < menus.length; index++) { - const menu = menus[index]; - const menuTitle = extensionActionOptions.menuTitles[menu.id.id]; - if (menuTitle) { - menusWithTitles.push({ id: menu.id, item: { ...menu, command: { id: extensionActionOptions.id, title: menuTitle } } }); - } else { - menusWithOutTitles.push(menu); - } - } - } else { - menusWithOutTitles = menus; - } - const disposables = new DisposableStore(); - disposables.add(registerAction2(class extends Action2 { - constructor() { - super({ - ...extensionActionOptions, - menu: menusWithOutTitles - }); - } - run(accessor: ServicesAccessor, ...args: unknown[]): Promise { - return extensionActionOptions.run(accessor, ...args); - } - })); - if (menusWithTitles.length) { - disposables.add(MenuRegistry.appendMenuItems(menusWithTitles)); - } - return disposables; - } - -} +// MEMBRANE: +// disable all ExtensionsContributions +// async function runAction(action: IAction): Promise { +// try { +// await action.run(); +// } finally { +// if (isDisposable(action)) { +// action.dispose(); +// } +// } +// } + +// type IExtensionActionOptions = IAction2Options & { +// menuTitles?: { [id: string]: string }; +// run(accessor: ServicesAccessor, ...args: any[]): Promise; +// }; + +// class ExtensionsContributions extends Disposable implements IWorkbenchContribution { + +// constructor( +// @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, +// @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, +// @IContextKeyService contextKeyService: IContextKeyService, +// @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService, +// @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, +// @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, +// @IInstantiationService private readonly instantiationService: IInstantiationService, +// @IDialogService private readonly dialogService: IDialogService, +// @ICommandService private readonly commandService: ICommandService, +// ) { +// super(); +// const hasGalleryContext = CONTEXT_HAS_GALLERY.bindTo(contextKeyService); +// if (extensionGalleryService.isEnabled()) { +// hasGalleryContext.set(true); +// } + +// const hasLocalServerContext = CONTEXT_HAS_LOCAL_SERVER.bindTo(contextKeyService); +// if (this.extensionManagementServerService.localExtensionManagementServer) { +// hasLocalServerContext.set(true); +// } + +// const hasRemoteServerContext = CONTEXT_HAS_REMOTE_SERVER.bindTo(contextKeyService); +// if (this.extensionManagementServerService.remoteExtensionManagementServer) { +// hasRemoteServerContext.set(true); +// } + +// const hasWebServerContext = CONTEXT_HAS_WEB_SERVER.bindTo(contextKeyService); +// if (this.extensionManagementServerService.webExtensionManagementServer) { +// hasWebServerContext.set(true); +// } + +// this.registerGlobalActions(); +// this.registerContextMenuActions(); +// this.registerQuickAccessProvider(); +// } + +// private registerQuickAccessProvider(): void { +// if (this.extensionManagementServerService.localExtensionManagementServer +// || this.extensionManagementServerService.remoteExtensionManagementServer +// || this.extensionManagementServerService.webExtensionManagementServer +// ) { +// Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({ +// ctor: InstallExtensionQuickAccessProvider, +// prefix: InstallExtensionQuickAccessProvider.PREFIX, +// placeholder: localize('installExtensionQuickAccessPlaceholder', "Type the name of an extension to install or search."), +// helpEntries: [{ description: localize('installExtensionQuickAccessHelp', "Install or Search Extensions") }] +// }); +// } +// } + +// // Global actions +// private registerGlobalActions(): void { +// this._register(MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { +// command: { +// id: VIEWLET_ID, +// title: localize({ key: 'miPreferencesExtensions', comment: ['&& denotes a mnemonic'] }, "&&Extensions") +// }, +// group: '2_configuration', +// order: 3 +// })); +// this._register(MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { +// command: { +// id: VIEWLET_ID, +// title: localize('showExtensions', "Extensions") +// }, +// group: '2_configuration', +// order: 3 +// })); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.focusExtensionsView', +// title: localize2('focusExtensions', 'Focus on Extensions View'), +// category: ExtensionsLocalizedLabel, +// f1: true, +// run: async (accessor: ServicesAccessor) => { +// await accessor.get(IPaneCompositePartService).openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.installExtensions', +// title: localize2('installExtensions', 'Install Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) +// }, +// run: async (accessor: ServicesAccessor) => { +// accessor.get(IViewsService).openViewContainer(VIEWLET_ID, true); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showRecommendedKeymapExtensions', +// title: localize2('showRecommendedKeymapExtensionsShort', 'Keymaps'), +// category: PreferencesLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: CONTEXT_HAS_GALLERY +// }, { +// id: MenuId.EditorTitle, +// when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_HAS_GALLERY), +// group: '2_keyboard_discover_actions' +// }], +// menuTitles: { +// [MenuId.EditorTitle.id]: localize('importKeyboardShortcutsFroms', "Migrate Keyboard Shortcuts from...") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended:keymaps ')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showLanguageExtensions', +// title: localize2('showLanguageExtensionsShort', 'Language Extensions'), +// category: PreferencesLocalizedLabel, +// menu: { +// id: MenuId.CommandPalette, +// when: CONTEXT_HAS_GALLERY +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended:languages ')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.checkForUpdates', +// title: localize2('checkForUpdates', 'Check for Extension Updates'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) +// }, { +// id: MenuId.ViewContainerTitle, +// when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY), +// group: '1_updates', +// order: 1 +// }], +// run: async () => { +// await this.extensionsWorkbenchService.checkForUpdates(); +// const outdated = this.extensionsWorkbenchService.outdated; +// if (outdated.length) { +// return runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@outdated ')); +// } else { +// return this.dialogService.info(localize('noUpdatesAvailable', "All extensions are up to date.")); +// } +// } +// }); + +// const autoUpdateExtensionsSubMenu = new MenuId('autoUpdateExtensionsSubMenu'); +// MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { +// submenu: autoUpdateExtensionsSubMenu, +// title: localize('configure auto updating extensions', "Auto Update Extensions"), +// when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY), +// group: '1_updates', +// order: 5, +// }); + +// this.registerExtensionAction({ +// id: 'configureExtensionsAutoUpdate.all', +// title: localize('configureExtensionsAutoUpdate.all', "All Extensions"), +// toggled: ContextKeyExpr.and(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions')), +// menu: [{ +// id: autoUpdateExtensionsSubMenu, +// order: 1, +// }], +// run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true) +// }); + +// this.registerExtensionAction({ +// id: 'configureExtensionsAutoUpdate.enabled', +// title: localize('configureExtensionsAutoUpdate.enabled', "Enabled Extensions"), +// toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), +// menu: [{ +// id: autoUpdateExtensionsSubMenu, +// order: 2, +// }], +// run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlyEnabledExtensions') +// }); + +// this.registerExtensionAction({ +// id: 'configureExtensionsAutoUpdate.selected', +// title: localize('configureExtensionsAutoUpdate.selected', "Selected Extensions"), +// toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), +// menu: [{ +// id: autoUpdateExtensionsSubMenu, +// order: 2, +// }], +// run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlySelectedExtensions') +// }); + +// this.registerExtensionAction({ +// id: 'configureExtensionsAutoUpdate.none', +// title: localize('configureExtensionsAutoUpdate.none', "None"), +// toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false), +// menu: [{ +// id: autoUpdateExtensionsSubMenu, +// order: 3, +// }], +// run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.updateAllExtensions', +// title: localize2('updateAll', 'Update All Extensions'), +// category: ExtensionsLocalizedLabel, +// precondition: HasOutdatedExtensionsContext, +// menu: [ +// { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) +// }, { +// id: MenuId.ViewContainerTitle, +// when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`).negate(), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'))), +// group: '1_updates', +// order: 2 +// }, { +// id: MenuId.ViewTitle, +// when: ContextKeyExpr.equals('view', OUTDATED_EXTENSIONS_VIEW_ID), +// group: 'navigation', +// order: 1 +// } +// ], +// icon: installWorkspaceRecommendedIcon, +// run: async () => { +// const outdated = this.extensionsWorkbenchService.outdated; +// const results = await this.extensionsWorkbenchService.updateAll(); +// results.forEach((result) => { +// if (result.error) { +// const extension: IExtension | undefined = outdated.find((extension) => areSameExtensions(extension.identifier, result.identifier)); +// if (extension) { +// runAction(this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, result.error)); +// } +// } +// }); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.disableAutoUpdate', +// title: localize2('disableAutoUpdate', 'Disable Auto Update for All Extensions'), +// category: ExtensionsLocalizedLabel, +// f1: true, +// precondition: CONTEXT_HAS_GALLERY, +// run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.enableAutoUpdate', +// title: localize2('enableAutoUpdate', 'Enable Auto Update for All Extensions'), +// category: ExtensionsLocalizedLabel, +// f1: true, +// precondition: CONTEXT_HAS_GALLERY, +// run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.enableAll', +// title: localize2('enableAll', 'Enable All Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) +// }, { +// id: MenuId.ViewContainerTitle, +// when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), +// group: '2_enablement', +// order: 1 +// }], +// run: async () => { +// const extensionsToEnable = this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); +// if (extensionsToEnable.length) { +// await this.extensionsWorkbenchService.setEnablement(extensionsToEnable, EnablementState.EnabledGlobally); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.enableAllWorkspace', +// title: localize2('enableAllWorkspace', 'Enable All Extensions for this Workspace'), +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) +// }, +// run: async () => { +// const extensionsToEnable = this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local)); +// if (extensionsToEnable.length) { +// await this.extensionsWorkbenchService.setEnablement(extensionsToEnable, EnablementState.EnabledWorkspace); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.disableAll', +// title: localize2('disableAll', 'Disable All Installed Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) +// }, { +// id: MenuId.ViewContainerTitle, +// when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), +// group: '2_enablement', +// order: 2 +// }], +// run: async () => { +// const extensionsToDisable = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); +// if (extensionsToDisable.length) { +// await this.extensionsWorkbenchService.setEnablement(extensionsToDisable, EnablementState.DisabledGlobally); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.disableAllWorkspace', +// title: localize2('disableAllWorkspace', 'Disable All Installed Extensions for this Workspace'), +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) +// }, +// run: async () => { +// const extensionsToDisable = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local)); +// if (extensionsToDisable.length) { +// await this.extensionsWorkbenchService.setEnablement(extensionsToDisable, EnablementState.DisabledWorkspace); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, +// title: localize2('InstallFromVSIX', 'Install from VSIX...'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER) +// }, { +// id: MenuId.ViewContainerTitle, +// when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)), +// group: '3_install', +// order: 1 +// }], +// run: async (accessor: ServicesAccessor) => { +// const fileDialogService = accessor.get(IFileDialogService); +// const commandService = accessor.get(ICommandService); +// const vsixPaths = await fileDialogService.showOpenDialog({ +// title: localize('installFromVSIX', "Install from VSIX"), +// filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }], +// canSelectFiles: true, +// canSelectMany: true, +// openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install")) +// }); +// if (vsixPaths) { +// await commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, +// title: localize('installVSIX', "Install Extension VSIX"), +// menu: [{ +// id: MenuId.ExplorerContext, +// group: 'extensions', +// when: ContextKeyExpr.and(ResourceContextKey.Extension.isEqualTo('.vsix'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)), +// }], +// run: async (accessor: ServicesAccessor, resources: URI[] | URI) => { +// const extensionService = accessor.get(IExtensionService); +// const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const hostService = accessor.get(IHostService); +// const notificationService = accessor.get(INotificationService); + +// const extensions = Array.isArray(resources) ? resources : [resources]; +// await Promises.settled(extensions.map(async (vsix) => await extensionsWorkbenchService.install(vsix))) +// .then(async (extensions) => { +// for (const extension of extensions) { +// const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local))); +// const message = requireReload ? localize('InstallVSIXAction.successReload', "Completed installing {0} extension from VSIX. Please reload Visual Studio Code to enable it.", extension.displayName || extension.name) +// : localize('InstallVSIXAction.success', "Completed installing {0} extension from VSIX.", extension.displayName || extension.name); +// const actions = requireReload ? [{ +// label: localize('InstallVSIXAction.reloadNow', "Reload Now"), +// run: () => hostService.reload() +// }] : []; +// notificationService.prompt( +// Severity.Info, +// message, +// actions +// ); +// } +// }); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.installExtensionFromLocation', +// title: localize2('installExtensionFromLocation', 'Install Extension from Location...'), +// category: Categories.Developer, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_WEB_SERVER, CONTEXT_HAS_LOCAL_SERVER) +// }], +// run: async (accessor: ServicesAccessor) => { +// const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService); +// if (isWeb) { +// const quickInputService = accessor.get(IQuickInputService); +// const disposables = new DisposableStore(); +// const quickPick = disposables.add(quickInputService.createQuickPick()); +// quickPick.title = localize('installFromLocation', "Install Extension from Location"); +// quickPick.customButton = true; +// quickPick.customLabel = localize('install button', "Install"); +// quickPick.placeholder = localize('installFromLocationPlaceHolder', "Location of the web extension"); +// quickPick.ignoreFocusOut = true; +// disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(() => { +// quickPick.hide(); +// if (quickPick.value) { +// extensionManagementService.installFromLocation(URI.parse(quickPick.value)); +// } +// })); +// disposables.add(quickPick.onDidHide(() => disposables.dispose())); +// quickPick.show(); +// } else { +// const fileDialogService = accessor.get(IFileDialogService); +// const extensionLocation = await fileDialogService.showOpenDialog({ +// canSelectFolders: true, +// canSelectFiles: false, +// canSelectMany: false, +// title: localize('installFromLocation', "Install Extension from Location"), +// }); +// if (extensionLocation?.[0]) { +// extensionManagementService.installFromLocation(extensionLocation[0]); +// } +// } +// } +// }); + +// const extensionsFilterSubMenu = new MenuId('extensionsFilterSubMenu'); +// MenuRegistry.appendMenuItem(extensionsSearchActionsMenu, { +// submenu: extensionsFilterSubMenu, +// title: localize('filterExtensions', "Filter Extensions..."), +// group: 'navigation', +// order: 2, +// icon: filterIcon, +// }); + +// const showFeaturedExtensionsId = 'extensions.filter.featured'; +// this.registerExtensionAction({ +// id: showFeaturedExtensionsId, +// title: localize2('showFeaturedExtensions', 'Show Featured Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: CONTEXT_HAS_GALLERY +// }, { +// id: extensionsFilterSubMenu, +// when: CONTEXT_HAS_GALLERY, +// group: '1_predefined', +// order: 1, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('featured filter', "Featured") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@featured ')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showPopularExtensions', +// title: localize2('showPopularExtensions', 'Show Popular Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: CONTEXT_HAS_GALLERY +// }, { +// id: extensionsFilterSubMenu, +// when: CONTEXT_HAS_GALLERY, +// group: '1_predefined', +// order: 2, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('most popular filter', "Most Popular") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@popular ')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showRecommendedExtensions', +// title: localize2('showRecommendedExtensions', 'Show Recommended Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: CONTEXT_HAS_GALLERY +// }, { +// id: extensionsFilterSubMenu, +// when: CONTEXT_HAS_GALLERY, +// group: '1_predefined', +// order: 2, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('most popular recommended', "Recommended") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended ')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.recentlyPublishedExtensions', +// title: localize2('recentlyPublishedExtensions', 'Show Recently Published Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: CONTEXT_HAS_GALLERY +// }, { +// id: extensionsFilterSubMenu, +// when: CONTEXT_HAS_GALLERY, +// group: '1_predefined', +// order: 2, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('recently published filter', "Recently Published") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recentlyPublished ')) +// }); + +// const extensionsCategoryFilterSubMenu = new MenuId('extensionsCategoryFilterSubMenu'); +// MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { +// submenu: extensionsCategoryFilterSubMenu, +// title: localize('filter by category', "Category"), +// when: CONTEXT_HAS_GALLERY, +// group: '2_categories', +// order: 1, +// }); + +// EXTENSION_CATEGORIES.map((category, index) => { +// this.registerExtensionAction({ +// id: `extensions.actions.searchByCategory.${category}`, +// title: category, +// menu: [{ +// id: extensionsCategoryFilterSubMenu, +// when: CONTEXT_HAS_GALLERY, +// order: index, +// }], +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, `@category:"${category.toLowerCase()}"`)) +// }); +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.listBuiltInExtensions', +// title: localize2('showBuiltInExtensions', 'Show Built-in Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) +// }, { +// id: extensionsFilterSubMenu, +// group: '3_installed', +// order: 2, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('builtin filter', "Built-in") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@builtin ')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.extensionUpdates', +// title: localize2('extensionUpdates', 'Show Extension Updates'), +// category: ExtensionsLocalizedLabel, +// precondition: CONTEXT_HAS_GALLERY, +// f1: true, +// menu: [{ +// id: extensionsFilterSubMenu, +// group: '3_installed', +// when: CONTEXT_HAS_GALLERY, +// order: 1, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('extension updates filter', "Updates") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@updates')) +// }); + +// this.registerExtensionAction({ +// id: LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, +// title: localize2('showWorkspaceUnsupportedExtensions', 'Show Extensions Unsupported By Workspace'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER), +// }, { +// id: extensionsFilterSubMenu, +// group: '3_installed', +// order: 5, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER), +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('workspace unsupported filter', "Workspace Unsupported") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@workspaceUnsupported')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showEnabledExtensions', +// title: localize2('showEnabledExtensions', 'Show Enabled Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) +// }, { +// id: extensionsFilterSubMenu, +// group: '3_installed', +// order: 3, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('enabled filter', "Enabled") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@enabled ')) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showDisabledExtensions', +// title: localize2('showDisabledExtensions', 'Show Disabled Extensions'), +// category: ExtensionsLocalizedLabel, +// menu: [{ +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER) +// }, { +// id: extensionsFilterSubMenu, +// group: '3_installed', +// order: 4, +// }], +// menuTitles: { +// [extensionsFilterSubMenu.id]: localize('disabled filter', "Disabled") +// }, +// run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@disabled ')) +// }); + +// const extensionsSortSubMenu = new MenuId('extensionsSortSubMenu'); +// MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { +// submenu: extensionsSortSubMenu, +// title: localize('sorty by', "Sort By"), +// when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext)), +// group: '4_sort', +// order: 1, +// }); + +// [ +// { id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate() }, +// { id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate() }, +// { id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate() }, +// { id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate() }, +// { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()) }, +// ].map(({ id, title, precondition }, index) => { +// this.registerExtensionAction({ +// id: `extensions.sort.${id}`, +// title, +// precondition: precondition, +// menu: [{ +// id: extensionsSortSubMenu, +// when: ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), +// order: index, +// }], +// toggled: ExtensionsSortByContext.isEqualTo(id), +// run: async () => { +// const viewlet = await this.paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); +// const extensionsViewPaneContainer = viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer; +// const currentQuery = Query.parse(extensionsViewPaneContainer.searchValue || ''); +// extensionsViewPaneContainer.search(new Query(currentQuery.value, id).toString()); +// extensionsViewPaneContainer.focus(); +// } +// }); +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.clearExtensionsSearchResults', +// title: localize2('clearExtensionsSearchResults', 'Clear Extensions Search Results'), +// category: ExtensionsLocalizedLabel, +// icon: clearSearchResultsIcon, +// f1: true, +// precondition: SearchHasTextContext, +// menu: { +// id: extensionsSearchActionsMenu, +// group: 'navigation', +// order: 1, +// }, +// run: async (accessor: ServicesAccessor) => { +// const viewPaneContainer = accessor.get(IViewsService).getActiveViewPaneContainerWithId(VIEWLET_ID); +// if (viewPaneContainer) { +// const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer; +// extensionsViewPaneContainer.search(''); +// extensionsViewPaneContainer.focus(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.refreshExtension', +// title: localize2('refreshExtension', 'Refresh'), +// category: ExtensionsLocalizedLabel, +// icon: refreshIcon, +// f1: true, +// menu: { +// id: MenuId.ViewContainerTitle, +// when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), +// group: 'navigation', +// order: 2 +// }, +// run: async (accessor: ServicesAccessor) => { +// const viewPaneContainer = accessor.get(IViewsService).getActiveViewPaneContainerWithId(VIEWLET_ID); +// if (viewPaneContainer) { +// await (viewPaneContainer as IExtensionsViewPaneContainer).refresh(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.installWorkspaceRecommendedExtensions', +// title: localize('installWorkspaceRecommendedExtensions', "Install Workspace Recommended Extensions"), +// icon: installWorkspaceRecommendedIcon, +// menu: { +// id: MenuId.ViewTitle, +// when: ContextKeyExpr.equals('view', WORKSPACE_RECOMMENDATIONS_VIEW_ID), +// group: 'navigation', +// order: 1 +// }, +// run: async (accessor: ServicesAccessor) => { +// const view = accessor.get(IViewsService).getActiveViewWithId(WORKSPACE_RECOMMENDATIONS_VIEW_ID) as IWorkspaceRecommendedExtensionsView; +// return view.installWorkspaceRecommendations(); +// } +// }); + +// this.registerExtensionAction({ +// id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, +// title: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, +// icon: configureRecommendedIcon, +// menu: [{ +// id: MenuId.CommandPalette, +// when: WorkbenchStateContext.notEqualsTo('empty'), +// }, { +// id: MenuId.ViewTitle, +// when: ContextKeyExpr.equals('view', WORKSPACE_RECOMMENDATIONS_VIEW_ID), +// group: 'navigation', +// order: 2 +// }], +// run: () => runAction(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL)) +// }); + +// this.registerExtensionAction({ +// id: InstallSpecificVersionOfExtensionAction.ID, +// title: { value: InstallSpecificVersionOfExtensionAction.LABEL, original: 'Install Specific Version of Extension...' }, +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER, CONTEXT_HAS_WEB_SERVER)) +// }, +// run: () => runAction(this.instantiationService.createInstance(InstallSpecificVersionOfExtensionAction, InstallSpecificVersionOfExtensionAction.ID, InstallSpecificVersionOfExtensionAction.LABEL)) +// }); + +// this.registerExtensionAction({ +// id: ReinstallAction.ID, +// title: { value: ReinstallAction.LABEL, original: 'Reinstall Extension...' }, +// category: Categories.Developer, +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)) +// }, +// run: () => runAction(this.instantiationService.createInstance(ReinstallAction, ReinstallAction.ID, ReinstallAction.LABEL)) +// }); +// } + +// // Extension Context Menu +// private registerContextMenuActions(): void { + +// this.registerExtensionAction({ +// id: SetColorThemeAction.ID, +// title: SetColorThemeAction.TITLE, +// menu: { +// id: MenuId.ExtensionContext, +// group: THEME_ACTIONS_GROUP, +// order: 0, +// when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasColorThemes')) +// }, +// run: async (accessor: ServicesAccessor, extensionId: string) => { +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const instantiationService = accessor.get(IInstantiationService); +// const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); +// if (extension) { +// const action = instantiationService.createInstance(SetColorThemeAction); +// action.extension = extension; +// return action.run(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: SetFileIconThemeAction.ID, +// title: SetFileIconThemeAction.TITLE, +// menu: { +// id: MenuId.ExtensionContext, +// group: THEME_ACTIONS_GROUP, +// order: 0, +// when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasFileIconThemes')) +// }, +// run: async (accessor: ServicesAccessor, extensionId: string) => { +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const instantiationService = accessor.get(IInstantiationService); +// const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); +// if (extension) { +// const action = instantiationService.createInstance(SetFileIconThemeAction); +// action.extension = extension; +// return action.run(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: SetProductIconThemeAction.ID, +// title: SetProductIconThemeAction.TITLE, +// menu: { +// id: MenuId.ExtensionContext, +// group: THEME_ACTIONS_GROUP, +// order: 0, +// when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasProductIconThemes')) +// }, +// run: async (accessor: ServicesAccessor, extensionId: string) => { +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const instantiationService = accessor.get(IInstantiationService); +// const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); +// if (extension) { +// const action = instantiationService.createInstance(SetProductIconThemeAction); +// action.extension = extension; +// return action.run(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showPreReleaseVersion', +// title: localize2('show pre-release version', 'Show Pre-Release Version'), +// menu: { +// id: MenuId.ExtensionContext, +// group: INSTALL_ACTIONS_GROUP, +// order: 0, +// when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.not('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) +// }, +// run: async (accessor: ServicesAccessor, extensionId: string) => { +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const extension = (await extensionWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; +// extensionWorkbenchService.open(extension, { showPreReleaseVersion: true }); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.showReleasedVersion', +// title: localize2('show released version', 'Show Release Version'), +// menu: { +// id: MenuId.ExtensionContext, +// group: INSTALL_ACTIONS_GROUP, +// order: 1, +// when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) +// }, +// run: async (accessor: ServicesAccessor, extensionId: string) => { +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const extension = (await extensionWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; +// extensionWorkbenchService.open(extension, { showPreReleaseVersion: false }); +// } +// }); + +// this.registerExtensionAction({ +// id: ToggleAutoUpdateForExtensionAction.ID, +// title: { value: ToggleAutoUpdateForExtensionAction.LABEL, original: 'Auto Update' }, +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.ExtensionContext, +// group: UPDATE_ACTIONS_GROUP, +// order: 1, +// when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.or(ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false)),) +// }, +// run: async (accessor: ServicesAccessor, id: string) => { +// const instantiationService = accessor.get(IInstantiationService); +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); +// if (extension) { +// const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, false, []); +// action.extension = extension; +// return action.run(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: ToggleAutoUpdatesForPublisherAction.ID, +// title: { value: ToggleAutoUpdatesForPublisherAction.LABEL, original: 'Auto Update (Publisher)' }, +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.ExtensionContext, +// group: UPDATE_ACTIONS_GROUP, +// order: 2, +// when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.or(ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false)),) +// }, +// run: async (accessor: ServicesAccessor, id: string) => { +// const instantiationService = accessor.get(IInstantiationService); +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); +// if (extension) { +// const action = instantiationService.createInstance(ToggleAutoUpdatesForPublisherAction); +// action.extension = extension; +// return action.run(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.switchToPreRlease', +// title: localize('enablePreRleaseLabel', "Switch to Pre-Release Version"), +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.ExtensionContext, +// group: INSTALL_ACTIONS_GROUP, +// order: 2, +// when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.not('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) +// }, +// run: async (accessor: ServicesAccessor, id: string) => { +// const instantiationService = accessor.get(IInstantiationService); +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); +// if (extension) { +// const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); +// action.extension = extension; +// return action.run(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.switchToRelease', +// title: localize('disablePreRleaseLabel', "Switch to Release Version"), +// category: ExtensionsLocalizedLabel, +// menu: { +// id: MenuId.ExtensionContext, +// group: INSTALL_ACTIONS_GROUP, +// order: 2, +// when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) +// }, +// run: async (accessor: ServicesAccessor, id: string) => { +// const instantiationService = accessor.get(IInstantiationService); +// const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); +// if (extension) { +// const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); +// action.extension = extension; +// return action.run(); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: ClearLanguageAction.ID, +// title: ClearLanguageAction.TITLE, +// menu: { +// id: MenuId.ExtensionContext, +// group: INSTALL_ACTIONS_GROUP, +// order: 0, +// when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.has('canSetLanguage'), ContextKeyExpr.has('isActiveLanguagePackExtension')) +// }, +// run: async (accessor: ServicesAccessor, extensionId: string) => { +// const instantiationService = accessor.get(IInstantiationService); +// const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); +// const extension = (await extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; +// const action = instantiationService.createInstance(ClearLanguageAction); +// action.extension = extension; +// return action.run(); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.copyExtension', +// title: localize2('workbench.extensions.action.copyExtension', 'Copy'), +// menu: { +// id: MenuId.ExtensionContext, +// group: '1_copy' +// }, +// run: async (accessor: ServicesAccessor, extensionId: string) => { +// const clipboardService = accessor.get(IClipboardService); +// const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] +// || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; +// if (extension) { +// const name = localize('extensionInfoName', 'Name: {0}', extension.displayName); +// const id = localize('extensionInfoId', 'Id: {0}', extensionId); +// const description = localize('extensionInfoDescription', 'Description: {0}', extension.description); +// const verision = localize('extensionInfoVersion', 'Version: {0}', extension.version); +// const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', extension.publisherDisplayName); +// const link = extension.url ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', `${extension.url}`) : null; +// const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`; +// await clipboardService.writeText(clipboardStr); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.copyExtensionId', +// title: localize2('workbench.extensions.action.copyExtensionId', 'Copy Extension ID'), +// menu: { +// id: MenuId.ExtensionContext, +// group: '1_copy' +// }, +// run: async (accessor: ServicesAccessor, id: string) => accessor.get(IClipboardService).writeText(id) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.configure', +// title: localize2('workbench.extensions.action.configure', 'Extension Settings'), +// menu: { +// id: MenuId.ExtensionContext, +// group: '2_configure', +// when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')), +// order: 1 +// }, +// run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openSettings({ jsonEditor: false, query: `@ext:${id}` }) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.configureKeybindings', +// title: localize2('workbench.extensions.action.configureKeybindings', 'Extension Keyboard Shortcuts'), +// menu: { +// id: MenuId.ExtensionContext, +// group: '2_configure', +// when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasKeybindings')), +// order: 2 +// }, +// run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query: `@ext:${id}` }) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.toggleApplyToAllProfiles', +// title: { value: localize('workbench.extensions.action.toggleApplyToAllProfiles', "Apply Extension to all Profiles"), original: `Apply Extension to all Profiles` }, +// toggled: ContextKeyExpr.has('isApplicationScopedExtension'), +// menu: { +// id: MenuId.ExtensionContext, +// group: '2_configure', +// when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('isDefaultApplicationScopedExtension').negate(), ContextKeyExpr.has('isBuiltinExtension').negate()), +// order: 3 +// }, +// run: async (accessor: ServicesAccessor, id: string) => { +// const extension = this.extensionsWorkbenchService.local.find(e => areSameExtensions({ id }, e.identifier)); +// if (extension) { +// return this.extensionsWorkbenchService.toggleApplyExtensionToAllProfiles(extension); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: TOGGLE_IGNORE_EXTENSION_ACTION_ID, +// title: { value: localize('workbench.extensions.action.toggleIgnoreExtension', "Sync This Extension"), original: `Sync This Extension` }, +// menu: { +// id: MenuId.ExtensionContext, +// group: '2_configure', +// when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT), +// order: 4 +// }, +// run: async (accessor: ServicesAccessor, id: string) => { +// const extension = this.extensionsWorkbenchService.local.find(e => areSameExtensions({ id }, e.identifier)); +// if (extension) { +// return this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(extension); +// } +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.ignoreRecommendation', +// title: { value: localize('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), original: `Ignore Recommendation` }, +// menu: { +// id: MenuId.ExtensionContext, +// group: '3_recommendations', +// when: ContextKeyExpr.has('isExtensionRecommended'), +// order: 1 +// }, +// run: async (accessor: ServicesAccessor, id: string) => accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, true) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.undoIgnoredRecommendation', +// title: { value: localize('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), original: `Undo Ignored Recommendation` }, +// menu: { +// id: MenuId.ExtensionContext, +// group: '3_recommendations', +// when: ContextKeyExpr.has('isUserIgnoredRecommendation'), +// order: 1 +// }, +// run: async (accessor: ServicesAccessor, id: string) => accessor.get(IExtensionIgnoredRecommendationsService).toggleGlobalIgnoredRecommendation(id, false) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations', +// title: { value: localize('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), original: `Add to Workspace Recommendations` }, +// menu: { +// id: MenuId.ExtensionContext, +// group: '3_recommendations', +// when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate()), +// order: 2 +// }, +// run: (accessor: ServicesAccessor, id: string) => accessor.get(IWorkspaceExtensionsConfigService).toggleRecommendation(id) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', +// title: { value: localize('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), original: `Remove from Workspace Recommendations` }, +// menu: { +// id: MenuId.ExtensionContext, +// group: '3_recommendations', +// when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended')), +// order: 2 +// }, +// run: (accessor: ServicesAccessor, id: string) => accessor.get(IWorkspaceExtensionsConfigService).toggleRecommendation(id) +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.addToWorkspaceRecommendations', +// title: { value: localize('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), original: `Add Extension to Workspace Recommendations` }, +// category: localize('extensions', "Extensions"), +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), +// }, +// async run(accessor: ServicesAccessor): Promise { +// const editorService = accessor.get(IEditorService); +// const workspaceExtensionsConfigService = accessor.get(IWorkspaceExtensionsConfigService); +// if (!(editorService.activeEditor instanceof ExtensionsInput)) { +// return; +// } +// const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); +// const recommendations = await workspaceExtensionsConfigService.getRecommendations(); +// if (recommendations.includes(extensionId)) { +// return; +// } +// await workspaceExtensionsConfigService.toggleRecommendation(extensionId); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations', +// title: { value: localize('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), original: `Add Extension to Workspace Folder Recommendations` }, +// category: localize('extensions', "Extensions"), +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), +// }, +// run: () => this.commandService.executeCommand('workbench.extensions.action.addToWorkspaceRecommendations') +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations', +// title: { value: localize('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), original: `Add Extension to Workspace Ignored Recommendations` }, +// category: localize('extensions', "Extensions"), +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), +// }, +// async run(accessor: ServicesAccessor): Promise { +// const editorService = accessor.get(IEditorService); +// const workspaceExtensionsConfigService = accessor.get(IWorkspaceExtensionsConfigService); +// if (!(editorService.activeEditor instanceof ExtensionsInput)) { +// return; +// } +// const extensionId = editorService.activeEditor.extension.identifier.id.toLowerCase(); +// const unwantedRecommendations = await workspaceExtensionsConfigService.getUnwantedRecommendations(); +// if (unwantedRecommendations.includes(extensionId)) { +// return; +// } +// await workspaceExtensionsConfigService.toggleUnwantedRecommendation(extensionId); +// } +// }); + +// this.registerExtensionAction({ +// id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', +// title: { value: localize('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), original: `Add Extension to Workspace Folder Ignored Recommendations` }, +// category: localize('extensions', "Extensions"), +// menu: { +// id: MenuId.CommandPalette, +// when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), +// }, +// run: () => this.commandService.executeCommand('workbench.extensions.action.addToWorkspaceIgnoredRecommendations') +// }); + +// this.registerExtensionAction({ +// id: ConfigureWorkspaceRecommendedExtensionsAction.ID, +// title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, +// category: localize('extensions', "Extensions"), +// menu: { +// id: MenuId.CommandPalette, +// when: WorkbenchStateContext.isEqualTo('workspace'), +// }, +// run: () => runAction(this.instantiationService.createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL)) +// }); + +// } + +// private registerExtensionAction(extensionActionOptions: IExtensionActionOptions): IDisposable { +// const menus = extensionActionOptions.menu ? Array.isArray(extensionActionOptions.menu) ? extensionActionOptions.menu : [extensionActionOptions.menu] : []; +// let menusWithOutTitles: ({ id: MenuId } & Omit)[] = []; +// const menusWithTitles: { id: MenuId; item: IMenuItem }[] = []; +// if (extensionActionOptions.menuTitles) { +// for (let index = 0; index < menus.length; index++) { +// const menu = menus[index]; +// const menuTitle = extensionActionOptions.menuTitles[menu.id.id]; +// if (menuTitle) { +// menusWithTitles.push({ id: menu.id, item: { ...menu, command: { id: extensionActionOptions.id, title: menuTitle } } }); +// } else { +// menusWithOutTitles.push(menu); +// } +// } +// } else { +// menusWithOutTitles = menus; +// } +// const disposables = new DisposableStore(); +// disposables.add(registerAction2(class extends Action2 { +// constructor() { +// super({ +// ...extensionActionOptions, +// menu: menusWithOutTitles +// }); +// } +// run(accessor: ServicesAccessor, ...args: any[]): Promise { +// return extensionActionOptions.run(accessor, ...args); +// } +// })); +// if (menusWithTitles.length) { +// disposables.add(MenuRegistry.appendMenuItems(menusWithTitles)); +// } +// return disposables; +// } + +// } class ExtensionStorageCleaner implements IWorkbenchContribution { @@ -1979,100 +1733,24 @@ class ExtensionStorageCleaner implements IWorkbenchContribution { } } -class TrustedPublishersInitializer implements IWorkbenchContribution { - constructor( - @IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService, - @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, - @IProductService productService: IProductService, - @IStorageService storageService: IStorageService, - ) { - const trustedPublishersInitStatusKey = 'trusted-publishers-init-migration'; - if (!storageService.get(trustedPublishersInitStatusKey, StorageScope.APPLICATION)) { - for (const profile of userDataProfilesService.profiles) { - extensionManagementService.getInstalled(ExtensionType.User, profile.extensionsResource) - .then(async extensions => { - const trustedPublishers = new Map(); - for (const extension of extensions) { - if (!extension.publisherDisplayName) { - continue; - } - const publisher = extension.manifest.publisher.toLowerCase(); - if (productService.trustedExtensionPublishers?.includes(publisher) - || (extension.publisherDisplayName && productService.trustedExtensionPublishers?.includes(extension.publisherDisplayName.toLowerCase()))) { - continue; - } - trustedPublishers.set(publisher, { publisher, publisherDisplayName: extension.publisherDisplayName }); - } - if (trustedPublishers.size) { - extensionManagementService.trustPublishers(...trustedPublishers.values()); - } - storageService.store(trustedPublishersInitStatusKey, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE); - }); - } - } - } -} - -class ExtensionToolsContribution extends Disposable implements IWorkbenchContribution { - - static readonly ID = 'extensions.chat.toolsContribution'; - - constructor( - @ILanguageModelToolsService toolsService: ILanguageModelToolsService, - @IInstantiationService instantiationService: IInstantiationService, - ) { - super(); - const searchExtensionsTool = instantiationService.createInstance(SearchExtensionsTool); - this._register(toolsService.registerTool(SearchExtensionsToolData, searchExtensionsTool)); - } -} - const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored); +// MEMBRANE: +// workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Restored); +// workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually); +// MEMBRANE: +// workbenchRegistry.registerWorkbenchContribution(ExtensionDependencyChecker, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(ExtensionEnablementWorkspaceTrustTransitionParticipant, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(ExtensionsCompletionItemsProvider, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(UnsupportedExtensionsMigrationContrib, LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(TrustedPublishersInitializer, LifecyclePhase.Eventually); -workbenchRegistry.registerWorkbenchContribution(ExtensionMarketplaceStatusUpdater, LifecyclePhase.Eventually); +// workbenchRegistry.registerWorkbenchContribution(DeprecatedExtensionsChecker, LifecyclePhase.Eventually); if (isWeb) { workbenchRegistry.registerWorkbenchContribution(ExtensionStorageCleaner, LifecyclePhase.Eventually); } -registerWorkbenchContribution2(ExtensionToolsContribution.ID, ExtensionToolsContribution, WorkbenchPhase.AfterRestored); - // Running Extensions -registerAction2(ShowRuntimeExtensionsAction); - -registerAction2(class ExtensionsGallerySignInAction extends Action2 { - constructor() { - super({ - id: 'workbench.extensions.actions.gallery.signIn', - title: localize2('signInToMarketplace', 'Sign in to access Extensions Marketplace'), - menu: { - id: MenuId.AccountsContext, - when: CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.RequiresSignIn) - }, - }); - } - run(accessor: ServicesAccessor): Promise { - return accessor.get(ICommandService).executeCommand(DEFAULT_ACCOUNT_SIGN_IN_COMMAND); - } -}); - -Registry.as(ConfigurationMigrationExtensions.ConfigurationMigration) - .registerConfigurationMigrations([{ - key: AutoUpdateConfigurationKey, - migrateFn: (value, accessor) => { - if (value === 'onlySelectedExtensions') { - return { value: false }; - } - return []; - } - }]); +registerAction2(ShowRuntimeExtensionsAction); \ No newline at end of file diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index ecc6af48668561..db85f0e4775949 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -19,7 +19,11 @@ import { IWorkspaceContextService, WorkbenchState } from '../../../../platform/w import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IContextKeyService, IContextKey, ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, ViewContentGroups } from '../../../common/views.js'; +import { + IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, + // IViewContainersRegistry, ViewContainerLocation, + IViewDescriptorService, ViewContentGroups +} from '../../../common/views.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; @@ -53,7 +57,8 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor super(); progressService.withProgress({ location: ProgressLocation.Explorer }, () => workspaceContextService.getCompleteWorkspace()).finally(() => { - this.registerViews(); + // MEMBRANE + // this.registerViews(); this._register(workspaceContextService.onDidChangeWorkbenchState(() => this.registerViews())); this._register(workspaceContextService.onDidChangeWorkspaceFolders(() => this.registerViews())); @@ -249,14 +254,16 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { } } -const viewContainerRegistry = Registry.as(Extensions.ViewContainersRegistry); +// MEMBRANE: Do not register explorer view but keep definition to avoid compilation errors +// const viewContainerRegistry = Registry.as(Extensions.ViewContainersRegistry); /** * Explorer viewlet container. */ -export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewContainer({ +export const VIEW_CONTAINER: ViewContainer = { id: VIEWLET_ID, - title: localize2('explore', "Explorer"), + // MEMBRANE: Call it "File Explorer" so it doesn't get confused with our "Graph Explorer" + title: localize2('explore', "File Explorer"), ctorDescriptor: new SyncDescriptor(ExplorerViewPaneContainer), storageId: 'workbench.explorer.views.state', icon: explorerViewIcon, @@ -265,12 +272,15 @@ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewC order: 0, openCommandActionDescriptor: { id: VIEWLET_ID, - title: localize2('explore', "Explorer"), + title: localize2('explore', "File Explorer"), mnemonicTitle: localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer"), keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyE }, order: 0 - }, -}, ViewContainerLocation.Sidebar, { isDefault: true }); + } +}; + +// MEMBRANE: Don't actually register the view container +// viewContainerRegistry.registerViewContainer(VIEW_CONTAINER, ViewContainerLocation.Sidebar, { isDefault: false, doNotRegisterOpenCommand: true }); const openFolder = localize('openFolder', "Open Folder"); const addAFolder = localize('addAFolder', "add a folder"); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 6cd04336cfb456..357b7c96837d2d 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from '../../../../nls.js'; -import { ToggleAutoSaveAction, FocusFilesExplorer, GlobalCompareResourcesAction, ShowActiveFileInExplorer, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, OpenActiveFileInEmptyWorkspace, UPLOAD_COMMAND_ID, UPLOAD_LABEL, CompareNewUntitledTextFilesAction, SetActiveEditorReadonlyInSession, SetActiveEditorWriteableInSession, ToggleActiveEditorReadonlyInSession, ResetActiveEditorReadonlyInSession } from './fileActions.js'; +import { ToggleAutoSaveAction, GlobalCompareResourcesAction, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, OpenActiveFileInEmptyWorkspace, UPLOAD_COMMAND_ID, UPLOAD_LABEL, CompareNewUntitledTextFilesAction, SetActiveEditorReadonlyInSession, SetActiveEditorWriteableInSession, ToggleActiveEditorReadonlyInSession, ResetActiveEditorReadonlyInSession } from './fileActions.js'; import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from './editors/textFileSaveErrorHandler.js'; import { MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ICommandAction } from '../../../../platform/action/common/action.js'; @@ -15,7 +15,7 @@ import { CommandsRegistry, ICommandHandler } from '../../../../platform/commands import { ContextKeyExpr, ContextKeyExpression } from '../../../../platform/contextkey/common/contextkey.js'; import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceWritableContext, ExplorerResourceCut, ExplorerResourceMoveableToTrash, ExplorerResourceAvailableEditorIdsContext, FoldersViewVisibleContext } from '../common/files.js'; -import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from '../../../browser/actions/workspaceCommands.js'; +// import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from '../../../browser/actions/workspaceCommands.js'; import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, REOPEN_WITH_COMMAND_ID } from '../../../browser/parts/editor/editorCommands.js'; import { AutoSaveAfterShortDelayContext } from '../../../services/filesConfiguration/common/filesConfigurationService.js'; import { WorkbenchListDoubleSelection } from '../../../../platform/list/browser/listService.js'; @@ -31,8 +31,9 @@ import { Categories } from '../../../../platform/action/common/actionCommonCateg // Contribute Global Actions registerAction2(GlobalCompareResourcesAction); -registerAction2(FocusFilesExplorer); -registerAction2(ShowActiveFileInExplorer); +// MEMBRANE: do not expose command to open the explorer viewlet +// registerAction2(FocusFilesExplorer); +// registerAction2(ShowActiveFileInExplorer); MEMBRANE: keep this because we open the Navigator instead registerAction2(CompareWithClipboardAction); registerAction2(CompareNewUntitledTextFilesAction); registerAction2(ToggleAutoSaveAction); @@ -159,6 +160,12 @@ export const revealInSideBarCommand = { title: nls.localize('revealInSideBar', "Reveal in Explorer View") }; +// Editor Title Context Menu +appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste', false); +appendEditorTitleContextMenuItem(COPY_RELATIVE_PATH_COMMAND_ID, copyRelativePathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste', false); +// MEMBRANE: Disable for now. Could reveal in Porgram Overview. +// appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Explorer View"), ResourceContextKey.IsFileSystemResource, '2_files', 1); + // Editor Title Context Menu appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste', true); appendEditorTitleContextMenuItem(COPY_RELATIVE_PATH_COMMAND_ID, copyRelativePathCommand.title, ResourceContextKey.IsFileSystemResource, '1_cutcopypaste', true); @@ -484,16 +491,17 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { when: ExplorerFolderContext }); -MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: 'navigation', - order: 6, - command: { - id: NEW_FOLDER_COMMAND_ID, - title: NEW_FOLDER_LABEL, - precondition: ExplorerResourceWritableContext - }, - when: ExplorerFolderContext -}); +// MEMBRANE +// MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { +// group: 'navigation', +// order: 6, +// command: { +// id: NEW_FOLDER_COMMAND_ID, +// title: NEW_FOLDER_LABEL, +// precondition: ExplorerResourceNotReadonlyContext +// }, +// when: ExplorerFolderContext +// }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: 'navigation', @@ -612,18 +620,20 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { when: ResourceContextKey.IsFileSystemResource }); -MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: '2_workspace', - order: 10, - command: { - id: ADD_ROOT_FOLDER_COMMAND_ID, - title: ADD_ROOT_FOLDER_LABEL, - }, - when: ContextKeyExpr.and(ExplorerRootContext, ContextKeyExpr.or(EnterMultiRootWorkspaceSupportContext, WorkbenchStateContext.isEqualTo('workspace'))) -}); +// Membrane: Programs are only added to the workspace by creating programs +// MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { +// group: '2_workspace', +// order: 10, +// command: { +// id: ADD_ROOT_FOLDER_COMMAND_ID, +// title: ADD_ROOT_FOLDER_LABEL +// }, +// when: ContextKeyExpr.and(ExplorerRootContext, ContextKeyExpr.or(EnterMultiRootWorkspaceSupportContext, WorkbenchStateContext.isEqualTo('workspace'))) +// }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: '2_workspace', + // Membrane: update position to delete program item + group: '7_modification', order: 30, command: { id: REMOVE_ROOT_FOLDER_COMMAND_ID, diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index c8fc8aa2dd207e..91cede03fed6bf 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -5,6 +5,7 @@ import * as nls from '../../../../nls.js'; import { URI } from '../../../../base/common/uri.js'; +import { hasKey } from '../../../../base/common/types.js'; import { EditorResourceAccessor, IEditorCommandsContext, SideBySideEditor, IEditorIdentifier, SaveReason, EditorsOrder, EditorInputCapabilities } from '../../../common/editor.js'; import { SideBySideEditorInput } from '../../../common/editor/sideBySideEditorInput.js'; import { IWindowOpenable, IOpenWindowOptions, isWorkspaceToOpen, IOpenEmptyWindowOptions } from '../../../../platform/window/common/window.js'; @@ -23,7 +24,7 @@ import { KeyMod, KeyCode, KeyChord } from '../../../../base/common/keyCodes.js'; import { isWeb, isWindows } from '../../../../base/common/platform.js'; import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection, IExplorerService } from './files.js'; -import { IWorkspaceEditingService } from '../../../services/workspaces/common/workspaceEditing.js'; +// import { IWorkspaceEditingService } from '../../../services/workspaces/common/workspaceEditing.js'; import { resolveCommandsContext } from '../../../browser/parts/editor/editorCommandsContext.js'; import { Schemas } from '../../../../base/common/network.js'; import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; @@ -37,7 +38,7 @@ import { IEnvironmentService } from '../../../../platform/environment/common/env import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; -import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +// import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { isCancellationError } from '../../../../base/common/errors.js'; import { IAction, toAction } from '../../../../base/common/actions.js'; import { EditorOpenSource, EditorResolution } from '../../../../platform/editor/common/editor.js'; @@ -48,7 +49,7 @@ import { ViewContainerLocation } from '../../../common/views.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { OPEN_TO_SIDE_COMMAND_ID, COMPARE_WITH_SAVED_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, COMPARE_SELECTED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, COPY_PATH_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_WITH_EXPLORER_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_IN_GROUP_COMMAND_ID, SAVE_FILES_COMMAND_ID, REVERT_FILE_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, PREVIOUS_COMPRESSED_FOLDER, NEXT_COMPRESSED_FOLDER, FIRST_COMPRESSED_FOLDER, LAST_COMPRESSED_FOLDER, NEW_UNTITLED_FILE_COMMAND_ID, NEW_UNTITLED_FILE_LABEL, NEW_FILE_COMMAND_ID } from './fileConstants.js'; import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { RemoveRootFolderAction } from '../../../browser/actions/workspaceActions.js'; +// import { RemoveRootFolderAction } from '../../../browser/actions/workspaceActions.js'; import { OpenEditorsView } from './views/openEditorsView.js'; import { ExplorerView } from './views/explorerView.js'; import { IListService } from '../../../../platform/list/browser/listService.js'; @@ -562,7 +563,13 @@ CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({ id: REMOVE_ROOT_FOLDER_COMMAND_ID, - handler: (accessor, resource: URI | object) => { + handler: async (accessor, resource: URI | object) => { + // MEMBRANE: We override this functionality in vscode-extension + const commandService = accessor.get(ICommandService); + const name = hasKey(resource, { path: true }) ? resource.path.slice(1) : undefined; + commandService.executeCommand('membrane.deleteProgram', { name }); + /* + const workspaceEditingService = accessor.get(IWorkspaceEditingService); const contextService = accessor.get(IWorkspaceContextService); const uriIdentityService = accessor.get(IUriIdentityService); const workspace = contextService.getWorkspace(); @@ -578,6 +585,7 @@ CommandsRegistry.registerCommand({ const workspaceEditingService = accessor.get(IWorkspaceEditingService); return workspaceEditingService.removeFolders(resources); + */ } }); diff --git a/src/vs/workbench/contrib/files/browser/fileConstants.ts b/src/vs/workbench/contrib/files/browser/fileConstants.ts index 9a591704ee7bb6..a438893306529b 100644 --- a/src/vs/workbench/contrib/files/browser/fileConstants.ts +++ b/src/vs/workbench/contrib/files/browser/fileConstants.ts @@ -39,7 +39,7 @@ export const OpenEditorsSelectedFileOrUntitledContext = new RawContextKey('resourceSelectedForCompare', false); export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; -export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace"); +export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace', "Delete Program"); export const PREVIOUS_COMPRESSED_FOLDER = 'previousCompressedFolder'; export const NEXT_COMPRESSED_FOLDER = 'nextCompressedFolder'; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index bef572b125a0c9..0e68e1b3b2ce75 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -246,7 +246,8 @@ export class ExplorerView extends ViewPane implements IExplorerView { } get name(): string { - return this.labelService.getWorkspaceLabel(this.contextService.getWorkspace()); + // MEMBRANE: We only ever use one (virtual) workspace so here we show a more helpful title for the file explorer. + return 'PROGRAM FILES'; } override get title(): string { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 8c40f80dbe2e25..b2d31b711ee0de 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -284,7 +284,9 @@ export class SettingsTargetsWidget extends Widget { this.resetLabels(); this.update(); - this.settingsSwitcherBar.push([this.userLocalSettings, this.userRemoteSettings, this.workspaceSettings, this.folderSettingsAction]); + // MEMBRANE: Only show User settings tabs + this.settingsSwitcherBar.push([this.userLocalSettings, this.userRemoteSettings]); + // Was: this.settingsSwitcherBar.push([this.userLocalSettings, this.userRemoteSettings, this.workspaceSettings, this.folderSettingsAction]); } get settingsTarget(): SettingsTarget | null { diff --git a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts index 051836b9bcbf1c..70f51486f8f374 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts @@ -14,7 +14,7 @@ import { KeyMod } from '../../../../base/common/keyCodes.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { inQuickPickContext, getQuickNavigateHandler } from '../../../browser/quickaccess.js'; import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; +// import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; //#region Quick Access Proviers @@ -97,15 +97,16 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { order: 1 }); -MenuRegistry.appendMenuItem(MenuId.EditorContext, { - group: 'z_commands', - when: EditorContextKeys.editorSimpleInput.toNegated(), - command: { - id: ShowAllCommandsAction.ID, - title: localize('commandPalette', "Command Palette..."), - }, - order: 1 -}); +// MEMBRANE: Remove command palette from all menus +// MenuRegistry.appendMenuItem(MenuId.EditorContext, { +// group: 'z_commands', +// when: EditorContextKeys.editorSimpleInput.toNegated(), +// command: { +// id: ShowAllCommandsAction.ID, +// title: localize('commandPalette', "Command Palette..."), +// }, +// order: 1 +// }); //#endregion @@ -113,7 +114,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { //#region Workbench actions and commands registerAction2(ClearCommandHistoryAction); -registerAction2(ShowAllCommandsAction); +// MEMBRANE: Disable command palette +// registerAction2(ShowAllCommandsAction); registerAction2(OpenViewPickerAction); registerAction2(QuickAccessViewPickerAction); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 40b580ef0f0fcd..4b066fb8f212e0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -3,137 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getFontSnippets } from '../../../../base/browser/fonts.js'; -import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; -import { Schemas } from '../../../../base/common/network.js'; -import { URI } from '../../../../base/common/uri.js'; -import * as nls from '../../../../nls.js'; -import { Extensions as DragAndDropExtensions, IDragAndDropContributionRegistry, IDraggedResourceEditorInput } from '../../../../platform/dnd/browser/dnd.js'; -import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { ITerminalProfileService } from '../common/terminal.js'; +import { TerminalService } from './terminalService.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { Registry } from '../../../../platform/registry/common/platform.js'; +import { ITerminalEditorService, ITerminalGroupService, ITerminalInstanceService, ITerminalService } from './terminal.js'; import { ITerminalLogService } from '../../../../platform/terminal/common/terminal.js'; -import { TerminalLogService } from '../../../../platform/terminal/common/terminalLogService.js'; -import { registerTerminalPlatformConfiguration } from '../../../../platform/terminal/common/terminalPlatformConfiguration.js'; -import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; -import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; -import { WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; -import { EditorExtensions, IEditorFactoryRegistry } from '../../../common/editor.js'; -import { IViewContainersRegistry, IViewsRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation } from '../../../common/views.js'; -import { ITerminalProfileService, TERMINAL_VIEW_ID, TerminalCommandId } from '../common/terminal.js'; -import { TerminalEditingService } from './terminalEditingService.js'; -import { registerColors } from '../common/terminalColorRegistry.js'; -import { registerTerminalConfiguration } from '../common/terminalConfiguration.js'; -import { terminalStrings } from '../common/terminalStrings.js'; -import './media/terminal.css'; -import './media/terminalVoice.css'; -import './media/widgets.css'; -import './media/xterm.css'; -import { RemoteTerminalBackendContribution } from './remoteTerminalBackend.js'; -import { ITerminalConfigurationService, ITerminalEditingService, ITerminalEditorService, ITerminalGroupService, ITerminalInstanceService, ITerminalService, TerminalDataTransfers, terminalEditorId } from './terminal.js'; -import { registerTerminalActions } from './terminalActions.js'; -import { setupTerminalCommands } from './terminalCommands.js'; -import { TerminalConfigurationService } from './terminalConfigurationService.js'; -import { TerminalEditor } from './terminalEditor.js'; -import { TerminalEditorInput } from './terminalEditorInput.js'; -import { TerminalInputSerializer } from './terminalEditorSerializer.js'; +import { TerminalInstanceService } from './terminalInstanceService.js'; import { TerminalEditorService } from './terminalEditorService.js'; import { TerminalGroupService } from './terminalGroupService.js'; -import { terminalViewIcon } from './terminalIcons.js'; -import { TerminalInstanceService } from './terminalInstanceService.js'; -import { TerminalMainContribution } from './terminalMainContribution.js'; -import { setupTerminalMenus } from './terminalMenus.js'; import { TerminalProfileService } from './terminalProfileService.js'; -import { TerminalService } from './terminalService.js'; -import { TerminalTelemetryContribution } from './terminalTelemetry.js'; -import { TerminalViewPane } from './terminalView.js'; +import { TerminalLogService } from '../../../../platform/terminal/common/terminalLogService.js'; // Register services registerSingleton(ITerminalLogService, TerminalLogService, InstantiationType.Delayed); -registerSingleton(ITerminalConfigurationService, TerminalConfigurationService, InstantiationType.Delayed); registerSingleton(ITerminalService, TerminalService, InstantiationType.Delayed); registerSingleton(ITerminalEditorService, TerminalEditorService, InstantiationType.Delayed); -registerSingleton(ITerminalEditingService, TerminalEditingService, InstantiationType.Delayed); registerSingleton(ITerminalGroupService, TerminalGroupService, InstantiationType.Delayed); registerSingleton(ITerminalInstanceService, TerminalInstanceService, InstantiationType.Delayed); registerSingleton(ITerminalProfileService, TerminalProfileService, InstantiationType.Delayed); - -// Register workbench contributions -// This contribution blocks startup as it's critical to enable the web embedder window.createTerminal API -registerWorkbenchContribution2(TerminalMainContribution.ID, TerminalMainContribution, WorkbenchPhase.BlockStartup); -registerWorkbenchContribution2(RemoteTerminalBackendContribution.ID, RemoteTerminalBackendContribution, WorkbenchPhase.AfterRestored); -registerWorkbenchContribution2(TerminalTelemetryContribution.ID, TerminalTelemetryContribution, WorkbenchPhase.AfterRestored); - -// Register configurations -registerTerminalPlatformConfiguration(); -registerTerminalConfiguration(getFontSnippets); - -// Register editor/dnd contributions -Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer(TerminalEditorInput.ID, TerminalInputSerializer); -Registry.as(EditorExtensions.EditorPane).registerEditorPane( - EditorPaneDescriptor.create( - TerminalEditor, - terminalEditorId, - terminalStrings.terminal - ), - [ - new SyncDescriptor(TerminalEditorInput) - ] -); -Registry.as(DragAndDropExtensions.DragAndDropContribution).register({ - dataFormatKey: TerminalDataTransfers.Terminals, - getEditorInputs(data) { - const editors: IDraggedResourceEditorInput[] = []; - try { - const terminalEditors: string[] = JSON.parse(data); - for (const terminalEditor of terminalEditors) { - editors.push({ resource: URI.parse(terminalEditor) }); - } - } catch (error) { - // Invalid transfer - } - return editors; - }, - setData(resources, event) { - const terminalResources = resources.filter(({ resource }) => resource.scheme === Schemas.vscodeTerminal); - if (terminalResources.length) { - event.dataTransfer?.setData(TerminalDataTransfers.Terminals, JSON.stringify(terminalResources.map(({ resource }) => resource.toString()))); - } - } -}); - -// Register views -const VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ - id: TERMINAL_VIEW_ID, - title: nls.localize2('terminal', "Terminal"), - icon: terminalViewIcon, - ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [TERMINAL_VIEW_ID, { mergeViewWithContainerWhenSingleView: true }]), - storageId: TERMINAL_VIEW_ID, - hideIfEmpty: true, - order: 3, -}, ViewContainerLocation.Panel, { doNotRegisterOpenCommand: true, isDefault: true }); -Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ - id: TERMINAL_VIEW_ID, - name: nls.localize2('terminal', "Terminal"), - containerIcon: terminalViewIcon, - canToggleVisibility: true, - canMoveView: true, - ctorDescriptor: new SyncDescriptor(TerminalViewPane), - openCommandActionDescriptor: { - id: TerminalCommandId.Toggle, - mnemonicTitle: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal"), - keybindings: { - primary: KeyMod.CtrlCmd | KeyCode.Backquote, - mac: { primary: KeyMod.WinCtrl | KeyCode.Backquote } - }, - order: 3 - } -}], VIEW_CONTAINER); - -registerTerminalActions(); - -setupTerminalCommands(); - -setupTerminalMenus(); - -registerColors(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index acb65754e1ab9e..13e75977d15734 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -7,20 +7,13 @@ import { ITerminalInstance, ITerminalInstanceService } from './terminal.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { IShellLaunchConfig, ITerminalBackend, ITerminalBackendRegistry, ITerminalProfile, TerminalExtensions, TerminalLocation } from '../../../../platform/terminal/common/terminal.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { TerminalInstance } from './terminalInstance.js'; -import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { TerminalContextKeys } from '../common/terminalContextKey.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; -import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; -import { promiseWithResolvers } from '../../../../base/common/async.js'; -import { hasKey } from '../../../../base/common/types.js'; export class TerminalInstanceService extends Disposable implements ITerminalInstanceService { declare _serviceBrand: undefined; - private _terminalShellTypeContextKey: IContextKey; + // private _configHelper: TerminalConfigHelper; private _backendRegistration = new Map; resolve: () => void }>(); private readonly _onDidCreateInstance = this._register(new Emitter()); @@ -30,67 +23,27 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst get onDidRegisterBackend(): Event { return this._onDidRegisterBackend.event; } constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + // @IInstantiationService private readonly _instantiationService: IInstantiationService, + // @IContextKeyService private readonly _contextKeyService: IContextKeyService, + // @IWorkbenchEnvironmentService readonly _environmentService: IWorkbenchEnvironmentService, + ...args: unknown[] ) { super(); - this._terminalShellTypeContextKey = TerminalContextKeys.shellType.bindTo(this._contextKeyService); - - for (const remoteAuthority of [undefined, environmentService.remoteAuthority]) { - const { promise, resolve } = promiseWithResolvers(); - this._backendRegistration.set(remoteAuthority, { promise, resolve }); - } } createInstance(profile: ITerminalProfile, target: TerminalLocation): ITerminalInstance; createInstance(shellLaunchConfig: IShellLaunchConfig, target: TerminalLocation): ITerminalInstance; createInstance(config: IShellLaunchConfig | ITerminalProfile, target: TerminalLocation): ITerminalInstance { - const shellLaunchConfig = this.convertProfileToShellLaunchConfig(config); - const instance = this._instantiationService.createInstance(TerminalInstance, this._terminalShellTypeContextKey, shellLaunchConfig); - instance.target = target; - this._onDidCreateInstance.fire(instance); - return instance; + throw new Error('Unimplemented'); } convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { - // Profile was provided - if (shellLaunchConfigOrProfile && hasKey(shellLaunchConfigOrProfile, { profileName: true })) { - const profile = shellLaunchConfigOrProfile; - if (!profile.path) { - return shellLaunchConfigOrProfile; - } - return { - executable: profile.path, - args: profile.args, - env: profile.env, - icon: profile.icon, - color: profile.color, - name: profile.overrideName ? profile.profileName : undefined, - cwd - }; - } - - // A shell launch config was provided - if (shellLaunchConfigOrProfile) { - if (cwd) { - shellLaunchConfigOrProfile.cwd = cwd; - } - return shellLaunchConfigOrProfile; - } - // Return empty shell launch config return {}; } async getBackend(remoteAuthority?: string): Promise { - let backend = Registry.as(TerminalExtensions.Backend).getTerminalBackend(remoteAuthority); - if (!backend) { - // Ensure backend is initialized and try again - await this._backendRegistration.get(remoteAuthority)?.promise; - backend = Registry.as(TerminalExtensions.Backend).getTerminalBackend(remoteAuthority); - } - return backend; + return undefined; } getRegisteredBackends(): IterableIterator { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts index 0cbef05b9221b0..c3d117846764a5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts @@ -3,273 +3,84 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from '../../../../base/common/arrays.js'; -import * as objects from '../../../../base/common/objects.js'; -import { AutoOpenBarrier } from '../../../../base/common/async.js'; import { throttle } from '../../../../base/common/decorators.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; -import { Disposable, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { isMacintosh, isWeb, isWindows, OperatingSystem, OS } from '../../../../base/common/platform.js'; -import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { ITerminalProfile, IExtensionTerminalProfile, TerminalSettingPrefix, TerminalSettingId, ITerminalProfileObject, IShellLaunchConfig, ITerminalExecutable } from '../../../../platform/terminal/common/terminal.js'; -import { registerTerminalDefaultProfileConfiguration } from '../../../../platform/terminal/common/terminalPlatformConfiguration.js'; -import { terminalIconsEqual, terminalProfileArgsMatch } from '../../../../platform/terminal/common/terminalProfiles.js'; -import { ITerminalInstanceService } from './terminal.js'; -import { refreshTerminalActions } from './terminalActions.js'; +import { Event } from '../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; +import { OperatingSystem } from '../../../../base/common/platform.js'; +import { ITerminalProfile, IExtensionTerminalProfile, IShellLaunchConfig } from '../../../../platform/terminal/common/terminal.js'; import { IRegisterContributedProfileArgs, ITerminalProfileProvider, ITerminalProfileService } from '../common/terminal.js'; -import { TerminalContextKeys } from '../common/terminalContextKey.js'; -import { ITerminalContributionService } from '../common/terminalExtensionPoints.js'; -import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; -import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; -import { hasKey, isString } from '../../../../base/common/types.js'; /* * Links TerminalService with TerminalProfileResolverService * and keeps the available terminal profiles updated + * + * NOTE: This is a web stub that returns safe defaults instead of throwing errors */ export class TerminalProfileService extends Disposable implements ITerminalProfileService { declare _serviceBrand: undefined; - private _webExtensionContributedProfileContextKey: IContextKey; - private _profilesReadyBarrier: AutoOpenBarrier | undefined; - private _profilesReadyPromise: Promise; - private _availableProfiles: ITerminalProfile[] | undefined; - private _automationProfile: unknown; - private _contributedProfiles: IExtensionTerminalProfile[] = []; - private _defaultProfileName?: string; - private _platformConfigJustRefreshed = false; - private readonly _refreshTerminalActionsDisposable = this._register(new MutableDisposable()); - private readonly _profileProviders: Map> = new Map(); + get onDidChangeAvailableProfiles(): Event { return Event.None; } - private readonly _onDidChangeAvailableProfiles = this._register(new Emitter()); - get onDidChangeAvailableProfiles(): Event { return this._onDidChangeAvailableProfiles.event; } + get profilesReady(): Promise { + return Promise.resolve(); + } - get profilesReady(): Promise { return this._profilesReadyPromise; } get availableProfiles(): ITerminalProfile[] { - if (!this._platformConfigJustRefreshed) { - this.refreshAvailableProfiles(); - } - return this._availableProfiles || []; + return []; } + get contributedProfiles(): IExtensionTerminalProfile[] { - const userConfiguredProfileNames = this._availableProfiles?.map(p => p.profileName) || []; - // Allow a user defined profile to override an extension contributed profile with the same name - return this._contributedProfiles?.filter(p => !userConfiguredProfileNames.includes(p.title)) || []; + return []; } constructor( - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IConfigurationService private readonly _configurationService: IConfigurationService, - @ITerminalContributionService private readonly _terminalContributionService: ITerminalContributionService, - @IExtensionService private readonly _extensionService: IExtensionService, - @IRemoteAgentService private _remoteAgentService: IRemoteAgentService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService + // @IContextKeyService private readonly _contextKeyService: IContextKeyService, + // @IConfigurationService private readonly _configurationService: IConfigurationService, + // @ITerminalContributionService private readonly _terminalContributionService: ITerminalContributionService, + // @IExtensionService private readonly _extensionService: IExtensionService, + // @IRemoteAgentService private _remoteAgentService: IRemoteAgentService, + // @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + // @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService + ...args: unknown[] ) { super(); - - // in web, we don't want to show the dropdown unless there's a web extension - // that contributes a profile - this._register(this._extensionService.onDidChangeExtensions(() => this.refreshAvailableProfiles())); - - this._webExtensionContributedProfileContextKey = TerminalContextKeys.webExtensionContributedProfile.bindTo(this._contextKeyService); - this._updateWebContextKey(); - this._profilesReadyPromise = this._remoteAgentService.getEnvironment() - .then(() => { - // Wait up to 20 seconds for profiles to be ready so it's assured that we know the actual - // default terminal before launching the first terminal. This isn't expected to ever take - // this long. - this._profilesReadyBarrier = new AutoOpenBarrier(20000); - return this._profilesReadyBarrier.wait().then(() => { }); - }); - this.refreshAvailableProfiles(); - this._setupConfigListener(); - } - - private async _setupConfigListener(): Promise { - const platformKey = await this.getPlatformKey(); - - this._register(this._configurationService.onDidChangeConfiguration(async e => { - if (e.affectsConfiguration(TerminalSettingPrefix.AutomationProfile + platformKey) || - e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + platformKey) || - e.affectsConfiguration(TerminalSettingPrefix.Profiles + platformKey) || - e.affectsConfiguration(TerminalSettingId.UseWslProfiles)) { - if (e.source !== ConfigurationTarget.DEFAULT) { - // when _refreshPlatformConfig is called within refreshAvailableProfiles - // on did change configuration is fired. this can lead to an infinite recursion - this.refreshAvailableProfiles(); - this._platformConfigJustRefreshed = false; - } else { - this._platformConfigJustRefreshed = true; - } - } - })); } getDefaultProfileName(): string | undefined { - return this._defaultProfileName; + return undefined; } getDefaultProfile(os?: OperatingSystem): ITerminalProfile | undefined { - let defaultProfileName: string | undefined; - if (os) { - defaultProfileName = this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${this._getOsKey(os)}`); - if (!defaultProfileName || !isString(defaultProfileName)) { - return undefined; - } - } else { - defaultProfileName = this._defaultProfileName; - } - if (!defaultProfileName) { - return undefined; - } - - // IMPORTANT: Only allow the default profile name to find non-auto detected profiles as - // to avoid unsafe path profiles being picked up. - return this.availableProfiles.find(e => e.profileName === defaultProfileName && !e.isAutoDetected); - } - - private _getOsKey(os: OperatingSystem): string { - switch (os) { - case OperatingSystem.Linux: return 'linux'; - case OperatingSystem.Macintosh: return 'osx'; - case OperatingSystem.Windows: return 'windows'; - } + return undefined; } - @throttle(2000) refreshAvailableProfiles(): void { - this._refreshAvailableProfilesNow(); + // No-op } protected async _refreshAvailableProfilesNow(): Promise { - // Profiles - const profiles = await this._detectProfiles(true); - const profilesChanged = !arrays.equals(profiles, this._availableProfiles, profilesEqual); - // Contributed profiles - const contributedProfilesChanged = await this._updateContributedProfiles(); - // Automation profiles - const platform = await this.getPlatformKey(); - const automationProfile = this._configurationService.getValue(`${TerminalSettingPrefix.AutomationProfile}${platform}`); - const automationProfileChanged = !objects.equals(automationProfile, this._automationProfile); - // Update - if (profilesChanged || contributedProfilesChanged || automationProfileChanged) { - this._availableProfiles = profiles; - this._automationProfile = automationProfile; - this._onDidChangeAvailableProfiles.fire(this._availableProfiles); - this._profilesReadyBarrier!.open(); - this._updateWebContextKey(); - await this._refreshPlatformConfig(this._availableProfiles); - } - } - - private async _updateContributedProfiles(): Promise { - const platformKey = await this.getPlatformKey(); - const excludedContributedProfiles: string[] = []; - const configProfiles: { [key: string]: ITerminalExecutable | null | undefined } = this._configurationService.getValue(TerminalSettingPrefix.Profiles + platformKey); - for (const [profileName, value] of Object.entries(configProfiles)) { - if (value === null) { - excludedContributedProfiles.push(profileName); - } - } - const filteredContributedProfiles = Array.from(this._terminalContributionService.terminalProfiles.filter(p => !excludedContributedProfiles.includes(p.title))); - const contributedProfilesChanged = !arrays.equals(filteredContributedProfiles, this._contributedProfiles, contributedProfilesEqual); - this._contributedProfiles = filteredContributedProfiles; - return contributedProfilesChanged; + // No-op } getContributedProfileProvider(extensionIdentifier: string, id: string): ITerminalProfileProvider | undefined { - const extMap = this._profileProviders.get(extensionIdentifier); - return extMap?.get(id); - } - - private async _detectProfiles(includeDetectedProfiles?: boolean): Promise { - const primaryBackend = await this._terminalInstanceService.getBackend(this._environmentService.remoteAuthority); - if (!primaryBackend) { - return this._availableProfiles || []; - } - 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); - } - - private _updateWebContextKey(): void { - this._webExtensionContributedProfileContextKey.set(isWeb && this._contributedProfiles.length > 0); - } - - private async _refreshPlatformConfig(profiles: ITerminalProfile[]) { - const env = await this._remoteAgentService.getEnvironment(); - registerTerminalDefaultProfileConfiguration({ os: env?.os || OS, profiles }, this._contributedProfiles); - this._refreshTerminalActionsDisposable.value = refreshTerminalActions(profiles); + return undefined; } 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'); + return 'web'; } registerTerminalProfileProvider(extensionIdentifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable { - let extMap = this._profileProviders.get(extensionIdentifier); - if (!extMap) { - extMap = new Map(); - this._profileProviders.set(extensionIdentifier, extMap); - } - extMap.set(id, profileProvider); - return toDisposable(() => this._profileProviders.delete(id)); + return { dispose: () => { } }; } 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: args.extensionIdentifier, - icon: args.options.icon, - id: args.id, - title: args.title, - color: args.options.color - }; - - (profilesConfig as { [key: string]: ITerminalProfileObject })[args.title] = newProfile; - } - await this._configurationService.updateValue(`${TerminalSettingPrefix.Profiles}${platformKey}`, profilesConfig, ConfigurationTarget.USER); - return; + // No-op } async getContributedDefaultProfile(shellLaunchConfig: IShellLaunchConfig): Promise { - // 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 && !hasKey(shellLaunchConfig, { executable: true })) { - const key = await this.getPlatformKey(); - const defaultProfileName = this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${key}`); - const contributedDefaultProfile = this.contributedProfiles.find(p => p.title === defaultProfileName); - return contributedDefaultProfile; - } return undefined; } -} - -function profilesEqual(one: ITerminalProfile, other: ITerminalProfile) { - return one.profileName === other.profileName && - terminalProfileArgsMatch(one.args, other.args) && - one.color === other.color && - terminalIconsEqual(one.icon, other.icon) && - one.isAutoDetected === other.isAutoDetected && - one.isDefault === other.isDefault && - one.overrideName === other.overrideName && - one.path === other.path; -} -function contributedProfilesEqual(one: IExtensionTerminalProfile, other: IExtensionTerminalProfile) { - return one.extensionIdentifier === other.extensionIdentifier && - one.color === other.color && - one.icon === other.icon && - one.id === other.id && - one.title === other.title; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 44ad6e6d2af085..80cc2167e6bcef 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -3,1341 +3,274 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as domStylesheets from '../../../../base/browser/domStylesheets.js'; -import * as cssValue from '../../../../base/browser/cssValue.js'; -import { DeferredPromise, timeout, type MaybePromise } from '../../../../base/common/async.js'; -import { debounce, memoize } from '../../../../base/common/decorators.js'; -import { DynamicListEventMultiplexer, Emitter, Event, IDynamicListEventMultiplexer } from '../../../../base/common/event.js'; -import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { Schemas } from '../../../../base/common/network.js'; -import { isMacintosh, isWeb } from '../../../../base/common/platform.js'; +import { memoize } from '../../../../base/common/decorators.js'; +import { Event, Emitter, IDynamicListEventMultiplexer, DynamicListEventMultiplexer } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { IKeyMods } from '../../../../platform/quickinput/common/quickInput.js'; -import * as nls from '../../../../nls.js'; -import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { INotificationService } from '../../../../platform/notification/common/notification.js'; -import { ICreateContributedTerminalProfileOptions, IExtensionTerminalProfile, IPtyHostAttachTarget, IRawTerminalInstanceLayoutInfo, IRawTerminalTabLayoutInfo, IShellLaunchConfig, ITerminalBackend, ITerminalLaunchError, ITerminalLogService, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalExitReason, TerminalLocation, TitleEventSource } from '../../../../platform/terminal/common/terminal.js'; -import { formatMessageForTerminal } from '../../../../platform/terminal/common/terminalStrings.js'; -import { iconForeground } from '../../../../platform/theme/common/colorRegistry.js'; -import { getIconRegistry } from '../../../../platform/theme/common/iconRegistry.js'; -import { isDark } from '../../../../platform/theme/common/theme.js'; -import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; -import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { VirtualWorkspaceContext } from '../../../common/contextkeys.js'; -import { ICreateTerminalOptions, IDetachedTerminalInstance, IDetachedXTermOptions, IRequestAddInstanceToGroupEvent, ITerminalConfigurationService, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, TerminalConnectionState, TerminalEditorLocation } from './terminal.js'; -import { getCwdForSplit } from './terminalActions.js'; -import { TerminalEditorInput } from './terminalEditorInput.js'; -import { getColorStyleContent, getUriClasses } from './terminalIcon.js'; -import { TerminalProfileQuickpick } from './terminalProfileQuickpick.js'; -import { getInstanceFromResource, getTerminalUri, parseTerminalUri } from './terminalUri.js'; -import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy, ITerminalProfileService } from '../common/terminal.js'; -import { TerminalContextKeys } from '../common/terminalContextKey.js'; -import { columnToEditorGroup } from '../../../services/editor/common/editorGroupColumn.js'; -import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; -import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, AUX_WINDOW_GROUP, AUX_WINDOW_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from '../../../services/editor/common/editorService.js'; -import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; -import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { ILifecycleService, ShutdownReason, StartupKind, WillShutdownEvent } from '../../../services/lifecycle/common/lifecycle.js'; -import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; -import { XtermTerminal } from './xterm/xtermTerminal.js'; -import { TerminalInstance } from './terminalInstance.js'; -import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { TerminalCapabilityStore } from '../../../../platform/terminal/common/capabilities/terminalCapabilityStore.js'; -import { ITimerService } from '../../../services/timer/browser/timerService.js'; -import { mark } from '../../../../base/common/performance.js'; -import { DetachedTerminal } from './detachedTerminal.js'; +import { ICreateContributedTerminalProfileOptions, ITerminalBackend, ITerminalLaunchError, TerminalLocation } from '../../../../platform/terminal/common/terminal.js'; +import { IEditableData } from '../../../common/views.js'; +import { ICreateTerminalOptions, IDetachedTerminalInstance, IDetachedXTermOptions, ITerminalGroup, ITerminalInstance, ITerminalInstanceHost, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, TerminalConnectionState } from './terminal.js'; +import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy } from '../common/terminal.js'; +import { ACTIVE_GROUP_TYPE, AUX_WINDOW_GROUP_TYPE, SIDE_GROUP_TYPE } from '../../../services/editor/common/editorService.js'; import { ITerminalCapabilityImplMap, TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js'; -import { createInstanceCapabilityEventMultiplexer } from './terminalEvents.js'; -import { isAuxiliaryWindow, mainWindow } from '../../../../base/browser/window.js'; import { GroupIdentifier } from '../../../common/editor.js'; -import { getActiveWindow } from '../../../../base/browser/dom.js'; -import { hasKey, isString } from '../../../../base/common/types.js'; - -interface IBackgroundTerminal { - instance: ITerminalInstance; - terminalLocationOptions?: ITerminalLocationOptions; -} export class TerminalService extends Disposable implements ITerminalService { - declare _serviceBrand: undefined; - - private _hostActiveTerminals: Map = new Map(); - - private _detachedXterms = new Set(); - private _terminalEditorActive: IContextKey; - private readonly _terminalShellTypeContextKey: IContextKey; - - private _isShuttingDown: boolean = false; - private _backgroundedTerminalInstances: IBackgroundTerminal[] = []; - private _backgroundedTerminalDisposables: Map = new Map(); - private _processSupportContextKey: IContextKey; + readonly _serviceBrand: undefined; - private _primaryBackend?: ITerminalBackend; - private _terminalHasBeenCreated: IContextKey; - private _terminalCountContextKey: IContextKey; - private _nativeDelegate?: ITerminalServiceNativeDelegate; - private _shutdownWindowCount?: number; + get isProcessSupportRegistered(): boolean { return false; } - get isProcessSupportRegistered(): boolean { return !!this._processSupportContextKey.get(); } + get connectionState(): TerminalConnectionState { return TerminalConnectionState.Connected; } - private _connectionState: TerminalConnectionState = TerminalConnectionState.Connecting; - get connectionState(): TerminalConnectionState { return this._connectionState; } + // Never resolve + get whenConnected(): Promise { return new Promise(() => { }); } - private readonly _whenConnected = new DeferredPromise(); - get whenConnected(): Promise { return this._whenConnected.p; } - - private _restoredGroupCount: number = 0; - get restoredGroupCount(): number { return this._restoredGroupCount; } + get restoredGroupCount(): number { return 0; } get instances(): ITerminalInstance[] { - return this._terminalGroupService.instances.concat(this._terminalEditorService.instances).concat(this._backgroundedTerminalInstances.map(bg => bg.instance)); + return []; } - /** Gets all non-background terminals. */ get foregroundInstances(): ITerminalInstance[] { - return this._terminalGroupService.instances.concat(this._terminalEditorService.instances); + return []; } get detachedInstances(): Iterable { - return this._detachedXterms; + return []; } - private _reconnectedTerminalGroups: Promise | undefined; - - private _reconnectedTerminals: Map = new Map(); - getReconnectedTerminals(reconnectionOwner: string): ITerminalInstance[] | undefined { - return this._reconnectedTerminals.get(reconnectionOwner); + getReconnectedTerminals(_reconnectionOwner: string): ITerminalInstance[] | undefined { + return undefined; } - private _activeInstance: ITerminalInstance | undefined; + get defaultLocation(): TerminalLocation { return TerminalLocation.Panel; } + get activeInstance(): ITerminalInstance | undefined { - // Check if either an editor or panel terminal has focus and return that, regardless of the - // value of _activeInstance. This avoids terminals created in the panel for example stealing - // the active status even when it's not focused. - for (const activeHostTerminal of this._hostActiveTerminals.values()) { - if (activeHostTerminal?.hasFocus) { - return activeHostTerminal; - } - } - // Fallback to the last recorded active terminal if neither have focus - return this._activeInstance; + return undefined; } - private readonly _onDidCreateInstance = this._register(new Emitter()); - get onDidCreateInstance(): Event { return this._onDidCreateInstance.event; } - private readonly _onDidChangeInstanceDimensions = this._register(new Emitter()); - get onDidChangeInstanceDimensions(): Event { return this._onDidChangeInstanceDimensions.event; } - private readonly _onDidRegisterProcessSupport = this._register(new Emitter()); - get onDidRegisterProcessSupport(): Event { return this._onDidRegisterProcessSupport.event; } - private readonly _onDidChangeConnectionState = this._register(new Emitter()); - get onDidChangeConnectionState(): Event { return this._onDidChangeConnectionState.event; } - private readonly _onDidRequestStartExtensionTerminal = this._register(new Emitter()); - get onDidRequestStartExtensionTerminal(): Event { return this._onDidRequestStartExtensionTerminal.event; } + // Return Event.None for all events - they will never fire but won't throw errors + get onDidCreateInstance(): Event { return Event.None; } + get onDidChangeInstanceDimensions(): Event { return Event.None; } + get onDidRegisterProcessSupport(): Event { return Event.None; } + get onDidChangeConnectionState(): Event { return Event.None; } + get onDidRequestStartExtensionTerminal(): Event { return Event.None; } // ITerminalInstanceHost events - private readonly _onDidDisposeInstance = this._register(new Emitter()); - get onDidDisposeInstance(): Event { return this._onDidDisposeInstance.event; } - private readonly _onDidFocusInstance = this._register(new Emitter()); - get onDidFocusInstance(): Event { return this._onDidFocusInstance.event; } - private readonly _onDidChangeActiveInstance = this._register(new Emitter()); - get onDidChangeActiveInstance(): Event { return this._onDidChangeActiveInstance.event; } - private readonly _onDidChangeInstances = this._register(new Emitter()); - get onDidChangeInstances(): Event { return this._onDidChangeInstances.event; } - private readonly _onDidChangeInstanceCapability = this._register(new Emitter()); - get onDidChangeInstanceCapability(): Event { return this._onDidChangeInstanceCapability.event; } + get onDidDisposeInstance(): Event { return Event.None; } + get onDidFocusInstance(): Event { return Event.None; } + get onDidChangeActiveInstance(): Event { return Event.None; } + get onDidChangeInstances(): Event { return Event.None; } + get onDidChangeInstanceCapability(): Event { return Event.None; } // Terminal view events - private readonly _onDidChangeActiveGroup = this._register(new Emitter()); - get onDidChangeActiveGroup(): Event { return this._onDidChangeActiveGroup.event; } + get onDidChangeActiveGroup(): Event { return Event.None; } - // Lazily initialized events that fire when the specified event fires on _any_ terminal - // TODO: Batch events + // Multiplexed events - return events that never fire @memoize get onAnyInstanceData() { return this._register(this.createOnInstanceEvent(instance => Event.map(instance.onData, data => ({ instance, data })))).event; } @memoize get onAnyInstanceDataInput() { return this._register(this.createOnInstanceEvent(e => Event.map(e.onDidInputData, () => e, e.store))).event; } @memoize get onAnyInstanceIconChange() { return this._register(this.createOnInstanceEvent(e => e.onIconChanged)).event; } @memoize get onAnyInstanceMaximumDimensionsChange() { return this._register(this.createOnInstanceEvent(e => Event.map(e.onMaximumDimensionsChanged, () => e, e.store))).event; } @memoize get onAnyInstancePrimaryStatusChange() { return this._register(this.createOnInstanceEvent(e => Event.map(e.statusList.onDidChangePrimaryStatus, () => e, e.store))).event; } - @memoize get onAnyInstanceProcessIdReady() { return this._register(this.createOnInstanceEvent(e => e.onProcessIdReady)).event; } - @memoize get onAnyInstanceSelectionChange() { return this._register(this.createOnInstanceEvent(e => e.onDidChangeSelection)).event; } - @memoize get onAnyInstanceTitleChange() { return this._register(this.createOnInstanceEvent(e => e.onTitleChanged)).event; } + @memoize get onAnyInstanceProcessIdReady() { return this._register(this.createOnInstanceEvent(e => Event.map(e.onProcessIdReady, () => e, e.store))).event; } + @memoize get onAnyInstanceSelectionChange() { return this._register(this.createOnInstanceEvent(e => Event.map(e.onDidChangeSelection, () => e, e.store))).event; } + @memoize get onAnyInstanceTitleChange() { return this._register(this.createOnInstanceEvent(e => Event.map(e.onTitleChanged, () => e, e.store))).event; } @memoize get onAnyInstanceShellTypeChanged() { return this._register(this.createOnInstanceEvent(e => Event.map(e.onDidChangeShellType, () => e))).event; } @memoize get onAnyInstanceAddedCapabilityType() { return this._register(this.createOnInstanceEvent(e => Event.map(e.capabilities.onDidAddCapability, e => e.id))).event; } constructor( - @IContextKeyService private _contextKeyService: IContextKeyService, - @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @ITerminalLogService private readonly _logService: ITerminalLogService, - @IDialogService private _dialogService: IDialogService, - @IInstantiationService private _instantiationService: IInstantiationService, - @IRemoteAgentService private _remoteAgentService: IRemoteAgentService, - @IConfigurationService private readonly _configurationService: IConfigurationService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @ITerminalConfigurationService private readonly _terminalConfigurationService: ITerminalConfigurationService, - @ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService, - @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, - @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, - @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, - @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, - @IExtensionService private readonly _extensionService: IExtensionService, - @INotificationService private readonly _notificationService: INotificationService, - @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, - @ICommandService private readonly _commandService: ICommandService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - @ITimerService private readonly _timerService: ITimerService + // @IContextKeyService private _contextKeyService: IContextKeyService, + // @ILifecycleService private readonly _lifecycleService: ILifecycleService, + // @ITerminalLogService private readonly _logService: ITerminalLogService, + // @IDialogService private _dialogService: IDialogService, + // @IInstantiationService private _instantiationService: IInstantiationService, + // @IRemoteAgentService private _remoteAgentService: IRemoteAgentService, + // @IViewsService private _viewsService: IViewsService, + // @IConfigurationService private readonly _configurationService: IConfigurationService, + // @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + // @ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService, + // @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, + // @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, + // @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, + // @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, + // @IExtensionService private readonly _extensionService: IExtensionService, + // @INotificationService private readonly _notificationService: INotificationService, + // @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + // @ICommandService private readonly _commandService: ICommandService, + // @IKeybindingService private readonly _keybindingService: IKeybindingService, + // @ITimerService private readonly _timerService: ITimerService + ...args: unknown[] ) { super(); - // the below avoids having to poll routinely. - // we update detected profiles when an instance is created so that, - // for example, we detect if you've installed a pwsh - this._register(this.onDidCreateInstance(() => this._terminalProfileService.refreshAvailableProfiles())); - this._forwardInstanceHostEvents(this._terminalGroupService); - this._forwardInstanceHostEvents(this._terminalEditorService); - this._register(this._terminalGroupService.onDidChangeActiveGroup(this._onDidChangeActiveGroup.fire, this._onDidChangeActiveGroup)); - this._register(this._terminalInstanceService.onDidCreateInstance(instance => { - this._initInstanceListeners(instance); - this._onDidCreateInstance.fire(instance); - })); - - // Hide the panel if there are no more instances, provided that VS Code is not shutting - // down. When shutting down the panel is locked in place so that it is restored upon next - // launch. - this._register(this._terminalGroupService.onDidChangeActiveInstance(instance => { - if (!instance && !this._isShuttingDown && this._terminalConfigurationService.config.hideOnLastClosed) { - this._terminalGroupService.hidePanel(); - } - if (instance?.shellType) { - this._terminalShellTypeContextKey.set(instance.shellType.toString()); - } else if (!instance || !(instance.shellType)) { - this._terminalShellTypeContextKey.reset(); - } - })); - - this._handleInstanceContextKeys(); - this._terminalShellTypeContextKey = TerminalContextKeys.shellType.bindTo(this._contextKeyService); - this._processSupportContextKey = TerminalContextKeys.processSupported.bindTo(this._contextKeyService); - this._processSupportContextKey.set(!isWeb || this._remoteAgentService.getConnection() !== null); - this._terminalHasBeenCreated = TerminalContextKeys.terminalHasBeenCreated.bindTo(this._contextKeyService); - this._terminalCountContextKey = TerminalContextKeys.count.bindTo(this._contextKeyService); - this._terminalEditorActive = TerminalContextKeys.terminalEditorActive.bindTo(this._contextKeyService); - - this._register(this.onDidChangeActiveInstance(instance => { - this._terminalEditorActive.set(!!instance?.target && instance.target === TerminalLocation.Editor); - })); - - this._register(_lifecycleService.onBeforeShutdown(async e => e.veto(this._onBeforeShutdown(e.reason), 'veto.terminal'))); - this._register(_lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); - - this._initializePrimaryBackend(); - - // Create async as the class depends on `this` - timeout(0).then(() => this._register(this._instantiationService.createInstance(TerminalEditorStyle, mainWindow.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 (isString(result)) { - return; - } - const keyMods: IKeyMods | undefined = result.keyMods; - if (type === 'createInstance') { - const activeInstance = this.getDefaultInstanceHost().activeInstance; - const defaultLocation = this._terminalConfigurationService.defaultLocation; - let instance; - - if (result.config && hasKey(result.config, { id: true })) { - 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 } : defaultLocation - }); - return; - } else if (result.config && hasKey(result.config, { profileName: true })) { - if (keyMods?.alt && activeInstance) { - // create split, only valid if there's an active instance - instance = await this.createTerminal({ location: { parentTerminal: activeInstance }, config: result.config, cwd }); - } else { - instance = await this.createTerminal({ location: defaultLocation, config: result.config, cwd }); - } - } - - if (instance && defaultLocation !== TerminalLocation.Editor) { - this._terminalGroupService.showPanel(true); - this.setActiveInstance(instance); - return instance; - } - } return undefined; } - private async _initializePrimaryBackend() { - mark('code/terminal/willGetTerminalBackend'); - this._primaryBackend = await this._terminalInstanceService.getBackend(this._environmentService.remoteAuthority); - mark('code/terminal/didGetTerminalBackend'); - const enableTerminalReconnection = this._terminalConfigurationService.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 (this._primaryBackend) { - this._register(this._primaryBackend.onDidRequestDetach(async (e) => { - const instanceToDetach = this.getInstanceFromResource(getTerminalUri(e.workspaceId, e.instanceId)); - if (instanceToDetach) { - const persistentProcessId = instanceToDetach?.persistentProcessId; - if (persistentProcessId && !instanceToDetach.shellLaunchConfig.isFeatureTerminal && !instanceToDetach.shellLaunchConfig.customPtyImplementation) { - if (instanceToDetach.target === TerminalLocation.Editor) { - this._terminalEditorService.detachInstance(instanceToDetach); - } else { - this._terminalGroupService.getGroupForInstance(instanceToDetach)?.removeInstance(instanceToDetach); - } - await instanceToDetach.detachProcessAndDispose(TerminalExitReason.User); - await this._primaryBackend?.acceptDetachInstanceReply(e.requestId, persistentProcessId); - } else { - // will get rejected without a persistentProcessId to attach to - await this._primaryBackend?.acceptDetachInstanceReply(e.requestId, undefined); - } - } - })); - } - - mark('code/terminal/willReconnect'); - let reconnectedPromise: Promise; - if (isPersistentRemote) { - reconnectedPromise = this._reconnectToRemoteTerminals(); - } else if (enableTerminalReconnection) { - reconnectedPromise = this._reconnectToLocalTerminals(); - } else { - reconnectedPromise = Promise.resolve(); - } - reconnectedPromise.then(async () => { - this._setConnected(); - mark('code/terminal/didReconnect'); - mark('code/terminal/willReplay'); - const instances = await this._reconnectedTerminalGroups?.then(groups => groups.map(e => e.terminalInstances).flat()) ?? []; - await Promise.all(instances.map(e => new Promise(r => Event.once(e.onProcessReplayComplete)(r)))); - mark('code/terminal/didReplay'); - mark('code/terminal/willGetPerformanceMarks'); - await Promise.all(Array.from(this._terminalInstanceService.getRegisteredBackends()).map(async backend => { - this._timerService.setPerformanceMarks(backend.remoteAuthority === undefined ? 'localPtyHost' : 'remotePtyHost', await backend.getPerformanceMarks()); - backend.setReady(); - })); - mark('code/terminal/didGetPerformanceMarks'); - this._whenConnected.complete(); - }); + async initializePrimaryBackend() { + // No-op } getPrimaryBackend(): ITerminalBackend | undefined { - return this._primaryBackend; + return undefined; } async setNextCommandId(id: number, commandLine: string, commandId: string): Promise { - if (!this._primaryBackend || id <= 0) { - return; - } - await this._primaryBackend.setNextCommandId(id, commandLine, commandId); - } - - private _forwardInstanceHostEvents(host: ITerminalInstanceHost) { - this._register(host.onDidChangeInstances(this._onDidChangeInstances.fire, this._onDidChangeInstances)); - this._register(host.onDidDisposeInstance(this._onDidDisposeInstance.fire, this._onDidDisposeInstance)); - this._register(host.onDidChangeActiveInstance(instance => this._evaluateActiveInstance(host, instance))); - this._register(host.onDidFocusInstance(instance => { - this._onDidFocusInstance.fire(instance); - this._evaluateActiveInstance(host, instance); - })); - this._register(host.onDidChangeInstanceCapability((instance) => { - this._onDidChangeInstanceCapability.fire(instance); - })); - this._hostActiveTerminals.set(host, undefined); - } - - private _evaluateActiveInstance(host: ITerminalInstanceHost, instance: ITerminalInstance | undefined) { - // Track the latest active terminal for each host so that when one becomes undefined, the - // TerminalService's active terminal is set to the last active terminal from the other host. - // This means if the last terminal editor is closed such that it becomes undefined, the last - // active group's terminal will be used as the active terminal if available. - this._hostActiveTerminals.set(host, instance); - if (instance === undefined) { - for (const active of this._hostActiveTerminals.values()) { - if (active) { - instance = active; - } - } - } - this._activeInstance = instance; - this._onDidChangeActiveInstance.fire(instance); + // No-op } - setActiveInstance(value: ITerminalInstance | undefined) { - // TODO@meganrogge: Is this the right logic for when instance is undefined? - if (!value) { - return; - } - // If this was a hideFromUser terminal created by the API this was triggered by show, - // in which case we need to create the terminal group - if (value.shellLaunchConfig.hideFromUser) { - this.showBackgroundTerminal(value); - } - if (value.target === TerminalLocation.Editor) { - this._terminalEditorService.setActiveInstance(value); - } else { - this._terminalGroupService.setActiveInstance(value); - } + setActiveInstance(value: ITerminalInstance) { + // No-op } - async focusInstance(instance: ITerminalInstance): Promise { - if (instance.target === TerminalLocation.Editor) { - return this._terminalEditorService.focusInstance(instance); - } - return this._terminalGroupService.focusInstance(instance); + async focusActiveInstance(): Promise { + // No-op } - async focusActiveInstance(): Promise { - if (!this._activeInstance) { - return; - } - return this.focusInstance(this._activeInstance); + focusInstance(instance: ITerminalInstance): void { + // No-op } async createContributedTerminalProfile(extensionIdentifier: string, id: string, options: ICreateContributedTerminalProfileOptions): Promise { - await this._extensionService.activateByEvent(`onTerminalProfile:${id}`); - - const profileProvider = this._terminalProfileService.getContributedProfileProvider(extensionIdentifier, id); - if (!profileProvider) { - this._notificationService.error(`No terminal profile provider registered for id "${id}"`); - return; - } - try { - await profileProvider.createContributedTerminalProfile(options); - this._terminalGroupService.setActiveInstanceByIndex(this._terminalGroupService.instances.length - 1); - await this._terminalGroupService.activeInstance?.focusWhenReady(); - } catch (e) { - this._notificationService.error(e.message); - } + // No-op } async safeDisposeTerminal(instance: ITerminalInstance): Promise { - // Confirm on kill in the editor is handled by the editor input - if (instance.target !== TerminalLocation.Editor && - instance.hasChildProcesses && - (this._terminalConfigurationService.config.confirmOnKill === 'panel' || this._terminalConfigurationService.config.confirmOnKill === 'always')) { - const veto = await this._showTerminalCloseConfirmation(true); - if (veto) { - return; - } - } - return new Promise(r => { - Event.once(instance.onExit)(() => r()); - instance.dispose(TerminalExitReason.User); - }); - } - - private _setConnected() { - this._connectionState = TerminalConnectionState.Connected; - this._onDidChangeConnectionState.fire(); - this._logService.trace('Pty host ready'); - } - - private async _reconnectToRemoteTerminals(): Promise { - const remoteAuthority = this._environmentService.remoteAuthority; - if (!remoteAuthority) { - return; - } - const backend = await this._terminalInstanceService.getBackend(remoteAuthority); - if (!backend) { - return; - } - mark('code/terminal/willGetTerminalLayoutInfo'); - const layoutInfo = await backend.getTerminalLayoutInfo(); - mark('code/terminal/didGetTerminalLayoutInfo'); - backend.reduceConnectionGraceTime(); - mark('code/terminal/willRecreateTerminalGroups'); - await this._recreateTerminalGroups(layoutInfo); - mark('code/terminal/didRecreateTerminalGroups'); - // now that terminals have been restored, - // attach listeners to update remote when terminals are changed - this._attachProcessLayoutListeners(); - - this._logService.trace('Reconnected to remote terminals'); - } - - private async _reconnectToLocalTerminals(): Promise { - const localBackend = await this._terminalInstanceService.getBackend(); - if (!localBackend) { - return; - } - mark('code/terminal/willGetTerminalLayoutInfo'); - const layoutInfo = await localBackend.getTerminalLayoutInfo(); - mark('code/terminal/didGetTerminalLayoutInfo'); - if (layoutInfo && (layoutInfo.tabs.length > 0 || layoutInfo?.background?.length)) { - mark('code/terminal/willRecreateTerminalGroups'); - this._reconnectedTerminalGroups = this._recreateTerminalGroups(layoutInfo); - const revivedInstances = await this._reviveBackgroundTerminalInstances(layoutInfo.background || []); - this._backgroundedTerminalInstances = revivedInstances.map(instance => ({ instance })); - mark('code/terminal/didRecreateTerminalGroups'); - } - // now that terminals have been restored, - // attach listeners to update local state when terminals are changed - this._attachProcessLayoutListeners(); - - this._logService.trace('Reconnected to local terminals'); - } - - private _recreateTerminalGroups(layoutInfo?: ITerminalsLayoutInfo): Promise { - const groupPromises: Promise[] = []; - let activeGroup: Promise | undefined; - if (layoutInfo) { - for (const tabLayout of layoutInfo.tabs) { - const terminalLayouts = tabLayout.terminals.filter(t => t.terminal && t.terminal.isOrphan); - if (terminalLayouts.length) { - this._restoredGroupCount += terminalLayouts.length; - const promise = this._recreateTerminalGroup(tabLayout, terminalLayouts); - groupPromises.push(promise); - if (tabLayout.isActive) { - activeGroup = promise; - } - const activeInstance = this.instances.find(t => t.shellLaunchConfig.attachPersistentProcess?.id === tabLayout.activePersistentProcessId); - if (activeInstance) { - this.setActiveInstance(activeInstance); - } - } - } - if (layoutInfo.tabs.length) { - activeGroup?.then(group => this._terminalGroupService.activeGroup = group); - } - } - return Promise.all(groupPromises).then(result => result.filter(e => !!e) as ITerminalGroup[]); - } - - private async _reviveBackgroundTerminalInstances(bgTerminals: (IPtyHostAttachTarget | null)[]): Promise { - const instances: ITerminalInstance[] = []; - for (const bg of bgTerminals) { - const attachPersistentProcess = bg; - if (!attachPersistentProcess) { - continue; - } - const instance = await this.createTerminal({ config: { attachPersistentProcess, hideFromUser: true, forcePersist: true }, location: TerminalLocation.Panel }); - instances.push(instance); - } - return instances; - } - - private async _recreateTerminalGroup(tabLayout: IRawTerminalTabLayoutInfo, terminalLayouts: IRawTerminalInstanceLayoutInfo[]): Promise { - let lastInstance: Promise | undefined; - for (const terminalLayout of terminalLayouts) { - const attachPersistentProcess = terminalLayout.terminal!; - if (this._lifecycleService.startupKind !== StartupKind.ReloadedWindow && attachPersistentProcess.type === 'Task') { - continue; - } - mark(`code/terminal/willRecreateTerminal/${attachPersistentProcess.id}-${attachPersistentProcess.pid}`); - lastInstance = this.createTerminal({ - config: { attachPersistentProcess }, - location: lastInstance ? { parentTerminal: lastInstance } : TerminalLocation.Panel - }); - lastInstance.then(() => mark(`code/terminal/didRecreateTerminal/${attachPersistentProcess.id}-${attachPersistentProcess.pid}`)); - } - const group = lastInstance?.then(instance => { - const g = this._terminalGroupService.getGroupForInstance(instance); - g?.resizePanes(tabLayout.terminals.map(terminal => terminal.relativeSize)); - return g; - }); - return group; - } - - private _attachProcessLayoutListeners(): void { - this._register(this.onDidChangeActiveGroup(() => this._saveState())); - this._register(this.onDidChangeActiveInstance(() => this._saveState())); - this._register(this.onDidChangeInstances(() => this._saveState())); - // The state must be updated when the terminal is relaunched, otherwise the persistent - // terminal ID will be stale and the process will be leaked. - this._register(this.onAnyInstanceProcessIdReady(() => this._saveState())); - this._register(this.onAnyInstanceTitleChange(instance => this._updateTitle(instance))); - this._register(this.onAnyInstanceIconChange(e => this._updateIcon(e.instance, e.userInitiated))); - } - - private _handleInstanceContextKeys(): void { - const terminalIsOpenContext = TerminalContextKeys.isOpen.bindTo(this._contextKeyService); - const updateTerminalContextKeys = () => { - terminalIsOpenContext.set(this.instances.length > 0); - this._terminalCountContextKey.set(this.instances.length); - }; - this._register(this.onDidChangeInstances(() => updateTerminalContextKeys())); + // No-op } async getActiveOrCreateInstance(options?: { acceptsInput?: boolean }): Promise { - const activeInstance = this.activeInstance; - // No instance, create - if (!activeInstance) { - return this.createTerminal(); - } - // Active instance, ensure accepts input - if (!options?.acceptsInput || activeInstance.xterm?.isStdinDisabled !== true) { - return activeInstance; - } - // Active instance doesn't accept input, create and focus - const instance = await this.createTerminal(); - this.setActiveInstance(instance); - await this.revealActiveTerminal(); - return instance; + throw new Error('Terminal not supported in web environment'); } async revealTerminal(source: ITerminalInstance, preserveFocus?: boolean): Promise { - if (source.target === TerminalLocation.Editor) { - await this._terminalEditorService.revealActiveEditor(preserveFocus); - } else { - await this._terminalGroupService.showPanel(); - } + // No-op } - async revealActiveTerminal(preserveFocus?: boolean): Promise { - const instance = this.activeInstance; - if (!instance) { - return; - } - await this.revealTerminal(instance, preserveFocus); + async showBackgroundTerminal(instance: ITerminalInstance, suppressSetActive?: boolean): Promise { + // No-op } - - - requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise { - // The initial request came from the extension host, no need to wait for it - return new Promise(callback => { - this._onDidRequestStartExtensionTerminal.fire({ proxy, cols, rows, callback }); - }); + async revealActiveTerminal(preserveFocus?: boolean): Promise { + // No-op } - private _onBeforeShutdown(reason: ShutdownReason): MaybePromise { - // Never veto on web as this would block all windows from being closed. This disables - // process revive as we can't handle it on shutdown. - if (isWeb) { - this._isShuttingDown = true; - return false; - } - return this._onBeforeShutdownAsync(reason); + setEditable(instance: ITerminalInstance, data?: IEditableData | null): void { + // No-op } - private async _onBeforeShutdownAsync(reason: ShutdownReason): Promise { - if (this.instances.length === 0) { - // No terminal instances, don't veto - return false; - } - - // Persist terminal _buffer state_, note that even if this happens the dirty terminal prompt - // still shows as that cannot be revived - try { - this._shutdownWindowCount = await this._nativeDelegate?.getWindowCount(); - const shouldReviveProcesses = this._shouldReviveProcesses(reason); - if (shouldReviveProcesses) { - // Attempt to persist the terminal state but only allow 2000ms as we can't block - // shutdown. This can happen when in a remote workspace but the other side has been - // suspended and is in the process of reconnecting, the message will be put in a - // queue in this case for when the connection is back up and running. Aborting the - // process is preferable in this case. - await Promise.race([ - this._primaryBackend?.persistTerminalState(), - timeout(2000) - ]); - } - - // Persist terminal _processes_ - const shouldPersistProcesses = this._terminalConfigurationService.config.enablePersistentSessions && reason === ShutdownReason.RELOAD; - if (!shouldPersistProcesses) { - const hasDirtyInstances = ( - (this._terminalConfigurationService.config.confirmOnExit === 'always' && this.foregroundInstances.length > 0) || - (this._terminalConfigurationService.config.confirmOnExit === 'hasChildProcesses' && this.foregroundInstances.some(e => e.hasChildProcesses)) - ); - if (hasDirtyInstances) { - return this._onBeforeShutdownConfirmation(reason); - } - } - } catch (err: unknown) { - // Swallow as exceptions should not cause a veto to prevent shutdown - this._logService.warn('Exception occurred during terminal shutdown', err); - } - - this._isShuttingDown = true; - + isEditable(instance: ITerminalInstance | undefined): boolean { return false; } - setNativeDelegate(nativeDelegate: ITerminalServiceNativeDelegate): void { - this._nativeDelegate = nativeDelegate; - } - - private _shouldReviveProcesses(reason: ShutdownReason): boolean { - if (!this._terminalConfigurationService.config.enablePersistentSessions) { - return false; - } - switch (this._terminalConfigurationService.config.persistentSessionReviveProcess) { - case 'onExit': { - // Allow on close if it's the last window on Windows or Linux - if (reason === ShutdownReason.CLOSE && (this._shutdownWindowCount === 1 && !isMacintosh)) { - return true; - } - return reason === ShutdownReason.LOAD || reason === ShutdownReason.QUIT; - } - case 'onExitAndWindowClose': return reason !== ShutdownReason.RELOAD; - default: return false; - } - } - - private async _onBeforeShutdownConfirmation(reason: ShutdownReason): Promise { - // veto if configured to show confirmation and the user chose not to exit - const veto = await this._showTerminalCloseConfirmation(); - if (!veto) { - this._isShuttingDown = true; - } - - return veto; - } - - private _onWillShutdown(e: WillShutdownEvent): void { - // Don't touch processes if the shutdown was a result of reload as they will be reattached - const shouldPersistTerminals = this._terminalConfigurationService.config.enablePersistentSessions && e.reason === ShutdownReason.RELOAD; - - for (const instance of [...this._terminalGroupService.instances, ...this._backgroundedTerminalInstances.map(bg => bg.instance)]) { - if (shouldPersistTerminals && instance.shouldPersist) { - instance.detachProcessAndDispose(TerminalExitReason.Shutdown); - } else { - instance.dispose(TerminalExitReason.Shutdown); - } - } - - // Clear terminal layout info only when not persisting - if (!shouldPersistTerminals && !this._shouldReviveProcesses(e.reason)) { - this._primaryBackend?.setTerminalLayoutInfo(undefined); - } - } - - @debounce(500) - private _saveState(): void { - // Avoid saving state when shutting down as that would override process state to be revived - if (this._isShuttingDown) { - return; - } - if (!this._terminalConfigurationService.config.enablePersistentSessions) { - return; - } - const tabs = this._terminalGroupService.groups.map(g => g.getLayoutInfo(g === this._terminalGroupService.activeGroup)); - const state: ITerminalsLayoutInfoById = { tabs, background: this._backgroundedTerminalInstances.map(bg => bg.instance).filter(i => i.shellLaunchConfig.forcePersist).map(i => i.persistentProcessId).filter((e): e is number => e !== undefined) }; - this._primaryBackend?.setTerminalLayoutInfo(state); + getEditableData(instance: ITerminalInstance): IEditableData | undefined { + return undefined; } - @debounce(500) - private _updateTitle(instance: ITerminalInstance | undefined): void { - if (!this._terminalConfigurationService.config.enablePersistentSessions || !instance || !instance.persistentProcessId || !instance.title || instance.isDisposed) { - return; - } - if (instance.staticTitle) { - this._primaryBackend?.updateTitle(instance.persistentProcessId, instance.staticTitle, TitleEventSource.Api); - } else { - this._primaryBackend?.updateTitle(instance.persistentProcessId, instance.title, instance.titleSource); - } + requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise { + return Promise.resolve(undefined); } - @debounce(500) - private _updateIcon(instance: ITerminalInstance, userInitiated: boolean): void { - if (!this._terminalConfigurationService.config.enablePersistentSessions || !instance || !instance.persistentProcessId || !instance.icon || instance.isDisposed) { - return; - } - this._primaryBackend?.updateIcon(instance.persistentProcessId, userInitiated, instance.icon, instance.color); + setNativeDelegate(nativeDelegate: ITerminalServiceNativeDelegate): void { + // No-op } refreshActiveGroup(): void { - this._onDidChangeActiveGroup.fire(this._terminalGroupService.activeGroup); + // No-op } getInstanceFromId(terminalId: number): ITerminalInstance | undefined { - let bgIndex = -1; - this._backgroundedTerminalInstances.forEach((bg, i) => { - if (bg.instance.instanceId === terminalId) { - bgIndex = i; - } - }); - if (bgIndex !== -1) { - return this._backgroundedTerminalInstances[bgIndex].instance; - } - try { - return this.instances[this._getIndexFromId(terminalId)]; - } catch { - return undefined; - } + return undefined; } - getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { - return getInstanceFromResource(this.instances, resource); + getInstanceFromIndex(terminalIndex: number): ITerminalInstance { + throw new Error('Terminal not supported in web environment'); } - openResource(resource: URI): void { - const instance = this.getInstanceFromResource(resource); - if (instance) { - this.setActiveInstance(instance); - this.revealTerminal(instance); - const commands = instance.capabilities.get(TerminalCapability.CommandDetection)?.commands; - const params = new URLSearchParams(resource.query); - const relevantCommand = commands?.find(c => c.id === params.get('command')); - if (relevantCommand) { - instance.xterm?.markTracker.revealCommand(relevantCommand); - } - } + getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { + return undefined; } isAttachedToTerminal(remoteTerm: IRemoteTerminalAttachTarget): boolean { - return this.instances.some(term => term.processId === remoteTerm.pid); + return false; } moveToEditor(source: ITerminalInstance, group?: GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | AUX_WINDOW_GROUP_TYPE): void { - if (source.target === TerminalLocation.Editor) { - return; - } - const sourceGroup = this._terminalGroupService.getGroupForInstance(source); - if (!sourceGroup) { - return; - } - sourceGroup.removeInstance(source); - this._terminalEditorService.openEditor(source, group ? { viewColumn: group } : undefined); - + // No-op } moveIntoNewEditor(source: ITerminalInstance): void { - this.moveToEditor(source, AUX_WINDOW_GROUP); + // No-op } async moveToTerminalView(source?: ITerminalInstance | URI, target?: ITerminalInstance, side?: 'before' | 'after'): Promise { - if (URI.isUri(source)) { - source = this.getInstanceFromResource(source); - } - - if (!source) { - return; - } - - this._terminalEditorService.detachInstance(source); - - if (source.target !== TerminalLocation.Editor) { - await this._terminalGroupService.showPanel(true); - return; - } - source.target = TerminalLocation.Panel; - - let group: ITerminalGroup | undefined; - if (target) { - group = this._terminalGroupService.getGroupForInstance(target); - } - - if (!group) { - group = this._terminalGroupService.createGroup(); - } - - group.addInstance(source); - this.setActiveInstance(source); - await this._terminalGroupService.showPanel(true); - - if (target && side) { - const index = group.terminalInstances.indexOf(target) + (side === 'after' ? 1 : 0); - group.moveInstance(source, index, side); - } - - // Fire events - this._onDidChangeInstances.fire(); - this._onDidChangeActiveGroup.fire(this._terminalGroupService.activeGroup); - } - - protected _initInstanceListeners(instance: ITerminalInstance): void { - const instanceDisposables = new DisposableStore(); - instanceDisposables.add(instance.onDimensionsChanged(() => { - this._onDidChangeInstanceDimensions.fire(instance); - if (this._terminalConfigurationService.config.enablePersistentSessions && this.isProcessSupportRegistered) { - this._saveState(); - } - })); - instanceDisposables.add(instance.onDidFocus(this._onDidChangeActiveInstance.fire, this._onDidChangeActiveInstance)); - instanceDisposables.add(instance.onRequestAddInstanceToGroup(async e => await this._addInstanceToGroup(instance, e))); - instanceDisposables.add(instance.onDidChangeShellType(() => this._extensionService.activateByEvent(`onTerminal:${instance.shellType}`))); - instanceDisposables.add(Event.runAndSubscribe(instance.capabilities.onDidAddCapability, (() => { - if (instance.capabilities.has(TerminalCapability.CommandDetection)) { - this._extensionService.activateByEvent(`onTerminalShellIntegration:${instance.shellType}`); - } - }))); - const disposeListener = this._register(instance.onDisposed(() => { - instanceDisposables.dispose(); - this._store.delete(disposeListener); - })); - } - - private async _addInstanceToGroup(instance: ITerminalInstance, e: IRequestAddInstanceToGroupEvent): Promise { - const terminalIdentifier = parseTerminalUri(e.uri); - if (terminalIdentifier.instanceId === undefined) { - return; - } - - let sourceInstance: ITerminalInstance | undefined = this.getInstanceFromResource(e.uri); - - // Terminal from a different window - if (!sourceInstance) { - const attachPersistentProcess = await this._primaryBackend?.requestDetachInstance(terminalIdentifier.workspaceId, terminalIdentifier.instanceId); - if (attachPersistentProcess) { - sourceInstance = await this.createTerminal({ config: { attachPersistentProcess }, resource: e.uri }); - this._terminalGroupService.moveInstance(sourceInstance, instance, e.side); - return; - } - } - - // View terminals - sourceInstance = this._terminalGroupService.getInstanceFromResource(e.uri); - if (sourceInstance) { - this._terminalGroupService.moveInstance(sourceInstance, instance, e.side); - return; - } - - // Terminal editors - sourceInstance = this._terminalEditorService.getInstanceFromResource(e.uri); - if (sourceInstance) { - this.moveToTerminalView(sourceInstance, instance, e.side); - return; - } - return; + // No-op } registerProcessSupport(isSupported: boolean): void { - if (!isSupported) { - return; - } - this._processSupportContextKey.set(isSupported); - this._onDidRegisterProcessSupport.fire(); - } - - // TODO: Remove this, it should live in group/editor servioce - private _getIndexFromId(terminalId: number): number { - let terminalIndex = -1; - this.instances.forEach((terminalInstance, i) => { - if (terminalInstance.instanceId === terminalId) { - terminalIndex = i; - } - }); - if (terminalIndex === -1) { - throw new Error(`Terminal with ID ${terminalId} does not exist (has it already been disposed?)`); - } - return terminalIndex; + // No-op } protected async _showTerminalCloseConfirmation(singleTerminal?: boolean): Promise { - let message: string; - const foregroundInstances = this.foregroundInstances; - if (foregroundInstances.length === 1 || singleTerminal) { - message = nls.localize('terminalService.terminalCloseConfirmationSingular', "Do you want to terminate the active terminal session?"); - } else { - message = nls.localize('terminalService.terminalCloseConfirmationPlural', "Do you want to terminate the {0} active terminal sessions?", foregroundInstances.length); - } - const { confirmed } = await this._dialogService.confirm({ - type: 'warning', - message, - primaryButton: nls.localize({ key: 'terminate', comment: ['&& denotes a mnemonic'] }, "&&Terminate") - }); - return !confirmed; + return false; } getDefaultInstanceHost(): ITerminalInstanceHost { - if (this._terminalConfigurationService.defaultLocation === TerminalLocation.Editor) { - return this._terminalEditorService; - } - return this._terminalGroupService; + throw new Error('Terminal not supported in web environment'); } async getInstanceHost(location: ITerminalLocationOptions | undefined): Promise { - if (location) { - if (location === TerminalLocation.Editor) { - return this._terminalEditorService; - } else if (typeof location === 'object') { - if (hasKey(location, { viewColumn: true })) { - return this._terminalEditorService; - } else if (hasKey(location, { parentTerminal: true })) { - return (await location.parentTerminal).target === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService; - } - } else { - return this._terminalGroupService; - } - } - return this; + throw new Error('Terminal not supported in web environment'); } async createTerminal(options?: ICreateTerminalOptions): Promise { - // Await the initialization of available profiles as long as this is not a pty terminal or a - // local terminal in a remote workspace as profile won't be used in those cases and these - // terminals need to be launched before remote connections are established. - if (this._terminalProfileService.availableProfiles.length === 0) { - const isPtyTerminal = options?.config && hasKey(options.config, { customPtyImplementation: true }); - const isLocalInRemoteTerminal = this._remoteAgentService.getConnection() && URI.isUri(options?.cwd) && options?.cwd.scheme === Schemas.vscodeFileResource; - if (!isPtyTerminal && !isLocalInRemoteTerminal) { - if (this._connectionState === TerminalConnectionState.Connecting) { - mark(`code/terminal/willGetProfiles`); - } - await this._terminalProfileService.profilesReady; - if (this._connectionState === TerminalConnectionState.Connecting) { - mark(`code/terminal/didGetProfiles`); - } - } - } - - const config = options?.config || this._terminalProfileService.getDefaultProfile(); - const shellLaunchConfig = config && hasKey(config, { extensionIdentifier: true }) ? {} : this._terminalInstanceService.convertProfileToShellLaunchConfig(config || {}); - - // Get the contributed profile if it was provided - const contributedProfile = options?.skipContributedProfileCheck ? undefined : await this._getContributedProfile(shellLaunchConfig, options); - - const splitActiveTerminal = typeof options?.location === 'object' && hasKey(options.location, { splitActiveTerminal: true }) - ? options.location.splitActiveTerminal - : typeof options?.location === 'object' ? hasKey(options.location, { parentTerminal: true }) : false; - - await this._resolveCwd(shellLaunchConfig, splitActiveTerminal, options); - - // Launch the contributed profile - // If it's a custom pty implementation, we did not await the profiles ready, so - // we cannot launch the contributed profile and doing so would cause an error - if (!shellLaunchConfig.customPtyImplementation && contributedProfile) { - const resolvedLocation = await this.resolveLocation(options?.location); - let location: TerminalLocation | { viewColumn: number; preserveState?: boolean } | { splitActiveTerminal: boolean } | undefined; - if (splitActiveTerminal) { - location = resolvedLocation === TerminalLocation.Editor ? { viewColumn: SIDE_GROUP } : { splitActiveTerminal: true }; - } else { - location = typeof options?.location === 'object' && hasKey(options.location, { viewColumn: true }) ? options.location : resolvedLocation; - } - await this.createContributedTerminalProfile(contributedProfile.extensionIdentifier, contributedProfile.id, { - icon: contributedProfile.icon, - color: contributedProfile.color, - location, - cwd: shellLaunchConfig.cwd, - }); - const instanceHost = resolvedLocation === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService; - // TODO@meganrogge: This returns undefined in the remote & web smoke tests but the function - // does not return undefined. This should be handled correctly. - const instance = instanceHost.instances[instanceHost.instances.length - 1]; - await instance?.focusWhenReady(); - this._terminalHasBeenCreated.set(true); - return instance; - } - - if (!shellLaunchConfig.customPtyImplementation && !this.isProcessSupportRegistered) { - throw new Error('Could not create terminal when process support is not registered'); - } - - this._evaluateLocalCwd(shellLaunchConfig); - const location = await this.resolveLocation(options?.location) || this._terminalConfigurationService.defaultLocation; - - if (shellLaunchConfig.hideFromUser) { - const instance = this._terminalInstanceService.createInstance(shellLaunchConfig, location); - this._backgroundedTerminalInstances.push({ instance, terminalLocationOptions: options?.location }); - this._backgroundedTerminalDisposables.set(instance.instanceId, [ - instance.onDisposed(instance => { - const idx = this._backgroundedTerminalInstances.findIndex(bg => bg.instance === instance); - if (idx !== -1) { - this._backgroundedTerminalInstances.splice(idx, 1); - } - this._onDidDisposeInstance.fire(instance); - }) - ]); - this._onDidChangeInstances.fire(); - return instance; - } - - const parent = await this._getSplitParent(options?.location); - this._terminalHasBeenCreated.set(true); - this._extensionService.activateByEvent('onTerminal:*'); - let instance; - if (parent) { - instance = this._splitTerminal(shellLaunchConfig, location, parent); - } else { - instance = this._createTerminal(shellLaunchConfig, location, options); - } - if (instance.shellType) { - this._extensionService.activateByEvent(`onTerminal:${instance.shellType}`); - } - - return instance; + throw new Error('Terminal not supported in web environment'); } async createAndFocusTerminal(options?: ICreateTerminalOptions): Promise { - const instance = await this.createTerminal(options); - this.setActiveInstance(instance); - await instance.focusWhenReady(); - return instance; - } - - private async _getContributedProfile(shellLaunchConfig: IShellLaunchConfig, options?: ICreateTerminalOptions): Promise { - if (options?.config && hasKey(options.config, { extensionIdentifier: true })) { - return options.config; - } - - return this._terminalProfileService.getContributedDefaultProfile(shellLaunchConfig); + throw new Error('Terminal not supported in web environment'); } async createDetachedTerminal(options: IDetachedXTermOptions): Promise { - const ctor = await TerminalInstance.getXtermConstructor(this._keybindingService, this._contextKeyService); - const xterm = this._instantiationService.createInstance(XtermTerminal, undefined, ctor, { - cols: options.cols, - rows: options.rows, - xtermColorProvider: options.colorProvider, - capabilities: options.capabilities || new TerminalCapabilityStore(), - }, undefined); - - if (options.readonly) { - xterm.raw.attachCustomKeyEventHandler(() => false); - } - - const instance = new DetachedTerminal(xterm, options, this._instantiationService); - this._detachedXterms.add(instance); - const l = xterm.onDidDispose(() => { - this._detachedXterms.delete(instance); - l.dispose(); - }); - - return instance; - } - - private async _resolveCwd(shellLaunchConfig: IShellLaunchConfig, splitActiveTerminal: boolean, options?: ICreateTerminalOptions): Promise { - const cwd = shellLaunchConfig.cwd; - if (!cwd) { - if (options?.cwd) { - shellLaunchConfig.cwd = options.cwd; - } else if (splitActiveTerminal && options?.location) { - let parent = this.activeInstance; - if (typeof options.location === 'object' && hasKey(options.location, { parentTerminal: true })) { - parent = await options.location.parentTerminal; - } - if (!parent) { - throw new Error('Cannot split without an active instance'); - } - shellLaunchConfig.cwd = await getCwdForSplit(parent, this._workspaceContextService.getWorkspace().folders, this._commandService, this._terminalConfigurationService); - } - } - } - - private _splitTerminal(shellLaunchConfig: IShellLaunchConfig, location: TerminalLocation, parent: ITerminalInstance): ITerminalInstance { - let instance; - // Use the URI from the base instance if it exists, this will correctly split local terminals - if (typeof shellLaunchConfig.cwd !== 'object' && typeof parent.shellLaunchConfig.cwd === 'object') { - shellLaunchConfig.cwd = URI.from({ - scheme: parent.shellLaunchConfig.cwd.scheme, - authority: parent.shellLaunchConfig.cwd.authority, - path: shellLaunchConfig.cwd || parent.shellLaunchConfig.cwd.path - }); - } - if (location === TerminalLocation.Editor || parent.target === TerminalLocation.Editor) { - instance = this._terminalEditorService.splitInstance(parent, shellLaunchConfig); - } else { - const group = this._terminalGroupService.getGroupForInstance(parent); - if (!group) { - throw new Error(`Cannot split a terminal without a group (instanceId: ${parent.instanceId}, title: ${parent.title})`); - } - shellLaunchConfig.parentTerminalId = parent.instanceId; - instance = group.split(shellLaunchConfig); - } - return instance; - } - - private _createTerminal(shellLaunchConfig: IShellLaunchConfig, location: TerminalLocation, options?: ICreateTerminalOptions): ITerminalInstance { - let instance; - if (location === TerminalLocation.Editor) { - instance = this._terminalInstanceService.createInstance(shellLaunchConfig, TerminalLocation.Editor); - if (!shellLaunchConfig.hideFromUser) { - const editorOptions = this._getEditorOptions(options?.location); - this._terminalEditorService.openEditor(instance, editorOptions); - } - } else { - // TODO: pass resource? - const group = this._terminalGroupService.createGroup(shellLaunchConfig); - instance = group.terminalInstances[0]; - } - return instance; + throw new Error('Terminal not supported in web environment'); } async resolveLocation(location?: ITerminalLocationOptions): Promise { - if (location && typeof location === 'object') { - if (hasKey(location, { parentTerminal: true })) { - // since we don't set the target unless it's an editor terminal, this is necessary - const parentTerminal = await location.parentTerminal; - return !parentTerminal.target ? TerminalLocation.Panel : parentTerminal.target; - } else if (hasKey(location, { viewColumn: true })) { - return TerminalLocation.Editor; - } else if (hasKey(location, { splitActiveTerminal: true })) { - // since we don't set the target unless it's an editor terminal, this is necessary - return !this._activeInstance?.target ? TerminalLocation.Panel : this._activeInstance?.target; - } - } - return location; - } - - private async _getSplitParent(location?: ITerminalLocationOptions): Promise { - if (location && typeof location === 'object' && hasKey(location, { parentTerminal: true })) { - return location.parentTerminal; - } else if (location && typeof location === 'object' && hasKey(location, { splitActiveTerminal: true })) { - return this.activeInstance; - } return undefined; } - private _getEditorOptions(location?: ITerminalLocationOptions): TerminalEditorLocation | undefined { - if (location && typeof location === 'object' && hasKey(location, { viewColumn: true })) { - // Terminal-specific workaround to resolve the active group in auxiliary windows to - // override the locked editor behavior. - if (location.viewColumn === ACTIVE_GROUP && isAuxiliaryWindow(getActiveWindow())) { - location.viewColumn = this._editorGroupsService.activeGroup.id; - return location; - } - location.viewColumn = columnToEditorGroup(this._editorGroupsService, this._configurationService, location.viewColumn); - return location; - } - return undefined; - } - - private _evaluateLocalCwd(shellLaunchConfig: IShellLaunchConfig) { - // Add welcome message and title annotation for local terminals launched within remote or - // virtual workspaces - if (!isString(shellLaunchConfig.cwd) && shellLaunchConfig.cwd?.scheme === Schemas.file) { - if (VirtualWorkspaceContext.getValue(this._contextKeyService)) { - shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize('localTerminalVirtualWorkspace', "This shell is open to a {0}local{1} folder, NOT to the virtual folder", '\x1b[3m', '\x1b[23m'), { excludeLeadingNewLine: true, loudFormatting: true }); - shellLaunchConfig.type = 'Local'; - } else if (this._remoteAgentService.getConnection()) { - shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize('localTerminalRemote', "This shell is running on your {0}local{1} machine, NOT on the connected remote machine", '\x1b[3m', '\x1b[23m'), { excludeLeadingNewLine: true, loudFormatting: true }); - shellLaunchConfig.type = 'Local'; - } - } + async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { + // No-op } - public async showBackgroundTerminal(instance: ITerminalInstance, suppressSetActive?: boolean): Promise { - const index = this._backgroundedTerminalInstances.findIndex(bg => bg.instance === instance); - if (index === -1) { - return; - } - const backgroundTerminal = this._backgroundedTerminalInstances[index]; - this._backgroundedTerminalInstances.splice(index, 1); - const disposables = this._backgroundedTerminalDisposables.get(instance.instanceId); - if (disposables) { - dispose(disposables); - } - this._backgroundedTerminalDisposables.delete(instance.instanceId); - if (instance.target === TerminalLocation.Panel) { - this._terminalGroupService.createGroup(instance); - - // Make active automatically if it's the first instance - if (this.instances.length === 1 && !suppressSetActive) { - this._terminalGroupService.setActiveInstanceByIndex(0); - } - } else { - const editorOptions = backgroundTerminal.terminalLocationOptions ? this._getEditorOptions(backgroundTerminal.terminalLocationOptions) : this._getEditorOptions(instance.target); - this._terminalEditorService.openEditor(instance, editorOptions); - } - - this._onDidChangeInstances.fire(); + getEditingTerminal(): ITerminalInstance | undefined { + return undefined; } - async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { - this._terminalConfigurationService.setPanelContainer(panelContainer); - this._terminalGroupService.setContainer(terminalContainer); + setEditingTerminal(instance: ITerminalInstance | undefined) { + return undefined; } - - createOnInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): DynamicListEventMultiplexer { - return new DynamicListEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, getEvent); + // Return a dummy multiplexer with a never-firing event + return new DynamicListEventMultiplexer( + [], + new Emitter().event, + new Emitter().event, + getEvent + ); } createOnInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): IDynamicListEventMultiplexer<{ instance: ITerminalInstance; data: K }> { - return createInstanceCapabilityEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, capabilityId, getEvent); - } -} - -class TerminalEditorStyle extends Themable { - private _styleElement: HTMLElement; - - constructor( - container: HTMLElement, - @ITerminalService private readonly _terminalService: ITerminalService, - @IThemeService private readonly _themeService: IThemeService, - @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, - @IEditorService private readonly _editorService: IEditorService - ) { - super(_themeService); - this._registerListeners(); - this._styleElement = domStylesheets.createStyleSheet(container); - this._register(toDisposable(() => this._styleElement.remove())); - this.updateStyles(); - } - - private _registerListeners(): void { - this._register(this._terminalService.onAnyInstanceIconChange(() => this.updateStyles())); - this._register(this._terminalService.onDidCreateInstance(() => this.updateStyles())); - this._register(this._editorService.onDidActiveEditorChange(() => { - if (this._editorService.activeEditor instanceof TerminalEditorInput) { - this.updateStyles(); - } - })); - this._register(this._editorService.onDidCloseEditor(() => { - if (this._editorService.activeEditor instanceof TerminalEditorInput) { - this.updateStyles(); - } - })); - this._register(this._terminalProfileService.onDidChangeAvailableProfiles(() => this.updateStyles())); + // Return a dummy multiplexer that never fires + const emitter = new Emitter<{ instance: ITerminalInstance; data: K }>(); + return { + event: emitter.event, + dispose: () => emitter.dispose() + }; } - override updateStyles(): void { - super.updateStyles(); - const colorTheme = this._themeService.getColorTheme(); - - // TODO: add a rule collector to avoid duplication - let css = ''; - - const productIconTheme = this._themeService.getProductIconTheme(); - - // Add icons - for (const instance of this._terminalService.instances) { - const icon = instance.icon; - if (!icon) { - continue; - } - let uri = undefined; - if (icon instanceof URI) { - uri = icon; - } else if (icon instanceof Object && hasKey(icon, { light: true, dark: true })) { - uri = isDark(colorTheme.type) ? icon.dark : icon.light; - } - const iconClasses = getUriClasses(instance, colorTheme.type); - if (uri instanceof URI && iconClasses && iconClasses.length > 1) { - css += ( - cssValue.inline`.monaco-workbench .terminal-tab.${cssValue.className(iconClasses[0])}::before - {content: ''; background-image: ${cssValue.asCSSUrl(uri)};}` - ); - } - if (ThemeIcon.isThemeIcon(icon)) { - const iconRegistry = getIconRegistry(); - const iconContribution = iconRegistry.getIcon(icon.id); - if (iconContribution) { - const def = productIconTheme.getIcon(iconContribution); - if (def) { - css += cssValue.inline`.monaco-workbench .terminal-tab.codicon-${cssValue.className(icon.id)}::before - {content: ${cssValue.stringValue(def.fontCharacter)} !important; font-family: ${cssValue.stringValue(def.font?.id ?? 'codicon')} !important;}`; - } - } - } - } - - // Add colors - const iconForegroundColor = colorTheme.getColor(iconForeground); - if (iconForegroundColor) { - css += cssValue.inline`.monaco-workbench .show-file-icons .file-icon.terminal-tab::before { color: ${iconForegroundColor}; }`; - } - - css += getColorStyleContent(colorTheme, true); - this._styleElement.textContent = css; + openResource(resource: URI): void { + // No-op } } diff --git a/src/vs/workbench/contrib/url/browser/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts index e4f83a86eb2d30..59e86174cfe7d0 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -144,6 +144,8 @@ export function readStaticTrustedDomains(accessor: ServicesAccessor): IStaticTru const environmentService = accessor.get(IBrowserWorkbenchEnvironmentService); const defaultTrustedDomains = [ + // MEMBRANE: add membrane.io to the list of trusted domains to skip confirmation dialog + 'https://membrane.io', ...productService.linkProtectionTrustedDomains ?? [], ...environmentService.options?.additionalTrustedDomains ?? [] ]; diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 936b635aa6cf19..c1f26056bcfc6f 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -367,7 +367,14 @@ export class WebviewOriginStore { return existing; } - const newOrigin = generateUuid(); + // MEMBRANE: run all of our webviews under the same origin (an arbitrarily chosen uuid) + let newOrigin; + if (additionalKey === 'membrane.membrane') { + newOrigin = '21c97241-117d-474d-bf38-a2e8d63a227b'; + } else { + newOrigin = generateUuid(); + } + this._state[key] = newOrigin; this._memento.saveMemento(); return newOrigin; diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts index 5a7de862a35936..6f3c26bf98aaf9 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts @@ -42,6 +42,8 @@ export class WebviewEditor extends EditorPane { private _dimension?: DOM.Dimension; private _visible = false; private _isDisposed = false; + // MEMBRANE: used for debouncing DOM changes to improve performance when toggling zen-mode + private _animationFrame?: number; private readonly _webviewVisibleDisposables = this._register(new DisposableStore()); private readonly _onFocusWindowHandler = this._register(new MutableDisposable()); @@ -190,12 +192,18 @@ export class WebviewEditor extends EditorPane { } private synchronizeWebviewContainerDimensions(webview: IOverlayWebview, dimension?: DOM.Dimension) { - if (!this._element?.isConnected) { - return; + // MEMBRANE: debounce DOM changes to improve performance when toggling zen-mode + if (this._animationFrame) { + this.window.cancelAnimationFrame(this._animationFrame); } - - const rootContainer = this._workbenchLayoutService.getContainer(this.window, Parts.EDITOR_PART); - webview.layoutWebviewOverElement(this._element.parentElement!, dimension, rootContainer); + this._animationFrame = this.window.requestAnimationFrame(() => { + this._animationFrame = undefined; + if (!this._element?.isConnected) { + return; + } + const rootContainer = this._workbenchLayoutService.getContainer(this.window, Parts.EDITOR_PART); + webview.layoutWebviewOverElement(this._element.parentElement!, dimension, rootContainer); + }); } private trackFocus(webview: IOverlayWebview): IDisposable { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts index b63e894b1eab39..3de0c910aa807f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts @@ -315,7 +315,7 @@ configurationRegistry.registerConfiguration({ localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.terminal' }, "Open a new terminal in the editor area."), ], - 'default': 'welcomePage', + 'default': 'none', 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.") }, 'workbench.welcomePage.preferReducedMotion': { diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 09187c4940ff6a..9240b57a6f4565 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -14,7 +14,6 @@ import { workbenchColorsSchemaId } from '../../../../platform/theme/common/color import { tokenStylingSchemaId } from '../../../../platform/theme/common/tokenClassificationRegistry.js'; import { ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IColorCustomizations, ITokenColorCustomizations, IWorkbenchProductIconTheme, ISemanticTokenColorCustomizations, ThemeSettingTarget, ThemeSettingDefaults } from './workbenchThemeService.js'; import { IConfigurationService, ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; -import { isWeb } from '../../../../base/common/platform.js'; import { ColorScheme } from '../../../../platform/theme/common/theme.js'; import { IHostColorSchemeService } from './hostColorSchemeService.js'; @@ -34,8 +33,9 @@ export const COLOR_THEME_CONFIGURATION_SETTINGS_TAG = 'colorThemeConfiguration'; const colorThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', markdownDescription: nls.localize({ key: 'colorTheme', comment: ['{0} will become a link to another setting.'] }, "Specifies the color theme used in the workbench when {0} is not enabled.", formatSettingAsLink(ThemeSettings.DETECT_COLOR_SCHEME)), - default: isWeb ? ThemeSettingDefaults.COLOR_THEME_LIGHT : ThemeSettingDefaults.COLOR_THEME_DARK, tags: [COLOR_THEME_CONFIGURATION_SETTINGS_TAG], + // MEMBRANE: default to user's OS preference + default: window.matchMedia('(prefers-color-scheme: dark)').matches ? ThemeSettingDefaults.COLOR_THEME_DARK : ThemeSettingDefaults.COLOR_THEME_LIGHT, enum: colorThemeSettingEnum, enumDescriptions: colorThemeSettingEnumDescriptions, enumItemLabels: colorThemeSettingEnumItemLabels, @@ -84,8 +84,10 @@ const preferredHCLightThemeSettingSchema: IConfigurationPropertySchema = { const detectColorSchemeSettingSchema: IConfigurationPropertySchema = { type: 'boolean', markdownDescription: nls.localize({ key: 'detectColorScheme', comment: ['{0} and {1} will become links to other settings.'] }, 'If enabled, will automatically select a color theme based on the system color mode. If the system color mode is dark, {0} is used, else {1}.', formatSettingAsLink(ThemeSettings.PREFERRED_DARK_THEME), formatSettingAsLink(ThemeSettings.PREFERRED_LIGHT_THEME)), - default: false, tags: [COLOR_THEME_CONFIGURATION_SETTINGS_TAG], + // MEMBRANE: auto-update color theme based on OS preference + // Note: it appears Microsoft doesn't do this for legacy reasons: https://github.com/microsoft/vscode/pull/236052 + default: true }; const colorCustomizationsSchema: IConfigurationPropertySchema = { diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 9c0f9e254d359a..be59f6fe042d35 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -39,8 +39,8 @@ export enum ThemeSettings { } export enum ThemeSettingDefaults { - COLOR_THEME_DARK = 'Default Dark Modern', - COLOR_THEME_LIGHT = 'Default Light Modern', + COLOR_THEME_DARK = 'Membrane Dark', + COLOR_THEME_LIGHT = 'Membrane Light', COLOR_THEME_HC_DARK = 'Default High Contrast', COLOR_THEME_HC_LIGHT = 'Default High Contrast Light', diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 49038938699bb0..2cc0ccf5fde19f 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -187,7 +187,7 @@ registerSingleton(IAllowedMcpServersService, AllowedMcpServersService, Instantia import './services/accounts/common/defaultAccount.js'; // Telemetry -import './contrib/telemetry/browser/telemetry.contribution.js'; +// import './contrib/telemetry/browser/telemetry.contribution.js'; // Preferences import './contrib/preferences/browser/preferences.contribution.js'; @@ -305,9 +305,9 @@ import './contrib/relauncher/browser/relauncher.contribution.js'; // Tasks import './contrib/tasks/browser/task.contribution.js'; -// Remote -import './contrib/remote/common/remote.contribution.js'; -import './contrib/remote/browser/remote.contribution.js'; +// // Remote +// import './contrib/remote/common/remote.contribution.js'; +// import './contrib/remote/browser/remote.contribution.js'; // Emmet import './contrib/emmet/browser/emmet.contribution.js'; @@ -342,15 +342,16 @@ import './contrib/themes/browser/themes.contribution.js'; // Update import './contrib/update/browser/update.contribution.js'; -// Surveys -import './contrib/surveys/browser/nps.contribution.js'; -import './contrib/surveys/browser/languageSurveys.contribution.js'; +// Membrane +// // Surveys +// import './contrib/surveys/browser/nps.contribution.js'; +// import './contrib/surveys/browser/languageSurveys.contribution.js'; -// Welcome -import './contrib/welcomeGettingStarted/browser/gettingStarted.contribution.js'; -import './contrib/welcomeWalkthrough/browser/walkThrough.contribution.js'; -import './contrib/welcomeViews/common/viewsWelcome.contribution.js'; -import './contrib/welcomeViews/common/newFile.contribution.js'; +// // Welcome +// import './contrib/welcomeGettingStarted/browser/gettingStarted.contribution.js'; +// import './contrib/welcomeWalkthrough/browser/walkThrough.contribution.js'; +// import './contrib/welcomeViews/common/viewsWelcome.contribution.js'; +// import './contrib/welcomeViews/common/newFile.contribution.js'; // Call Hierarchy import './contrib/callHierarchy/browser/callHierarchy.contribution.js'; @@ -405,7 +406,8 @@ import './contrib/list/browser/list.contribution.js'; import './contrib/accessibilitySignals/browser/accessibilitySignal.contribution.js'; // Bracket Pair Colorizer 2 Telemetry -import './contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.js'; +// MEMBRANE: unused telemetry +// import './contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.js'; // Accessibility import './contrib/accessibility/browser/accessibility.contribution.js';