From 80fa96908668f429f639a2fdbecb526af934bb87 Mon Sep 17 00:00:00 2001 From: Jhonny Date: Mon, 29 Jan 2024 16:12:37 -0500 Subject: [PATCH 01/80] implements a custom workbench for membrane. --- src/vs/code/browser/workbench/workbench.ts | 644 ++------------------- 1 file changed, 57 insertions(+), 587 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 533cb26ae8e5c8..a9e28b28060812 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,621 +3,91 @@ * 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 { create } from '../../../workbench/workbench.web.main.internal.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import product from '../../../platform/product/common/product.js'; +import { + IWorkbenchConstructionOptions, + IWorkspace, + IWorkspaceProvider, +} from '../../../workbench/browser/web.api.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; - } +import { mainWindow } from '../../../base/browser/window.js'; - 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; - } +class SecretStorageProvider implements ISecretStorageProvider { + public type = 'persisted' as const; async get(key: string): Promise { - const secrets = await this.secretsPromise; - - return secrets[key]; + try { + const secret = JSON.parse(key); + return localStorage.getItem(secret.key) ?? undefined; + } catch { + return undefined; + } } async set(key: string, value: string): Promise { - const secrets = await this.secretsPromise; - secrets[key] = value; - this.secretsPromise = Promise.resolve(secrets); - this.save(); + localStorage.setItem(key, value); } async delete(key: string): Promise { - const secrets = await this.secretsPromise; - delete secrets[key]; - this.secretsPromise = Promise.resolve(secrets); - this.save(); + localStorage.removeItem(key); } 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); - } + return []; } } -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' - ]; +(async function () { + let config: IWorkbenchConstructionOptions & { + folderUri?: UriComponents; + workspaceUri?: UriComponents; + domElementId?: string; + } = {}; - 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)}`); - } - } - - // 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; + const windowProduct = (globalThis as { product?: unknown }).product; + if (windowProduct && typeof windowProduct === 'object') { + config = windowProduct as typeof config; + } else { + const result = await fetch('/product.json'); + if (!result.ok) { + throw new Error(`Failed to fetch product.json: ${result.status}`); } - - this.onDidChangeLocalStorageDisposable = addDisposableListener(mainWindow, 'storage', () => this.onDidChangeLocalStorage()); + config = await result.json() as typeof config; } - private stopListening(): void { - this.onDidChangeLocalStorageDisposable?.dispose(); - this.onDidChangeLocalStorageDisposable = undefined; + // Revive URIs in additionalBuiltinExtensions if present + if (Array.isArray(config.additionalBuiltinExtensions)) { + config = { + ...config, + additionalBuiltinExtensions: config.additionalBuiltinExtensions.map((ext: unknown) => URI.revive(ext as UriComponents)) + }; } - // 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 - ) { + let workspace: IWorkspace | undefined; + if (config.folderUri) { + workspace = { folderUri: URI.revive(config.folderUri) }; + } else if (config.workspaceUri) { + workspace = { workspaceUri: URI.revive(config.workspaceUri) }; } - 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 domElement = mainWindow.document.body; - const targetHref = this.createTargetUrl(workspace, options); - if (targetHref) { - if (options?.reuse) { - mainWindow.location.href = targetHref; + if (workspace) { + const workspaceProvider: IWorkspaceProvider = { + workspace, + open: async ( + workspaceToOpen: IWorkspace, + options?: { reuse?: boolean; payload?: object } + ): Promise => { 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); - } + }, + trusted: true, + }; + (config as unknown as { workspaceProvider?: IWorkspaceProvider }).workspaceProvider = workspaceProvider; } - return undefined; -} - -(function () { - - // 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(); + (config as unknown as { secretStorageProvider?: ISecretStorageProvider }).secretStorageProvider = new SecretStorageProvider(); - // 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), - }); + create(domElement, config as IWorkbenchConstructionOptions); })(); From da4139cc0a10e696c7b6edb9e2d831bdc32b683f Mon Sep 17 00:00:00 2001 From: Jhonny Date: Sat, 13 Apr 2024 16:58:52 -0500 Subject: [PATCH 02/80] changes associated with ts-plugin --- .../extension-browser.webpack.config.js | 2 +- .../src/tsServer/serverProcess.browser.ts | 12 ++++++++- .../web/src/logging.ts | 2 +- .../web/src/serverHost.ts | 25 +++++-------------- .../web/src/webServer.ts | 3 ++- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/extensions/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js index 86733dfd1ee307..ca45aa8169ea70 100644 --- a/extensions/typescript-language-features/extension-browser.webpack.config.js +++ b/extensions/typescript-language-features/extension-browser.webpack.config.js @@ -55,7 +55,7 @@ export default [withBrowserDefaults({ })) ], }), - ], + ], }), withBrowserDefaults({ context: import.meta.dirname, entry: { diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index 5adf1866112561..ba9dfecdf7b79b 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -155,7 +155,17 @@ class WorkerServerProcess implements TsServerProcess { } write(serverRequest: Proto.Request): void { - this._tsserver.postMessage(serverRequest); + 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..672b766860b464 100644 --- a/extensions/typescript-language-features/web/src/logging.ts +++ b/extensions/typescript-language-features/web/src/logging.ts @@ -20,7 +20,7 @@ export class Logger { constructor(logLevel: LogLevel | undefined) { const doLog = typeof logLevel === 'undefined' ? (_message: string) => { } - : (message: string) => { postMessage({ type: 'log', body: message }); }; + : (message: string) => { console.log(message); }; this.tsLogger = { close: () => { }, diff --git a/extensions/typescript-language-features/web/src/serverHost.ts b/extensions/typescript-language-features/web/src/serverHost.ts index fdc617868b5c82..3731939a4c1b4d 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 }; @@ -87,27 +88,13 @@ 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.`) }; - } - - 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..663c54673c20e7 100644 --- a/extensions/typescript-language-features/web/src/webServer.ts +++ b/extensions/typescript-language-features/web/src/webServer.ts @@ -26,7 +26,7 @@ async function initializeSession( extensionUri: URI, ports: { tsserver: MessagePort; sync: MessagePort; watcher: MessagePort }, ): Promise { - const logLevel = parseLogLevel(findArgument(args, '--logVerbosity')); + const logLevel = parseLogLevel(findArgument(args, '--logVerbosity') ?? 'verbose'); const logger = new Logger(logLevel); const modeOrUnknown = parseServerMode(args); @@ -72,6 +72,7 @@ function parseSessionOptions(args: readonly string[], serverMode: ts.LanguageSer let hasInitialized = false; const listener = async (e: any) => { + console.log('>>> received message: ' + Object.keys(e)); if (!hasInitialized) { hasInitialized = true; if ('args' in e.data) { From 59dbc0eeaa6be121b03fd42d0e115b1d50e1424e Mon Sep 17 00:00:00 2001 From: Jhonny Date: Mon, 29 Apr 2024 15:58:33 -0500 Subject: [PATCH 03/80] Update configuration for enhanced compatibility with membrane-web --- extensions/shared.webpack.config.mjs | 9 +++++++-- extensions/tsconfig.base.json | 2 +- .../typescript-language-features/web/src/webServer.ts | 1 - 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index f54499dc2272cd..e67ddcd980ab99 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -122,9 +122,14 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo }, }, module: { - rules: [{ + rules: [ + { + test: /\.d\.ts$/, + type: 'asset/source' + }, + { test: /\.ts$/, - exclude: /node_modules/, + exclude: /node_modules|typings\/.*\.d\.ts$/, // Exclude node_modules and .d.ts files in the typings folder use: [ { // configure TypeScript loader: 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/web/src/webServer.ts b/extensions/typescript-language-features/web/src/webServer.ts index 663c54673c20e7..bd35a1cb04b82e 100644 --- a/extensions/typescript-language-features/web/src/webServer.ts +++ b/extensions/typescript-language-features/web/src/webServer.ts @@ -72,7 +72,6 @@ function parseSessionOptions(args: readonly string[], serverMode: ts.LanguageSer let hasInitialized = false; const listener = async (e: any) => { - console.log('>>> received message: ' + Object.keys(e)); if (!hasInitialized) { hasInitialized = true; if ('args' in e.data) { From 39abc452527699c06345dd7e923e13523fcdcfac Mon Sep 17 00:00:00 2001 From: Jhonny Date: Wed, 29 May 2024 08:53:17 -0500 Subject: [PATCH 04/80] optional transfer port --- .../src/tsServer/serverProcess.browser.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index ba9dfecdf7b79b..660f937d26d98a 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -59,7 +59,6 @@ export class WorkerServerProcessFactory implements TsServerProcessFactory { } class WorkerServerProcess implements TsServerProcess { - private static idPool = 0; private readonly id = WorkerServerProcess.idPool++; @@ -156,7 +155,7 @@ class WorkerServerProcess implements TsServerProcess { write(serverRequest: Proto.Request): void { const { arguments: args } = serverRequest; - const transfer = args.configuration?.transfer; + const transfer = args?.configuration?.transfer; const request = { ...serverRequest, arguments: args, From 4de9cc584687dad182cfbed1198da52b1dbab39c Mon Sep 17 00:00:00 2001 From: Jhonny Date: Wed, 29 May 2024 09:10:34 -0500 Subject: [PATCH 05/80] add webpack config --- extensions/shared.webpack.config.mjs | 54 +++++++++++++++------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index e67ddcd980ab99..5298cd4c002f51 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -109,12 +109,16 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo 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 resolve: { + alias: { + './platform/vscode': path.resolve(__dirname, 'membrane-ts-plugin/src/platform/browser.ts'), + }, 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'), + 'path': require.resolve('path-browserify'), + 'util': require.resolve('util/') }, extensionAlias: { // this is needed to resolve dynamic imports that now require the .js extension @@ -123,34 +127,34 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo }, module: { rules: [ - { + { 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: additionalOptions.configFile }), - } - }, - { - loader: path.resolve(import.meta.dirname, 'mangle-loader.js'), - options: { - configFile: path.join(extConfig.context, additionalOptions?.configFile ?? 'tsconfig.json') + 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: 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, From b4734b2f171b452f5d11991bcb2a30afe32c6471 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 4 Jun 2024 11:32:39 -0400 Subject: [PATCH 06/80] Formatting and additional comments --- extensions/shared.webpack.config.mjs | 6 ++++-- .../src/tsServer/serverProcess.browser.ts | 4 ++-- extensions/typescript-language-features/web/src/logging.ts | 4 +++- .../typescript-language-features/web/src/serverHost.ts | 3 +++ .../typescript-language-features/web/src/webServer.ts | 3 ++- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index 5298cd4c002f51..d89a8901fac1f5 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -117,6 +117,7 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo fallback: { 'os': require.resolve('os-browserify'), 'events': require.resolve('events'), + // 'os': require.resolve('os-browserify'), 'path': require.resolve('path-browserify'), 'util': require.resolve('util/') }, @@ -127,6 +128,7 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo }, module: { rules: [ + // MEMBRANE: see ts-plugin/src/membraneLib.ts { test: /\.d\.ts$/, type: 'asset/source' @@ -141,11 +143,11 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo loader: 'ts-loader', options: { ...tsLoaderOptions, - // ...(additionalOptions ? {} : { configFile: additionalOptions.configFile }), + ...(additionalOptions?.configFile ? { configFile: additionalOptions.configFile } : {}), } }, { - loader: path.resolve(import.meta.dirname, 'mangle-loader.js'), + loader: path.resolve(__dirname, 'mangle-loader.js'), options: { configFile: path.join(extConfig.context, additionalOptions?.configFile ?? 'tsconfig.json') }, diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index 660f937d26d98a..41c21872d5bbcf 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -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); @@ -154,6 +152,8 @@ class WorkerServerProcess implements TsServerProcess { } write(serverRequest: Proto.Request): void { + // 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 = { diff --git a/extensions/typescript-language-features/web/src/logging.ts b/extensions/typescript-language-features/web/src/logging.ts index 672b766860b464..38523e87afd76e 100644 --- a/extensions/typescript-language-features/web/src/logging.ts +++ b/extensions/typescript-language-features/web/src/logging.ts @@ -20,7 +20,9 @@ export class Logger { constructor(logLevel: LogLevel | undefined) { const doLog = typeof logLevel === 'undefined' ? (_message: string) => { } - : (message: string) => { console.log(message); }; + : (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/serverHost.ts b/extensions/typescript-language-features/web/src/serverHost.ts index 3731939a4c1b4d..7eb664b37ca641 100644 --- a/extensions/typescript-language-features/web/src/serverHost.ts +++ b/extensions/typescript-language-features/web/src/serverHost.ts @@ -88,6 +88,9 @@ function createServerHost( this.clearTimeout(timeoutId); }, importPlugin: async (root, moduleName) => { + if (moduleName !== 'membrane-ts-plugin') { + throw new Error('Only the Membrane TS plugin is supported'); + } const scriptPath = combinePaths(root, moduleName); try { // Dynamically import the script using the constructed path diff --git a/extensions/typescript-language-features/web/src/webServer.ts b/extensions/typescript-language-features/web/src/webServer.ts index bd35a1cb04b82e..525aa2ba3df532 100644 --- a/extensions/typescript-language-features/web/src/webServer.ts +++ b/extensions/typescript-language-features/web/src/webServer.ts @@ -26,7 +26,8 @@ async function initializeSession( extensionUri: URI, ports: { tsserver: MessagePort; sync: MessagePort; watcher: MessagePort }, ): Promise { - const logLevel = parseLogLevel(findArgument(args, '--logVerbosity') ?? 'verbose'); + // 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); From 5a9021ec99a9668939d5f39fc8160213a12f69f7 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 4 Jun 2024 11:38:24 -0400 Subject: [PATCH 07/80] Fix scheme mixup in FileSystemWatcher In FileSystemWatcher, if a "created" event fired for `tmpfs:///project/` It would fire the event for files sharing the same prefix but different scheme. In our case: `memfs:///project/package.json` This caused an assertion in typescript-language-features breaking intellisense. I also added a new test but it doesn't test this particular fix. --- src/vs/base/test/common/glob.test.ts | 6 ++++++ .../api/common/extHostFileSystemEventService.ts | 14 +++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) 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/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); } } From 65fe98fba102f429b9c25a28d7d08f6eb2db4646 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 4 Jun 2024 11:38:52 -0400 Subject: [PATCH 08/80] Add missing `ts-nul-authority` in `fromResource` --- .../typescript-language-features/web/src/pathMapper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/web/src/pathMapper.ts b/extensions/typescript-language-features/web/src/pathMapper.ts index dbe9aada758f03..07fda2a5997ce3 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) { From 1cc67ead741846846ab4782a8b2119f784e55ab8 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 11 Jun 2024 11:31:54 -0400 Subject: [PATCH 09/80] Use a workspace in memfs --- src/vs/code/browser/workbench/workbench.ts | 43 +++++++++------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index a9e28b28060812..3432c824e9d910 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -57,37 +57,28 @@ class SecretStorageProvider implements ISecretStorageProvider { } // Revive URIs in additionalBuiltinExtensions if present - if (Array.isArray(config.additionalBuiltinExtensions)) { - config = { - ...config, - additionalBuiltinExtensions: config.additionalBuiltinExtensions.map((ext: unknown) => URI.revive(ext as UriComponents)) - }; + let additionalBuiltinExtensions = config.additionalBuiltinExtensions; + if (Array.isArray(additionalBuiltinExtensions)) { + additionalBuiltinExtensions = additionalBuiltinExtensions.map((ext: unknown) => URI.revive(ext as UriComponents)); } - let workspace: IWorkspace | undefined; - if (config.folderUri) { - workspace = { folderUri: URI.revive(config.folderUri) }; - } else if (config.workspaceUri) { - workspace = { workspaceUri: URI.revive(config.workspaceUri) }; - } - - const domElement = mainWindow.document.body; - - if (workspace) { - const workspaceProvider: IWorkspaceProvider = { - workspace, + // Create final config object with all properties (avoiding readonly mutation) + const finalConfig: IWorkbenchConstructionOptions = { + ...config, + additionalBuiltinExtensions, + workspaceProvider: { + workspace: { workspaceUri: URI.parse('memfs:/membrane.code-workspace') }, + trusted: true, open: async ( - workspaceToOpen: IWorkspace, - options?: { reuse?: boolean; payload?: object } + _workspace: IWorkspace, + _options?: { reuse?: boolean; payload?: Record } ): Promise => { return true; }, - trusted: true, - }; - (config as unknown as { workspaceProvider?: IWorkspaceProvider }).workspaceProvider = workspaceProvider; - } + }, + secretStorageProvider: new SecretStorageProvider(), + }; - (config as unknown as { secretStorageProvider?: ISecretStorageProvider }).secretStorageProvider = new SecretStorageProvider(); - - create(domElement, config as IWorkbenchConstructionOptions); + const domElement = mainWindow.document.body; + create(domElement, finalConfig); })(); From 149787984750efd7911aecc434a97b4346703a2e Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 14 Jun 2024 21:46:03 -0400 Subject: [PATCH 10/80] Modifications required for CI (#2) * Update the web build to have a vscode-web and vscode-web-min step * Don't run git config commands in submodule * Fix plugin path to work as submodule of mnode * Add some extra context around postinstall changes * Fix platform path --- build/npm/postinstall.js | 7 +++++-- extensions/shared.webpack.config.mjs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) 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/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index d89a8901fac1f5..d68eac48d56cbe 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -110,7 +110,7 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo target: 'webworker', // extensions run in a webworker context resolve: { alias: { - './platform/vscode': path.resolve(__dirname, 'membrane-ts-plugin/src/platform/browser.ts'), + './platform/vscode': path.resolve(__dirname, '../../ts-plugin/src/platform/browser.ts'), }, mainFields: ['browser', 'module', 'main'], extensions: ['.ts', '.js'], // support ts-files and js-files From 8a23256770d406d5a44ae55ec151f22d91bb8c22 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Mon, 17 Jun 2024 22:21:07 -0400 Subject: [PATCH 11/80] Rename titles of File Explorer --- src/vs/code/browser/workbench/workbench.ts | 2 +- src/vs/workbench/contrib/files/browser/explorerViewlet.ts | 5 +++-- src/vs/workbench/contrib/files/browser/views/explorerView.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 3432c824e9d910..c1c8b2dd2fa362 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from '../../../base/common/uri.js'; import { IWorkbenchConstructionOptions, IWorkspace, - IWorkspaceProvider, + // IWorkspaceProvider, } from '../../../workbench/browser/web.api.js'; import { ISecretStorageProvider } from '../../../platform/secrets/common/secrets.js'; import { mainWindow } from '../../../base/browser/window.js'; diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index ecc6af48668561..2295ac9b39445b 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -256,7 +256,8 @@ const viewContainerRegistry = Registry.as(Extensions.Vi */ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewContainer({ 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,7 +266,7 @@ 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 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 { From f7180eef5dbac6dbf7d0110a3a3b6c1db9aa0492 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Wed, 19 Jun 2024 13:39:59 -0400 Subject: [PATCH 12/80] Fix build as a submodule (.git folder is hoisted) --- build/lib/git.js | 21 +++++++++++++++++++-- build/lib/git.ts | 18 +++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/build/lib/git.js b/build/lib/git.js index 30de97ed6e3699..79d61458164bc7 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -14,8 +14,25 @@ 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'); - const headPath = path_1.default.join(git, 'HEAD'); + // 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; + 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; try { head = fs_1.default.readFileSync(headPath, 'utf8').trim(); 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; From 5b471c4fb650bff366127bae966d9351c3ded8a3 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Thu, 20 Jun 2024 11:57:22 -0400 Subject: [PATCH 13/80] Fix our SecretStorageProvider - Don't throw when key is not a JSON - Don't throw when key is not found, return undefined instead. - Simplify a bit --- src/vs/code/browser/workbench/workbench.ts | 39 +++++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index c1c8b2dd2fa362..cd3745ca5632f9 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -14,15 +14,37 @@ import { ISecretStorageProvider } from '../../../platform/secrets/common/secrets import { mainWindow } from '../../../base/browser/window.js'; class SecretStorageProvider implements ISecretStorageProvider { - public type = 'persisted' as const; + public type: 'persisted'; + + constructor() { + this.type = 'persisted'; + } async get(key: string): Promise { + let extensionKey; try { - const secret = JSON.parse(key); - return localStorage.getItem(secret.key) ?? undefined; - } catch { - return undefined; + // Check if the key is for an extension + 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' + ) { + // HACK: Find the first key that matches the pattern of auth0 React + const localStorageKey = Object.keys(localStorage).find((key) => + key.includes('::default::openid') + ); + if (localStorageKey) { + const json = localStorage.getItem(localStorageKey); + const value = JSON.parse(json!); + return value.body.access_token; + } else { + throw new Error('Failed to read Membrane API token'); + } + } + return localStorage.getItem(key) ?? undefined; } async set(key: string, value: string): Promise { @@ -77,6 +99,13 @@ class SecretStorageProvider implements ISecretStorageProvider { }, }, secretStorageProvider: new SecretStorageProvider(), + defaultLayout: { + force: true, + views: [ + { id: 'membrane.explorer' }, + { id: 'membrane.logs' }, + ] + }, }; const domElement = mainWindow.document.body; From 5f16bc407de55ca2f80d74d51dafe240513fa438 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Mon, 24 Jun 2024 15:17:46 -0400 Subject: [PATCH 14/80] Rip out terminals from VSCode (#3) * Disable terminal functionality I removed most of the terminal-related contributions (terminal.contribution.ts), which includes: commands, the terminal tab, menu items, keybindings, and so on. Many terminal-related services are used by other services (e.g. the task service depends on the terminal service), so removing TerminalService would cause a cascade of "missing singleton" errors. Thankfully vscode uses DI everywhere, so I replaced the TerminalService with our own empty shell of a service. Same for a few related services. The empty shells are there to satisfy the dependency injection system. Note that there's still quite a bit of terminal-related code left that I think will end up in our build. We might be able to use a webpack plugin to for example, replace xterm.js with a dummy module. * Undo extension webpack change --- extensions/shared.webpack.config.mjs | 2 + src/tsconfig.json | 1 + .../mainThreadTerminalService.membrane.ts | 105 ++++++++ .../browser/terminal.contribution.membrane.ts | 23 ++ .../terminalInstanceService.membrane.ts | 55 ++++ .../terminalProfileService.membrane.ts | 95 +++++++ .../browser/terminalService.membrane.ts | 237 ++++++++++++++++++ 7 files changed, 518 insertions(+) create mode 100644 src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/terminal.contribution.membrane.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalProfileService.membrane.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index d68eac48d56cbe..ecd3dad49dd352 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -110,6 +110,8 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo target: 'webworker', // extensions run in a webworker context 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(__dirname, '../../ts-plugin/src/platform/browser.ts'), }, mainFields: ['browser', 'module', 'main'], 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/workbench/api/browser/mainThreadTerminalService.membrane.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts new file mode 100644 index 00000000000000..c96c4e3ec03062 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ExtHostTerminalIdentifier } from '../../api/common/extHost.protocol.js'; +import { extHostNamedCustomer } from '../../../services/extensions/common/extHostCustomers.js'; +import { IProcessProperty, IProcessReadyWindowsPty, ITerminalOutputMatch, ITerminalOutputMatcher } from '../../../../platform/terminal/common/terminal.js'; +import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from '../../../../platform/terminal/common/environmentVariable.js'; + +@extHostNamedCustomer(MainContext.MainThreadTerminalService) +export class MainThreadTerminalService implements MainThreadTerminalServiceShape { + constructor() { } + + public dispose(): void { } + + public async $createTerminal(extHostTerminalId: string, launchConfig: TerminalLaunchConfig): Promise { + throw new Error('Unsupported'); + } + + public async $show(id: ExtHostTerminalIdentifier, preserveFocus: boolean): Promise { + throw new Error('Unsupported'); + } + + public async $hide(id: ExtHostTerminalIdentifier): Promise { + throw new Error('Unsupported'); + } + + public async $dispose(id: ExtHostTerminalIdentifier): Promise { + throw new Error('Unsupported'); + } + + public async $sendText(id: ExtHostTerminalIdentifier, text: string, shouldExecute: boolean): Promise { + throw new Error('Unsupported'); + } + + public $sendProcessExit(terminalId: number, exitCode: number | undefined): void { + throw new Error('Unsupported'); + } + + public $startSendingDataEvents(): void { + throw new Error('Unsupported'); + } + + public $stopSendingDataEvents(): void { + throw new Error('Unsupported'); + } + + public $startSendingCommandEvents(): void { + throw new Error('Unsupported'); + } + + public $stopSendingCommandEvents(): void { + throw new Error('Unsupported'); + } + + public $startLinkProvider(): void { + throw new Error('Unsupported'); + } + + public $stopLinkProvider(): void { + throw new Error('Unsupported'); + } + + public $registerProcessSupport(isSupported: boolean): void { + // Empty + } + + public $registerProfileProvider(id: string, extensionIdentifier: string): void { + throw new Error('Unsupported'); + } + + public $unregisterProfileProvider(id: string): void { + throw new Error('Unsupported'); + } + + public async $registerQuickFixProvider(id: string, extensionId: string): Promise { + throw new Error('Unsupported'); + } + + public $unregisterQuickFixProvider(id: string): void { + throw new Error('Unsupported'); + } + + public $sendProcessData(terminalId: number, data: string): void { + throw new Error('Unsupported'); + } + + public $sendProcessReady(terminalId: number, pid: number, cwd: string, windowsPty: IProcessReadyWindowsPty | undefined): void { + throw new Error('Unsupported'); + } + + public $sendProcessProperty(terminalId: number, property: IProcessProperty): void { + throw new Error('Unsupported'); + } + + $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined, descriptionMap: ISerializableEnvironmentDescriptionMap): void { + throw new Error('Unsupported'); + } +} + +export function getOutputMatchForLines(lines: string[], outputMatcher: ITerminalOutputMatcher): ITerminalOutputMatch | undefined { + const match: RegExpMatchArray | null | undefined = lines.join('\n').match(outputMatcher.lineMatcher); + return match ? { regexMatch: match, outputLines: lines } : undefined; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.membrane.ts new file mode 100644 index 00000000000000..2a11ba15d02099 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.membrane.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITerminalProfileService } from '../common/terminal.js'; +import { TerminalService } from './terminalService.membrane.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { ITerminalEditorService, ITerminalGroupService, ITerminalInstanceService, ITerminalService } from './terminal.js'; +import { ITerminalLogService } from '../../../../platform/terminal/common/terminal.js'; +import { TerminalInstanceService } from './terminalInstanceService.membrane.js'; +import { TerminalEditorService } from './terminalEditorService.js'; +import { TerminalGroupService } from './terminalGroupService.js'; +import { TerminalProfileService } from './terminalProfileService.membrane.js'; +import { TerminalLogService } from '../../../../platform/terminal/common/terminalLogService.js'; + +// Register services +registerSingleton(ITerminalLogService, TerminalLogService, InstantiationType.Delayed); +registerSingleton(ITerminalService, TerminalService, InstantiationType.Delayed); +registerSingleton(ITerminalEditorService, TerminalEditorService, InstantiationType.Delayed); +registerSingleton(ITerminalGroupService, TerminalGroupService, InstantiationType.Delayed); +registerSingleton(ITerminalInstanceService, TerminalInstanceService, InstantiationType.Delayed); +registerSingleton(ITerminalProfileService, TerminalProfileService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts new file mode 100644 index 00000000000000..adc3fdff62654f --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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 { URI } from '../../../../base/common/uri.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; + +export class TerminalInstanceService extends Disposable implements ITerminalInstanceService { + declare _serviceBrand: undefined; + // private _configHelper: TerminalConfigHelper; + private _backendRegistration = new Map; resolve: () => void }>(); + + private readonly _onDidCreateInstance = this._register(new Emitter()); + get onDidCreateInstance(): Event { return this._onDidCreateInstance.event; } + + constructor( + // @IInstantiationService private readonly _instantiationService: IInstantiationService, + // @IContextKeyService private readonly _contextKeyService: IContextKeyService, + // @IWorkbenchEnvironmentService readonly _environmentService: IWorkbenchEnvironmentService, + ...args: unknown[] + ) { + super(); + } + + createInstance(profile: ITerminalProfile, target: TerminalLocation): ITerminalInstance; + createInstance(shellLaunchConfig: IShellLaunchConfig, target: TerminalLocation): ITerminalInstance; + createInstance(config: IShellLaunchConfig | ITerminalProfile, target: TerminalLocation): ITerminalInstance { + throw new Error('Unimplemented'); + } + + convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { + // Return empty shell launch config + return {}; + } + + async getBackend(remoteAuthority?: string): Promise { + return undefined; + } + + getRegisteredBackends(): IterableIterator { + return Registry.as(TerminalExtensions.Backend).backends.values(); + } + + didRegisterBackend(remoteAuthority?: string) { + this._backendRegistration.get(remoteAuthority)?.resolve(); + } +} + +registerSingleton(ITerminalInstanceService, TerminalInstanceService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.membrane.ts new file mode 100644 index 00000000000000..eaa2640e3ea35c --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.membrane.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { throttle } from '../../../../base/common/decorators.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'; + +/* + * Links TerminalService with TerminalProfileResolverService + * and keeps the available terminal profiles updated + */ +export class TerminalProfileService extends Disposable implements ITerminalProfileService { + declare _serviceBrand: undefined; + + get onDidChangeAvailableProfiles(): Event { throw new Error('Unsupported'); } + + get profilesReady(): Promise { + + throw new Error('Unsupported'); + } + get availableProfiles(): ITerminalProfile[] { + + throw new Error('Unsupported'); + } + get contributedProfiles(): IExtensionTerminalProfile[] { + + throw new Error('Unsupported'); + } + + 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 + ...args: unknown[] + ) { + super(); + } + + getDefaultProfileName(): string | undefined { + + throw new Error('Unsupported'); + } + + getDefaultProfile(os?: OperatingSystem): ITerminalProfile | undefined { + + throw new Error('Unsupported'); + } + + + @throttle(2000) + refreshAvailableProfiles(): void { + + throw new Error('Unsupported'); + } + + protected async _refreshAvailableProfilesNow(): Promise { + + throw new Error('Unsupported'); + } + + getContributedProfileProvider(extensionIdentifier: string, id: string): ITerminalProfileProvider | undefined { + + throw new Error('Unsupported'); + } + + async getPlatformKey(): Promise { + + throw new Error('Unsupported'); + } + + registerTerminalProfileProvider(extensionIdentifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable { + + throw new Error('Unsupported'); + } + + async registerContributedProfile(args: IRegisterContributedProfileArgs): Promise { + + throw new Error('Unsupported'); + } + + async getContributedDefaultProfile(shellLaunchConfig: IShellLaunchConfig): Promise { + + throw new Error('Unsupported'); + } + +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts new file mode 100644 index 00000000000000..2971c7be776527 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts @@ -0,0 +1,237 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { memoize } from '../../../../base/common/decorators.js'; +import { Event, IDynamicListEventMultiplexer } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { ICreateContributedTerminalProfileOptions, ITerminalBackend, ITerminalLaunchError, TerminalLocation, TerminalLocationString } from '../../../../platform/terminal/common/terminal.js'; +import { IEditableData } from '../../../common/views.js'; +import { ICreateTerminalOptions, IDetachedTerminalInstance, IDetachedXTermOptions, ITerminalConfigHelper, 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 { GroupIdentifier } from '../../../common/editor.js'; + +export class TerminalService extends Disposable implements ITerminalService { + readonly _serviceBrand: undefined; + + get isProcessSupportRegistered(): boolean { throw new Error('Unsupported'); } + + get connectionState(): TerminalConnectionState { throw new Error('Unsupported'); } + + // Never resolve + get whenConnected(): Promise { return new Promise(() => { }); } + + get restoredGroupCount(): number { throw new Error('Unsupported'); } + + get configHelper(): ITerminalConfigHelper { throw new Error('Unsupported'); } + get instances(): ITerminalInstance[] { + + throw new Error('Unsupported'); + } + get detachedInstances(): Iterable { + throw new Error('Unsupported'); + } + + + getReconnectedTerminals(_reconnectionOwner: string): ITerminalInstance[] | undefined { + return undefined; + } + + get defaultLocation(): TerminalLocation { return this.configHelper.config.defaultLocation === TerminalLocationString.Editor ? TerminalLocation.Editor : TerminalLocation.Panel; } + + get activeInstance(): ITerminalInstance | undefined { + return undefined; + } + + get onDidCreateInstance(): Event { throw new Error('Unsupported'); } + get onDidChangeInstanceDimensions(): Event { throw new Error('Unsupported'); } + get onDidRegisterProcessSupport(): Event { throw new Error('Unsupported'); } + get onDidChangeConnectionState(): Event { throw new Error('Unsupported'); } + get onDidRequestStartExtensionTerminal(): Event { throw new Error('Unsupported'); } + + // ITerminalInstanceHost events + get onDidDisposeInstance(): Event { throw new Error('Unsupported'); } + get onDidFocusInstance(): Event { throw new Error('Unsupported'); } + get onDidChangeActiveInstance(): Event { throw new Error('Unsupported'); } + get onDidChangeInstances(): Event { throw new Error('Unsupported'); } + get onDidChangeInstanceCapability(): Event { throw new Error('Unsupported'); } + + // Terminal view events + get onDidChangeActiveGroup(): Event { throw new Error('Unsupported'); } + + // Lazily initialized events that fire when the specified event fires on _any_ terminal + @memoize get onAnyInstanceDataInput() { return this.createOnInstanceEvent(e => e.onDidInputData); } + @memoize get onAnyInstanceIconChange() { return this.createOnInstanceEvent(e => e.onIconChanged); } + @memoize get onAnyInstanceMaximumDimensionsChange() { return this.createOnInstanceEvent(e => Event.map(e.onMaximumDimensionsChanged, () => e, e.store)); } + @memoize get onAnyInstancePrimaryStatusChange() { return this.createOnInstanceEvent(e => Event.map(e.statusList.onDidChangePrimaryStatus, () => e, e.store)); } + @memoize get onAnyInstanceProcessIdReady() { return this.createOnInstanceEvent(e => e.onProcessIdReady); } + @memoize get onAnyInstanceSelectionChange() { return this.createOnInstanceEvent(e => e.onDidChangeSelection); } + @memoize get onAnyInstanceTitleChange() { return this.createOnInstanceEvent(e => e.onTitleChanged); } + + 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, + // @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(); + + } + + async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise { + throw new Error('Unsupported'); + } + + async initializePrimaryBackend() { + throw new Error('Unsupported'); + } + + getPrimaryBackend(): ITerminalBackend | undefined { + throw new Error('Unsupported'); + } + + setActiveInstance(value: ITerminalInstance) { + throw new Error('Unsupported'); + } + + async focusActiveInstance(): Promise { + throw new Error('Unsupported'); + } + + async createContributedTerminalProfile(extensionIdentifier: string, id: string, options: ICreateContributedTerminalProfileOptions): Promise { + throw new Error('Unsupported'); + } + + async safeDisposeTerminal(instance: ITerminalInstance): Promise { + throw new Error('Unsupported'); + } + + async getActiveOrCreateInstance(options?: { acceptsInput?: boolean }): Promise { + throw new Error('Unsupported'); + } + + async revealActiveTerminal(preserveFocus?: boolean): Promise { + throw new Error('Unsupported'); + } + + setEditable(instance: ITerminalInstance, data?: IEditableData | null): void { + throw new Error('Unsupported'); + } + + isEditable(instance: ITerminalInstance | undefined): boolean { + throw new Error('Unsupported'); + } + + getEditableData(instance: ITerminalInstance): IEditableData | undefined { + throw new Error('Unsupported'); + } + + requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise { + throw new Error('Unsupported'); + } + + setNativeDelegate(nativeDelegate: ITerminalServiceNativeDelegate): void { + throw new Error('Unsupported'); + } + + refreshActiveGroup(): void { + throw new Error('Unsupported'); + } + + getInstanceFromId(terminalId: number): ITerminalInstance | undefined { + throw new Error('Unsupported'); + } + + getInstanceFromIndex(terminalIndex: number): ITerminalInstance { + throw new Error('Unsupported'); + } + + getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { + throw new Error('Unsupported'); + } + + isAttachedToTerminal(remoteTerm: IRemoteTerminalAttachTarget): boolean { + throw new Error('Unsupported'); + } + + moveToEditor(source: ITerminalInstance, group?: GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | AUX_WINDOW_GROUP_TYPE): void { + throw new Error('Unsupported'); + } + + moveIntoNewEditor(source: ITerminalInstance): void { + throw new Error('Unsupported'); + } + + async moveToTerminalView(source?: ITerminalInstance | URI, target?: ITerminalInstance, side?: 'before' | 'after'): Promise { + throw new Error('Unsupported'); + } + + registerProcessSupport(isSupported: boolean): void { + throw new Error('Unsupported'); + } + + protected async _showTerminalCloseConfirmation(singleTerminal?: boolean): Promise { + throw new Error('Unsupported'); + } + + getDefaultInstanceHost(): ITerminalInstanceHost { + throw new Error('Unsupported'); + } + + async getInstanceHost(location: ITerminalLocationOptions | undefined): Promise { + throw new Error('Unsupported'); + } + + async createTerminal(options?: ICreateTerminalOptions): Promise { + throw new Error('Unsupported'); + } + + async createDetachedTerminal(options: IDetachedXTermOptions): Promise { + throw new Error('Unsupported'); + } + + async resolveLocation(location?: ITerminalLocationOptions): Promise { + throw new Error('Unsupported'); + } + + async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { + throw new Error('Unsupported'); + } + + getEditingTerminal(): ITerminalInstance | undefined { + return undefined; + } + + setEditingTerminal(instance: ITerminalInstance | undefined) { + return undefined; + } + + createOnInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): Event { + throw new Error('Unsupported'); + } + + createOnInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): IDynamicListEventMultiplexer<{ instance: ITerminalInstance; data: K }> { + throw new Error('Unsupported'); + } +} From cd0a940f6a70ef05a1afe70e7abed244131012b2 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Thu, 20 Nov 2025 21:29:31 -0400 Subject: [PATCH 15/80] Fix previous cherry pick errors for rip out terminals. --- eslint.config.js | 13 ++++ .../mainThreadTerminalService.membrane.ts | 21 ++++-- .../terminalInstanceService.membrane.ts | 8 ++- .../browser/terminalService.membrane.ts | 67 ++++++++++++++----- 4 files changed, 83 insertions(+), 26 deletions(-) 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/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts index c96c4e3ec03062..e0f8823c282443 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts @@ -2,15 +2,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ExtHostTerminalIdentifier } from '../../api/common/extHost.protocol.js'; -import { extHostNamedCustomer } from '../../../services/extensions/common/extHostCustomers.js'; -import { IProcessProperty, IProcessReadyWindowsPty, ITerminalOutputMatch, ITerminalOutputMatcher } from '../../../../platform/terminal/common/terminal.js'; -import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from '../../../../platform/terminal/common/environmentVariable.js'; +import { MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ExtHostTerminalIdentifier } from '../common/extHost.protocol.js'; +import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; +import { IProcessProperty, IProcessReadyWindowsPty, ITerminalOutputMatch, ITerminalOutputMatcher } from '../../../platform/terminal/common/terminal.js'; +import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from '../../../platform/terminal/common/environmentVariable.js'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) export class MainThreadTerminalService implements MainThreadTerminalServiceShape { - constructor() { } + constructor(_extHostContext: IExtHostContext) { } public dispose(): void { } @@ -82,6 +81,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape throw new Error('Unsupported'); } + public $registerCompletionProvider(id: string, extensionId: string): void { + throw new Error('Unsupported'); + } + + public $unregisterCompletionProvider(id: string): void { + throw new Error('Unsupported'); + } + public $sendProcessData(terminalId: number, data: string): void { throw new Error('Unsupported'); } @@ -90,7 +97,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape throw new Error('Unsupported'); } - public $sendProcessProperty(terminalId: number, property: IProcessProperty): void { + public $sendProcessProperty(terminalId: number, property: IProcessProperty): void { throw new Error('Unsupported'); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts index adc3fdff62654f..13e75977d15734 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts @@ -19,6 +19,9 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst private readonly _onDidCreateInstance = this._register(new Emitter()); get onDidCreateInstance(): Event { return this._onDidCreateInstance.event; } + private readonly _onDidRegisterBackend = this._register(new Emitter()); + get onDidRegisterBackend(): Event { return this._onDidRegisterBackend.event; } + constructor( // @IInstantiationService private readonly _instantiationService: IInstantiationService, // @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -47,8 +50,9 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst return Registry.as(TerminalExtensions.Backend).backends.values(); } - didRegisterBackend(remoteAuthority?: string) { - this._backendRegistration.get(remoteAuthority)?.resolve(); + didRegisterBackend(backend: ITerminalBackend) { + this._backendRegistration.get(backend.remoteAuthority)?.resolve(); + this._onDidRegisterBackend.fire(backend); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts index 2971c7be776527..983d1ebab22868 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { memoize } from '../../../../base/common/decorators.js'; -import { Event, IDynamicListEventMultiplexer } from '../../../../base/common/event.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 { ICreateContributedTerminalProfileOptions, ITerminalBackend, ITerminalLaunchError, TerminalLocation, TerminalLocationString } from '../../../../platform/terminal/common/terminal.js'; +import { ICreateContributedTerminalProfileOptions, ITerminalBackend, ITerminalLaunchError, TerminalLocation } from '../../../../platform/terminal/common/terminal.js'; import { IEditableData } from '../../../common/views.js'; -import { ICreateTerminalOptions, IDetachedTerminalInstance, IDetachedXTermOptions, ITerminalConfigHelper, ITerminalGroup, ITerminalInstance, ITerminalInstanceHost, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, TerminalConnectionState } from './terminal.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'; @@ -27,21 +27,21 @@ export class TerminalService extends Disposable implements ITerminalService { get restoredGroupCount(): number { throw new Error('Unsupported'); } - get configHelper(): ITerminalConfigHelper { throw new Error('Unsupported'); } get instances(): ITerminalInstance[] { - + throw new Error('Unsupported'); + } + get foregroundInstances(): ITerminalInstance[] { throw new Error('Unsupported'); } get detachedInstances(): Iterable { throw new Error('Unsupported'); } - getReconnectedTerminals(_reconnectionOwner: string): ITerminalInstance[] | undefined { return undefined; } - get defaultLocation(): TerminalLocation { return this.configHelper.config.defaultLocation === TerminalLocationString.Editor ? TerminalLocation.Editor : TerminalLocation.Panel; } + get defaultLocation(): TerminalLocation { return TerminalLocation.Panel; } get activeInstance(): ITerminalInstance | undefined { return undefined; @@ -63,14 +63,17 @@ export class TerminalService extends Disposable implements ITerminalService { // Terminal view events get onDidChangeActiveGroup(): Event { throw new Error('Unsupported'); } - // Lazily initialized events that fire when the specified event fires on _any_ terminal - @memoize get onAnyInstanceDataInput() { return this.createOnInstanceEvent(e => e.onDidInputData); } - @memoize get onAnyInstanceIconChange() { return this.createOnInstanceEvent(e => e.onIconChanged); } - @memoize get onAnyInstanceMaximumDimensionsChange() { return this.createOnInstanceEvent(e => Event.map(e.onMaximumDimensionsChanged, () => e, e.store)); } - @memoize get onAnyInstancePrimaryStatusChange() { return this.createOnInstanceEvent(e => Event.map(e.statusList.onDidChangePrimaryStatus, () => e, e.store)); } - @memoize get onAnyInstanceProcessIdReady() { return this.createOnInstanceEvent(e => e.onProcessIdReady); } - @memoize get onAnyInstanceSelectionChange() { return this.createOnInstanceEvent(e => e.onDidChangeSelection); } - @memoize get onAnyInstanceTitleChange() { return this.createOnInstanceEvent(e => e.onTitleChanged); } + // Multiplexed events + @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 => 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, @@ -111,6 +114,10 @@ export class TerminalService extends Disposable implements ITerminalService { throw new Error('Unsupported'); } + async setNextCommandId(id: number, commandLine: string, commandId: string): Promise { + throw new Error('Unsupported'); + } + setActiveInstance(value: ITerminalInstance) { throw new Error('Unsupported'); } @@ -119,6 +126,10 @@ export class TerminalService extends Disposable implements ITerminalService { throw new Error('Unsupported'); } + focusInstance(instance: ITerminalInstance): void { + throw new Error('Unsupported'); + } + async createContributedTerminalProfile(extensionIdentifier: string, id: string, options: ICreateContributedTerminalProfileOptions): Promise { throw new Error('Unsupported'); } @@ -131,6 +142,14 @@ export class TerminalService extends Disposable implements ITerminalService { throw new Error('Unsupported'); } + async revealTerminal(source: ITerminalInstance, preserveFocus?: boolean): Promise { + throw new Error('Unsupported'); + } + + async showBackgroundTerminal(instance: ITerminalInstance, suppressSetActive?: boolean): Promise { + throw new Error('Unsupported'); + } + async revealActiveTerminal(preserveFocus?: boolean): Promise { throw new Error('Unsupported'); } @@ -207,6 +226,10 @@ export class TerminalService extends Disposable implements ITerminalService { throw new Error('Unsupported'); } + async createAndFocusTerminal(options?: ICreateTerminalOptions): Promise { + throw new Error('Unsupported'); + } + async createDetachedTerminal(options: IDetachedXTermOptions): Promise { throw new Error('Unsupported'); } @@ -227,11 +250,21 @@ export class TerminalService extends Disposable implements ITerminalService { return undefined; } - createOnInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): Event { - throw new Error('Unsupported'); + createOnInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): DynamicListEventMultiplexer { + // 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 }> { throw new Error('Unsupported'); } + + openResource(resource: URI): void { + throw new Error('Unsupported'); + } } From 995885f7abbe45bc6cf0bc9b102d19538e533119 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 26 Jun 2024 12:24:16 -0400 Subject: [PATCH 16/80] Don't open welcome page by default (#5) --- .../browser/gettingStarted.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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': { From 6f21a49f226a9e964923d02f4aaef205a6da75cb Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Tue, 2 Jul 2024 11:26:45 -0400 Subject: [PATCH 17/80] Add delete program logic (#7) --- .../files/browser/fileActions.contribution.ts | 24 ++++++++++--------- .../contrib/files/browser/fileCommands.ts | 16 +++++++++---- .../contrib/files/browser/fileConstants.ts | 2 +- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 6cd04336cfb456..24c541938360e6 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -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'; @@ -612,18 +612,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'; From 052dbc9092b7bb0c9a3e5a5b41e352c17493d87c Mon Sep 17 00:00:00 2001 From: Jhonny Date: Wed, 31 Jul 2024 11:29:20 -0500 Subject: [PATCH 18/80] Get the Membrane API token using the global authentication function. (#13) * Add getAuthToken * fix log error and remove token cache --- src/vs/code/browser/workbench/workbench.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index cd3745ca5632f9..4b84e0d43eaa30 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -15,9 +15,15 @@ import { mainWindow } from '../../../base/browser/window.js'; class SecretStorageProvider implements ISecretStorageProvider { public type: 'persisted'; + private getAuthToken: () => Promise; constructor() { this.type = 'persisted'; + // Capture the window function + this.getAuthToken = (window as any).globalIdeState.getAuthToken; + (window as any).globalIdeState.getAuthToken = () => { + throw new Error('This function is no longer available'); + }; } async get(key: string): Promise { @@ -32,16 +38,10 @@ class SecretStorageProvider implements ISecretStorageProvider { extensionKey?.extensionId === 'membrane.membrane' && extensionKey?.key === 'membraneApiToken' ) { - // HACK: Find the first key that matches the pattern of auth0 React - const localStorageKey = Object.keys(localStorage).find((key) => - key.includes('::default::openid') - ); - if (localStorageKey) { - const json = localStorage.getItem(localStorageKey); - const value = JSON.parse(json!); - return value.body.access_token; - } else { - throw new Error('Failed to read Membrane API token'); + try { + return await this.getAuthToken(); + } catch (error) { + throw new Error(`Failed to read Membrane API token: ${error}`); } } return localStorage.getItem(key) ?? undefined; From 49712d4fa2f4d5a88432b983ac38c30136525ade Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Fri, 21 Nov 2025 04:23:54 -0400 Subject: [PATCH 19/80] Fix vscode-extensions commands. --- build/lib/git.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/lib/git.js b/build/lib/git.js index 79d61458164bc7..9d00ee5e970d7e 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -15,16 +15,16 @@ const fs_1 = __importDefault(require("fs")); */ function getVersion(repo) { // 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); + const maybeGit = path_1.default.join(repo, '.git'); + const stat = fs_1.default.statSync(maybeGit); let git; if (stat.isFile()) { - const data = fs.readFileSync(maybeGit, 'utf8'); + 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.join(repo, gitdir); + git = path_1.default.join(repo, gitdir); } else if (stat.isDirectory()) { git = maybeGit; @@ -32,7 +32,7 @@ function getVersion(repo) { else { return undefined; } - const headPath = path.join(git, 'HEAD'); + const headPath = path_1.default.join(git, 'HEAD'); let head; try { head = fs_1.default.readFileSync(headPath, 'utf8').trim(); From 5bdb01aa1e5ee1ffd8052726bf62884943a1f42e Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Fri, 2 Aug 2024 17:09:18 -0400 Subject: [PATCH 20/80] Remove unused features (#15) * Remove unused features * Comment out unused code --- src/vs/workbench/workbench.common.main.ts | 28 ++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) 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'; From b18b629c8eb5ae816f89693600150d50c62da55f Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Fri, 21 Jun 2024 21:01:50 -0400 Subject: [PATCH 21/80] Move packages to the right. Our views always appear first. --- .../api/browser/viewsExtensionPoint.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index c0ff40a33138bd..315ec54bb260c9 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -322,15 +322,22 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return; } switch (key) { - case 'activitybar': - activityBarOrder = this.registerCustomViewContainers(value, description, activityBarOrder, existingViewContainers, ViewContainerLocation.Sidebar); + case 'activitybar': { + // MEMBRANE: ensure Navigator is first in activity bar (left sidebar) + const order = value?.some(v => v.id === 'membraneContainer') ? 0 : activityBarOrder; + activityBarOrder = this.registerCustomViewContainers(value, description, order, existingViewContainers, ViewContainerLocation.Sidebar); break; - case 'panel': - panelOrder = this.registerCustomViewContainers(value, description, panelOrder, existingViewContainers, ViewContainerLocation.Panel); + } + case 'panel': { + // MEMBRANE: ensure Logs are first in panel (bottom pane) + const order = description.identifier.value === 'membrane.membrane' ? 0 : panelOrder; + panelOrder = this.registerCustomViewContainers(value, description, order, existingViewContainers, ViewContainerLocation.Panel); break; - case 'secondarySidebar': + } + case 'secondarySidebar': { auxiliaryBarOrder = this.registerCustomViewContainers(value, description, auxiliaryBarOrder, existingViewContainers, ViewContainerLocation.AuxiliaryBar); break; + } } }); } @@ -387,10 +394,13 @@ class ViewsExtensionHandler implements IWorkbenchContribution { containers.forEach(descriptor => { const themeIcon = ThemeIcon.fromString(descriptor.icon); + // MEMBRANE: move Packages to auxiliary bar (right-side bar) + const overridenLocation = descriptor.id === 'membraneAuxContainer' ? ViewContainerLocation.AuxiliaryBar : location; + const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); const id = `workbench.view.extension.${descriptor.id}`; const title = descriptor.title || id; - const viewContainer = this.registerCustomViewContainer(id, title, icon, order++, extension.identifier, location); + const viewContainer = this.registerCustomViewContainer(id, title, icon, order++, extension.identifier, overridenLocation); // Move those views that belongs to this container if (existingViewContainers.length) { From 6efd09c95fb48b1f3ee841d65fea6a3020ea0e72 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Fri, 23 Aug 2024 09:12:51 -0500 Subject: [PATCH 22/80] disable vscode-node-modules --- .../web/src/pathMapper.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/web/src/pathMapper.ts b/extensions/typescript-language-features/web/src/pathMapper.ts index 07fda2a5997ce3..58262e318808bf 100644 --- a/extensions/typescript-language-features/web/src/pathMapper.ts +++ b/extensions/typescript-language-features/web/src/pathMapper.ts @@ -106,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}`, + }); } From 7d42eed1df81c40eb4db3892018328521ab7206b Mon Sep 17 00:00:00 2001 From: Pete Millspaugh <73900714+pmillspaugh@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:25:02 -0400 Subject: [PATCH 23/80] T-1223: add `membrane.io` as a trusted domain; add special logout redirect (#25) --- src/vs/workbench/browser/window.ts | 6 ++++++ src/vs/workbench/contrib/url/browser/trustedDomains.ts | 2 ++ 2 files changed, 8 insertions(+) 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/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 ?? [] ]; From bac0bdd61461e5e6e5f350babd632885e2aff3a9 Mon Sep 17 00:00:00 2001 From: Jhonny Date: Wed, 6 Nov 2024 09:46:11 -0500 Subject: [PATCH 24/80] Implement User Settings Sync with Membrane API (#23) * add membrane user settings * fix commandService usage * add save/update membrane gaze data * add try catch * new settings req and fix update command * move get/update data to runInTransaction * fix membrane module * change the proxy to use async code * fix the indexeddb proxy reqs * use getAllKeys to restore settings --- build/hygiene.mjs | 30 +++--- src/vs/base/browser/indexedDB.ts | 109 +++++++++++++++++++-- src/vs/code/browser/workbench/workbench.ts | 37 ++++++- 3 files changed, 151 insertions(+), 25 deletions(-) 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/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index c723dffdb206ef..7b9d19e75f6ee5 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -6,6 +6,7 @@ import { toErrorMessage } from '../common/errorMessage.js'; import { ErrorNoTelemetry, getErrorMessage } from '../common/errors.js'; import { mark } from '../common/performance.js'; +import { membraneApi } from '../../code/browser/workbench/workbench.js'; class MissingStoresError extends Error { constructor(readonly db: IDBDatabase) { @@ -20,6 +21,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 +120,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?: any; request: IDBRequest }> = []; + const storeProxy = new Proxy(transaction.objectStore(store), { + get(target: IDBObjectStore, prop: string): any { + return (...args: any[]) => { + const result = (target[prop as keyof IDBObjectStore] as Function).apply(target, args); + requests.push({ + prop, + key: prop === 'get' ? args[0] : args[1], + 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 +225,44 @@ export class IndexedDB { }).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1)); } } + + +function isMembraneKey(key: any): boolean { + const MEMBRANE_KEYS = [ + 'memento/webviewView.membrane.logs', + 'memento/webviewView.membrane.navigator', + 'memento/webviewView.membrane.packages', + '/User/settings.json' + ]; + return MEMBRANE_KEYS.includes(key); +} + +async function handleMembraneRequests(requests: Array<{ prop: string; key: string; value?: any }>): 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: any) => { + 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) + )); +} \ No newline at end of file diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 4b84e0d43eaa30..4d1e285a0fb549 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -13,9 +13,10 @@ import { import { ISecretStorageProvider } from '../../../platform/secrets/common/secrets.js'; import { mainWindow } from '../../../base/browser/window.js'; -class SecretStorageProvider implements ISecretStorageProvider { +export class SecretStorageProvider implements ISecretStorageProvider { public type: 'persisted'; - private getAuthToken: () => Promise; + private static instance: SecretStorageProvider; + public getAuthToken: () => Promise; constructor() { this.type = 'persisted'; @@ -26,6 +27,13 @@ class SecretStorageProvider implements ISecretStorageProvider { }; } + public static getInstance(): SecretStorageProvider { + if (!SecretStorageProvider.instance) { + SecretStorageProvider.instance = new SecretStorageProvider(); + } + return SecretStorageProvider.instance; + } + async get(key: string): Promise { let extensionKey; try { @@ -111,3 +119,28 @@ class SecretStorageProvider implements ISecretStorageProvider { const domElement = mainWindow.document.body; create(domElement, finalConfig); })(); + +export async function membraneApi( + method: 'GET' | 'POST', + path: `/${string}`, + body?: BodyInit +): Promise { + const isDev = window.location.hostname === 'localhost'; + const baseUrl = isDev ? 'http://localhost:8091' : 'https://api.membrane.io'; + + 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, + }); +} \ No newline at end of file From dbf78e8cedacc0d0499d4fdcbb878d719982d571 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Mon, 11 Nov 2024 17:37:16 -0500 Subject: [PATCH 25/80] Fix import cycle (#27) * Fix import cycle causing the build to fail * Add membrane.ts --- src/vs/base/browser/indexedDB.ts | 21 +-- src/vs/code/browser/workbench/membrane.ts | 85 +++++++++++ src/vs/code/browser/workbench/workbench.ts | 166 ++++++--------------- 3 files changed, 141 insertions(+), 131 deletions(-) create mode 100644 src/vs/code/browser/workbench/membrane.ts diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index 7b9d19e75f6ee5..dab17b981ec1d9 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -6,7 +6,8 @@ import { toErrorMessage } from '../common/errorMessage.js'; import { ErrorNoTelemetry, getErrorMessage } from '../common/errors.js'; import { mark } from '../common/performance.js'; -import { membraneApi } from '../../code/browser/workbench/workbench.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) { @@ -122,14 +123,14 @@ export class IndexedDB { this.pendingTransactions.push(transaction); // MEMBRANE // Proxy to save indexeddb reqs - const requests: Array<{ prop: string; key: string; value?: any; request: IDBRequest }> = []; + const requests: Array<{ prop: string; key: string; value?: unknown; request: IDBRequest }> = []; const storeProxy = new Proxy(transaction.objectStore(store), { - get(target: IDBObjectStore, prop: string): any { - return (...args: any[]) => { + 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], + key: (prop === 'get' ? args[0] : args[1]) as string, value: prop === 'put' ? args[0] : undefined, request: result }); @@ -227,17 +228,17 @@ export class IndexedDB { } -function isMembraneKey(key: any): boolean { +function isMembraneKey(key: unknown): boolean { const MEMBRANE_KEYS = [ 'memento/webviewView.membrane.logs', 'memento/webviewView.membrane.navigator', 'memento/webviewView.membrane.packages', '/User/settings.json' ]; - return MEMBRANE_KEYS.includes(key); + return typeof key === 'string' && MEMBRANE_KEYS.includes(key); } -async function handleMembraneRequests(requests: Array<{ prop: string; key: string; value?: any }>): Promise> { +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}`); @@ -249,7 +250,7 @@ async function handleMembraneRequests(requests: Array<{ prop: string; key: strin } }; - const handlePut = async (key: string, value: any) => { + const handlePut = async (key: string, value: unknown) => { try { const stringValue = value instanceof Uint8Array ? new TextDecoder().decode(value) @@ -265,4 +266,4 @@ async function handleMembraneRequests(requests: Array<{ prop: string; key: strin return Promise.all(requests.map(({ prop, key, value }) => prop === 'get' ? handleGet(key) : handlePut(key, value) )); -} \ No newline at end of file +} diff --git a/src/vs/code/browser/workbench/membrane.ts b/src/vs/code/browser/workbench/membrane.ts new file mode 100644 index 00000000000000..00d5fc28d0682e --- /dev/null +++ b/src/vs/code/browser/workbench/membrane.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { 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 { + // const isDev = window.location.hostname === 'localhost'; + const baseUrl = 'https://staging.membrane.io'; + + 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 4d1e285a0fb549..1003cd7453cc85 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -4,143 +4,67 @@ *--------------------------------------------------------------------------------------------*/ import { create } from '../../../workbench/workbench.web.main.internal.js'; -import { URI, UriComponents } from '../../../base/common/uri.js'; +import { URI } from '../../../base/common/uri.js'; import { IWorkbenchConstructionOptions, IWorkspace, // IWorkspaceProvider, } from '../../../workbench/browser/web.api.js'; -import { ISecretStorageProvider } from '../../../platform/secrets/common/secrets.js'; -import { mainWindow } from '../../../base/browser/window.js'; - -export class SecretStorageProvider implements ISecretStorageProvider { - public type: 'persisted'; - private static instance: SecretStorageProvider; - public getAuthToken: () => Promise; - - constructor() { - this.type = 'persisted'; - // Capture the window function - this.getAuthToken = (window as any).globalIdeState.getAuthToken; - (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 - 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); - } - - async keys(): Promise { - return []; - } -} +import { SecretStorageProvider } from '../workbench/membrane.js'; +declare const window: Window & { product?: Writeable }; +type Writeable = { -readonly [P in keyof T]: T[P] }; (async function () { - let config: IWorkbenchConstructionOptions & { - folderUri?: UriComponents; - workspaceUri?: UriComponents; - domElementId?: string; - } = {}; + // create workbench + let config: Writeable; - const windowProduct = (globalThis as { product?: unknown }).product; - if (windowProduct && typeof windowProduct === 'object') { - config = windowProduct as typeof config; + if (window.product) { + config = window.product; } else { const result = await fetch('/product.json'); - if (!result.ok) { - throw new Error(`Failed to fetch product.json: ${result.status}`); - } - config = await result.json() as typeof config; + config = await result.json(); } - // Revive URIs in additionalBuiltinExtensions if present - let additionalBuiltinExtensions = config.additionalBuiltinExtensions; - if (Array.isArray(additionalBuiltinExtensions)) { - additionalBuiltinExtensions = additionalBuiltinExtensions.map((ext: unknown) => URI.revive(ext as UriComponents)); - } + const isHttps = window.location.protocol === 'https:'; + const isDev = window.location.hostname === 'localhost'; + const extensionUrl = { + scheme: isHttps ? 'https' : 'http', + path: isDev ? '/membrane-dev' : '/membrane', + }; + + config.additionalBuiltinExtensions = [URI.revive(extensionUrl)]; - // Create final config object with all properties (avoiding readonly mutation) - const finalConfig: IWorkbenchConstructionOptions = { - ...config, - additionalBuiltinExtensions, - workspaceProvider: { - workspace: { workspaceUri: URI.parse('memfs:/membrane.code-workspace') }, - trusted: true, - open: async ( - _workspace: IWorkspace, - _options?: { reuse?: boolean; payload?: Record } - ): Promise => { - return true; - }, + 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', }, - secretStorageProvider: new SecretStorageProvider(), - defaultLayout: { - force: true, - views: [ - { id: 'membrane.explorer' }, - { id: 'membrane.logs' }, - ] + trusted: true, + open: async ( + _workspace: IWorkspace, + _options?: { reuse?: boolean; payload?: object } + ) => { + return true; }, }; - const domElement = mainWindow.document.body; - create(domElement, finalConfig); -})(); + config.secretStorageProvider = SecretStorageProvider.getInstance(); -export async function membraneApi( - method: 'GET' | 'POST', - path: `/${string}`, - body?: BodyInit -): Promise { - const isDev = window.location.hostname === 'localhost'; - const baseUrl = isDev ? 'http://localhost:8091' : 'https://api.membrane.io'; - - const secretProvider = SecretStorageProvider.getInstance(); - const token = await secretProvider.getAuthToken(); - - if (!token) { - throw new Error('Failed to retrieve Membrane API token'); - } + 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() }, + { + 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 ?? ''; + } + }]; - return await fetch(`${baseUrl}${path}`, { - method, - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - body, - }); -} \ No newline at end of file + // eslint-disable-next-line no-restricted-syntax + const domElement = document.body; + create(domElement, config); +})(); \ No newline at end of file From cbd94b446ff36872207879ee801e78a4b2b17548 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Wed, 13 Nov 2024 12:47:18 -0500 Subject: [PATCH 26/80] Use the same deterministic origin for all membrane webviews --- src/vs/workbench/contrib/webview/browser/webview.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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; From c9265d29cd34dc8a6434eb77bd3abeae30aba1c5 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Wed, 20 Nov 2024 17:48:48 -0500 Subject: [PATCH 27/80] Use correct api endpoint (#28) --- src/vs/code/browser/workbench/membrane.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/code/browser/workbench/membrane.ts b/src/vs/code/browser/workbench/membrane.ts index 00d5fc28d0682e..a882641d73a1e4 100644 --- a/src/vs/code/browser/workbench/membrane.ts +++ b/src/vs/code/browser/workbench/membrane.ts @@ -64,8 +64,7 @@ export async function membraneApi( path: `/${string}`, body?: BodyInit ): Promise { - // const isDev = window.location.hostname === 'localhost'; - const baseUrl = 'https://staging.membrane.io'; + const baseUrl = 'https://api.membrane.io'; const secretProvider = SecretStorageProvider.getInstance(); const token = await secretProvider.getAuthToken(); From fbe9a84c31c0d4e42812ddbaf9abc2905a57f452 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Fri, 22 Nov 2024 13:55:39 -0500 Subject: [PATCH 28/80] Exclude unused extensions (#31) * Exclude unused extensions from build * Formatting --- build/lib/extensions.js | 44 +++++++++++++++++++++++++++++++++++++++++ build/lib/extensions.ts | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/build/lib/extensions.js b/build/lib/extensions.js index e373688892474d..74761bd0d7557a 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,44 @@ 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-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-language-features', + 'xml', + 'yaml', +]; +function isAllowedInMembrane(name) { + return allowedExtensions.some(allowedExtensionName => allowedExtensionName === name); +} +exports.isAllowedInMembrane = isAllowedInMembrane; /** * 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 +450,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 +505,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..429276f1d7b7f9 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -373,6 +373,44 @@ 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-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-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 +465,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 +549,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; From aaa180f49f57cf0ab1a3f48662a76b95409a7269 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Fri, 22 Nov 2024 14:11:24 -0500 Subject: [PATCH 29/80] Make intellisense/ts-plugin more snappy (#32) --- .../src/tsServer/bufferSyncSupport.ts | 2 ++ src/vs/editor/contrib/codelens/browser/codelensController.ts | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) 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/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())); From 09ce744b99dd94da5d310e9f1d085c7569890183 Mon Sep 17 00:00:00 2001 From: Pete Millspaugh <73900714+pmillspaugh@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:01:19 -0500 Subject: [PATCH 30/80] T-1279: custom theme (#36) --- extensions/theme-defaults/package.json | 12 ++ extensions/theme-defaults/package.nls.json | 2 + .../theme-defaults/themes/membrane_dark.json | 53 +++++ .../theme-defaults/themes/membrane_light.json | 184 ++++++++++++++++++ .../parts/editor/editorGroupWatermark.ts | 65 ++++--- .../parts/editor/media/editorgroupview.css | 16 +- .../media/membrane-letterpress-dark.svg | 4 + .../media/membrane-letterpress-light.svg | 4 + 8 files changed, 307 insertions(+), 33 deletions(-) create mode 100644 extensions/theme-defaults/themes/membrane_dark.json create mode 100644 extensions/theme-defaults/themes/membrane_light.json create mode 100644 src/vs/workbench/browser/parts/editor/media/membrane-letterpress-dark.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/membrane-letterpress-light.svg 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..2b73bbcef3c6ab --- /dev/null +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -0,0 +1,53 @@ +{ + "$schema": "vscode://schemas/color-theme", + "name": "Membrane Dark", + "include": "./dark_modern.json", + "colors": { + "editorCodeLens.foreground": "#d3e" + }, + "tokenColors": [ + { + "scope": "comment", + "settings": { + "foreground": "#727272" + } + }, + { + "scope": "string", + "settings": { + "foreground": "#999999" + } + }, + { + "scope": "entity.name.function", + "settings": { + "foreground": "#ffefa9" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#bbbbbb" + } + }, + { + "scope": "variable", + "settings": { + "foreground": "#bbbbbb" + } + }, + { + "scope": "entity.name.type", + "settings": { + "foreground": "#7bc7b0" + } + }, + { + "scope": "constant.numeric", + "settings": { + "foreground": "#b8a0ff" + } + } + ], + "semanticHighlighting": true +} diff --git a/extensions/theme-defaults/themes/membrane_light.json b/extensions/theme-defaults/themes/membrane_light.json new file mode 100644 index 00000000000000..7e452f83a77643 --- /dev/null +++ b/extensions/theme-defaults/themes/membrane_light.json @@ -0,0 +1,184 @@ +{ + "$schema": "vscode://schemas/color-theme", + "name": "Membrane Light", + "include": "./light_modern.json", + "colors": { + "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.lineHighlightBorder": "#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": "#bbb", + "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" + }, + "tokenColors": [ + { + "scope": "comment", + "settings": { + "foreground": "#999" + } + }, + { + "scope": "string", + "settings": { + "foreground": "#117f5a" + } + }, + { + "scope": "entity.name.function", + "settings": { + "foreground": "#8600fb" + } + }, + { + "scope": "keyword.control", + "settings": { + "foreground": "#2020b1" + } + }, + { + "scope": "variable", + "settings": { + "foreground": "#0400ff" + } + }, + { + "scope": "entity.name.type", + "settings": { + "foreground": "#07781a" + } + }, + { + "scope": "constant.numeric", + "settings": { + "foreground": "#c600af" + } + } + ], + "semanticHighlighting": true +} diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index e3b1dd55c69a95..a8c3d1b0229ddd 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -7,11 +7,11 @@ 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 { 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 { 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 +27,22 @@ 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 quickAccess: WatermarkEntry = { text: localize('watermark.quickAccess', "Go to File"), id: 'workbench.action.quickOpen' }; +// const openFileNonMacOnly: WatermarkEntry = { text: localize('watermark.openFile', "Open File"), id: 'workbench.action.files.openFile', mac: false }; +// const openFolderNonMacOnly: WatermarkEntry = { text: localize('watermark.openFolder', "Open Folder"), id: 'workbench.action.files.openFolder', mac: false }; +// const openFileOrFolderMacOnly: WatermarkEntry = { text: localize('watermark.openFileFolder', "Open File or Folder"), id: 'workbench.action.files.openFileFolder', mac: true }; +// const openRecent: WatermarkEntry = { text: localize('watermark.openRecent', "Open Recent"), id: 'workbench.action.openRecent' }; +// const newUntitledFileMacOnly: WatermarkEntry = { text: localize('watermark.newUntitledFile', "New Untitled Text File"), id: 'workbench.action.files.newUntitledFile', mac: true }; +// 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: ContextKeyExpr.equals('terminalProcessSupported', true) }; +// const startDebugging: WatermarkEntry = { text: localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: ContextKeyExpr.equals('terminalProcessSupported', true) }; +// const toggleFullscreen: WatermarkEntry = { text: localize({ key: 'watermark.toggleFullscreen', comment: ['toggle is a verb here'] }, "Toggle Full Screen"), id: 'workbench.action.toggleFullScreen', when: ContextKeyExpr.equals('terminalProcessSupported', true).negate() }; +// const showSettings: WatermarkEntry = { text: localize('watermark.showSettings', "Show Settings"), id: 'workbench.action.openSettings', when: ContextKeyExpr.equals('terminalProcessSupported', true).negate() }; 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 + ]); const randomEmptyWindowEntries: WatermarkEntry[] = [ @@ -55,17 +50,31 @@ const randomEmptyWindowEntries: WatermarkEntry[] = [ ]; const workspaceEntries: WatermarkEntry[] = [ - showCommands, - gotoFile, - openChat + ]; const randomWorkspaceEntries: WatermarkEntry[] = [ - findInFiles, - startDebugging, - toggleTerminal, - openSettings, + ]; +// const noFolderEntries: WatermarkEntry[] = [ +// showCommands, +// openFileNonMacOnly, +// openFolderNonMacOnly, +// openFileOrFolderMacOnly, +// openRecent, +// newUntitledFileMacOnly +// ]; + +// const folderEntries: WatermarkEntry[] = [ +// showCommands, +// quickAccess, +// findInFiles, +// startDebugging, +// toggleTerminal, +// MEMBRANE: Unnecessary hotkey +// toggleFullscreen, +// showSettings +// ]; export class EditorGroupWatermark extends Disposable { diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index 0974b591f16c05..251666e18b70cb 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -50,18 +50,22 @@ } .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 +75,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 @@ + + + + From dc5a0f2699dc99ee7889633b1ed48d565ef807aa Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 21 Jan 2025 18:39:26 -0500 Subject: [PATCH 31/80] Add keybindings.json to the list of files we save in the DB (#45) --- src/vs/base/browser/indexedDB.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index dab17b981ec1d9..dc8a5045659182 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -232,8 +232,9 @@ function isMembraneKey(key: unknown): boolean { const MEMBRANE_KEYS = [ 'memento/webviewView.membrane.logs', 'memento/webviewView.membrane.navigator', - 'memento/webviewView.membrane.packages', - '/User/settings.json' + 'memento/webviewView.membrane.program', + '/User/settings.json', + '/User/keybindings.json' ]; return typeof key === 'string' && MEMBRANE_KEYS.includes(key); } From 6a86f3fe333e20179a86b1da640108f4fed635d3 Mon Sep 17 00:00:00 2001 From: Pete Millspaugh <73900714+pmillspaugh@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:12:01 -0500 Subject: [PATCH 32/80] Update API base URL to `ide.membrane.io/api` (#46) --- src/vs/code/browser/workbench/membrane.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/code/browser/workbench/membrane.ts b/src/vs/code/browser/workbench/membrane.ts index a882641d73a1e4..0aa40aab712e28 100644 --- a/src/vs/code/browser/workbench/membrane.ts +++ b/src/vs/code/browser/workbench/membrane.ts @@ -64,7 +64,9 @@ export async function membraneApi( path: `/${string}`, body?: BodyInit ): Promise { - const baseUrl = 'https://api.membrane.io'; + // 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(); From f8d78b539aa6b4a0608bd2cab136ead09c5a90cf Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Thu, 6 Feb 2025 16:20:24 -0500 Subject: [PATCH 33/80] Use zen-mode for dashboard. Improve zen-mode perf (#48) * Use custom zen-mode config for dashboard * Improve performance of switching to zen-mode by debouncing DOM changes * Add MEMBRANE comments --- src/vs/workbench/browser/layout.ts | 17 ++++++++++++++--- .../webviewPanel/browser/webviewEditor.ts | 18 +++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 7d0fcca60ea059..765fb9b6fc98b4 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1366,6 +1366,16 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const config = getZenModeConfiguration(this.configurationService); const zenModeExitInfo = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_EXIT_INFO); + // MEMBRANE: hard-coded settings when entering zen-mode for the dashboard + const activeEditor = this.mainPartEditorService.activeEditor; + if (activeEditor?.editorId === 'mainThreadWebview-membrane.dashboard') { + config.fullScreen = false; + config.centerLayout = false; + config.hideActivityBar = true; + config.showTabs = 'none'; + config.silentNotifications = false; + } + // Zen Mode Active if (this.isZenModeActive()) { @@ -1469,9 +1479,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.setSideBarHidden(false); } - if (!this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, true)) { - this.setActivityBarHidden(false); - } + // MEMBRANE: we don't use the activity bar so don't restore it + // if (!this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, true)) { + // this.setActivityBarHidden(false, true); + // } if (!this.stateModel.getRuntimeValue(LayoutStateKeys.STATUSBAR_HIDDEN, true)) { this.setStatusBarHidden(false); 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 { From 057ed8ed2b507973825c3899ace85449088adf22 Mon Sep 17 00:00:00 2001 From: Pete Millspaugh <73900714+pmillspaugh@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:32:13 -0500 Subject: [PATCH 34/80] T-1049: default color theme based on OS preference (#50) --- .../theme-defaults/themes/membrane_dark.json | 528 ++++++++++++++++- .../theme-defaults/themes/membrane_light.json | 546 +++++++++++++++++- .../themes/common/themeConfiguration.ts | 7 +- .../themes/common/workbenchThemeService.ts | 4 +- 4 files changed, 1055 insertions(+), 30 deletions(-) diff --git a/extensions/theme-defaults/themes/membrane_dark.json b/extensions/theme-defaults/themes/membrane_dark.json index 2b73bbcef3c6ab..60b25fef5eb432 100644 --- a/extensions/theme-defaults/themes/membrane_dark.json +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -3,51 +3,551 @@ "name": "Membrane Dark", "include": "./dark_modern.json", "colors": { - "editorCodeLens.foreground": "#d3e" + "editorCodeLens.foreground": "#845bff" }, + // Copied from light_vs.json and light_plus.json + // Made all colors #000 except comments and functions "tokenColors": [ { "scope": "comment", "settings": { - "foreground": "#727272" + "foreground": "#999" } }, { - "scope": "string", + "name": "Function declarations", + "scope": [ + "entity.name.function", + "support.function", + "support.constant.handlebars", + "source.powershell variable.other.member", + "entity.name.operator.custom-literal" + ], "settings": { - "foreground": "#999999" + "foreground": "#0f0" } }, { - "scope": "entity.name.function", + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" + ], "settings": { - "foreground": "#ffefa9" + "foreground": "#ececec" + } + }, + { + "scope": "emphasis", + "settings": { + "fontStyle": "italic" + } + }, + { + "scope": "strong", + "settings": { + "fontStyle": "bold" + } + }, + { + "scope": "header", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": "constant.language", + "settings": { + "foreground": "#ececec" + } + }, + { + "scope": [ + "constant.numeric", + "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": "#bbbbbb" + "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": "variable", + "scope": ["constant.character", "constant.other.option"], "settings": { - "foreground": "#bbbbbb" + "foreground": "#ececec" } }, { - "scope": "entity.name.type", + "scope": "constant.character.escape", "settings": { - "foreground": "#7bc7b0" + "foreground": "#ececec" } }, { - "scope": "constant.numeric", + "scope": "entity.name.label", "settings": { - "foreground": "#b8a0ff" + "foreground": "#ececec" } } ], - "semanticHighlighting": true + "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 index 7e452f83a77643..da4204f225de1f 100644 --- a/extensions/theme-defaults/themes/membrane_light.json +++ b/extensions/theme-defaults/themes/membrane_light.json @@ -136,6 +136,8 @@ "titleBar.inactiveForeground": "#8B949E", "widget.border": "#bbb" }, + // Copied from light_vs.json and light_plus.json + // Made all colors #000 except comments and functions "tokenColors": [ { "scope": "comment", @@ -144,41 +146,561 @@ } }, { - "scope": "string", + "name": "Function declarations", + "scope": [ + "entity.name.function", + "support.function", + "support.constant.handlebars", + "source.powershell variable.other.member", + "entity.name.operator.custom-literal" + ], "settings": { - "foreground": "#117f5a" + "foreground": "#63f" } }, { - "scope": "entity.name.function", + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" + ], "settings": { - "foreground": "#8600fb" + "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": "#2020b1" + "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": "variable", + "scope": ["constant.character", "constant.other.option"], "settings": { - "foreground": "#0400ff" + "foreground": "#000" } }, { - "scope": "entity.name.type", + "scope": "constant.character.escape", "settings": { - "foreground": "#07781a" + "foreground": "#000" } }, { - "scope": "constant.numeric", + "scope": "entity.name.label", "settings": { - "foreground": "#c600af" + "foreground": "#000" } } ], - "semanticHighlighting": true + "semanticHighlighting": true, + "semanticTokenColors": { + "newOperator": "#000", + "stringLiteral": "#000", + "customLiteral": "#000", + "numberLiteral": "#000" + } } diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 09187c4940ff6a..e70f465cc4d9bf 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -34,8 +34,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 +85,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', From cf2afef76d18caf0c08206ec56ee5a93eaf6347e Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Mon, 3 Mar 2025 17:44:02 -0500 Subject: [PATCH 35/80] Use `typescript` fork to get contextual typing of exports (#53) * Use our typescript fork to get contextual typing for exports * Upgrade typescript to only check for index.ts --- extensions/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" From c37b454d8b818803ae8680990f8c4ce0961945fa Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 4 Mar 2025 15:26:01 -0500 Subject: [PATCH 36/80] Tweak default themes (#55) --- .../theme-defaults/themes/membrane_dark.json | 167 ++++++++++++++++-- .../theme-defaults/themes/membrane_light.json | 167 ++++++++++++++++-- 2 files changed, 307 insertions(+), 27 deletions(-) diff --git a/extensions/theme-defaults/themes/membrane_dark.json b/extensions/theme-defaults/themes/membrane_dark.json index 60b25fef5eb432..c67ee1e61c4572 100644 --- a/extensions/theme-defaults/themes/membrane_dark.json +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -5,9 +5,8 @@ "colors": { "editorCodeLens.foreground": "#845bff" }, - // Copied from light_vs.json and light_plus.json - // Made all colors #000 except comments and functions "tokenColors": [ + // BEGIN STYLE RESET: makes everything white (actual colors are below) { "scope": "comment", "settings": { @@ -64,7 +63,6 @@ }, { "scope": [ - "constant.numeric", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent" @@ -86,7 +84,10 @@ } }, { - "scope": ["entity.name.tag.css", "entity.name.tag.less"], + "scope": [ + "entity.name.tag.css", + "entity.name.tag.less" + ], "settings": { "foreground": "#ececec" } @@ -195,7 +196,10 @@ } }, { - "scope": ["meta.preprocessor", "entity.name.function.preprocessor"], + "scope": [ + "meta.preprocessor", + "entity.name.function.preprocessor" + ], "settings": { "foreground": "#ececec" } @@ -237,13 +241,19 @@ } }, { - "scope": ["storage.modifier", "keyword.operator.noexcept"], + "scope": [ + "storage.modifier", + "keyword.operator.noexcept" + ], "settings": { "foreground": "#ececec" } }, { - "scope": ["string", "meta.embedded.assembly"], + "scope": [ + "string", + "meta.embedded.assembly" + ], "settings": { "foreground": "#ececec" } @@ -279,7 +289,9 @@ }, { "name": "Reset JavaScript string interpolation expression", - "scope": ["meta.template.expression"], + "scope": [ + "meta.template.expression" + ], "settings": { "foreground": "#ececec" } @@ -459,14 +471,19 @@ }, { "name": "Constants and enums", - "scope": ["variable.other.constant", "variable.other.enummember"], + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], "settings": { "foreground": "#ececec" } }, { "name": "Object keys, TS grammar specific", - "scope": ["meta.object-literal.key"], + "scope": [ + "meta.object-literal.key" + ], "settings": { "foreground": "#ececec" } @@ -513,7 +530,10 @@ } }, { - "scope": ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"], + "scope": [ + "keyword.operator.or.regexp", + "keyword.control.anchor.regexp" + ], "settings": { "foreground": "#ececec" } @@ -525,7 +545,10 @@ } }, { - "scope": ["constant.character", "constant.other.option"], + "scope": [ + "constant.character", + "constant.other.option" + ], "settings": { "foreground": "#ececec" } @@ -541,13 +564,129 @@ "settings": { "foreground": "#ececec" } - } + }, + // BEGIN STYLE + { + "scope": "constant.numeric", + "settings": { + "foreground": "#8dcb23", + } + }, + { + "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin.ts, punctuation.definition.string.end.ts, string.regexp.ts", + "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.ts, support.constant", + "settings": { + "foreground": "#eee", + } + }, + { + "scope": "keyword.control.export.ts, keyword.control.import.ts, keyword.control.switch.ts, storage.type.interface.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.conditional.ts, keyword.control.loop.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator.new, keyword.control.flow, keyword.control.from, storage.type.function.ts, keyword.control.as, keyword.operator.expression.typeof.ts, keyword.operator.expression.in.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.modifier.async.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.trycatch.ts", + "settings": { + "foreground": "#b62828", + } + }, + { + "scope": "entity.name.type.interface.ts, entity.name.type.ts", + "settings": { + "foreground": "#8fa1b3", + } + }, + { + "scope": "entity.name.function", + "settings": { + "foreground": "#eee", + } + }, + { + "scope": "meta.object-literal.member.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type.function.arrow.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "punctuation.separator.key-value.ts, punctuation.accessor.ts, keyword.operator.type.annotation.ts, punctuation.terminator.statement", + "settings": { + "foreground": "#808080", + } + }, + { + "scope": "punctuation.definition.binding-pattern.array.ts", + "settings": { + "foreground": "#808080", + } + }, ], "semanticHighlighting": true, "semanticTokenColors": { "newOperator": "#ececec", "stringLiteral": "#ececec", "customLiteral": "#ececec", - "numberLiteral": "#ececec" + "numberLiteral": "#ececec", + "*.async": "#ff0000" } } diff --git a/extensions/theme-defaults/themes/membrane_light.json b/extensions/theme-defaults/themes/membrane_light.json index da4204f225de1f..a628ee048c1b79 100644 --- a/extensions/theme-defaults/themes/membrane_light.json +++ b/extensions/theme-defaults/themes/membrane_light.json @@ -35,7 +35,7 @@ "editor.findMatchBorder": "#4e1", "editor.findMatchHighlightBackground": "#add6ff99", "editor.findMatchHighlightBorder": "#add6ff99", - "editor.lineHighlightBorder": "#ddd", + "editor.lineHighlightBackground": "#ddd", "editorCodeLens.foreground": "#3c3c3c", "editorGroup.border": "#bbb", "editorGroupHeader.tabsBackground": "#efefef", @@ -45,7 +45,8 @@ "editorGutter.modifiedBackground": "#43f", "editorIndentGuide.background1": "#bbb", "editorIndentGuide.activeBackground1": "#bbb", - "editorLineNumber.foreground": "#bbb", + "editorLineNumber.foreground": "#aaa", + "editorLineNumber.activeForeground": "#222", "editorOverviewRuler.border": "#efefef", "editorSuggestWidget.background": "#efefef", "editorWidget.background": "#efefef", @@ -139,6 +140,7 @@ // 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": { @@ -155,7 +157,7 @@ "entity.name.operator.custom-literal" ], "settings": { - "foreground": "#63f" + "foreground": "#000" } }, { @@ -193,6 +195,7 @@ "foreground": "#000" } }, + // BEGIN STYLE { "scope": [ "constant.numeric", @@ -324,7 +327,10 @@ } }, { - "scope": ["meta.preprocessor", "entity.name.function.preprocessor"], + "scope": [ + "meta.preprocessor", + "entity.name.function.preprocessor" + ], "settings": { "foreground": "#000" } @@ -360,13 +366,19 @@ } }, { - "scope": ["storage.modifier", "keyword.operator.noexcept"], + "scope": [ + "storage.modifier", + "keyword.operator.noexcept" + ], "settings": { "foreground": "#000" } }, { - "scope": ["string", "meta.embedded.assembly"], + "scope": [ + "string", + "meta.embedded.assembly" + ], "settings": { "foreground": "#000" } @@ -412,7 +424,9 @@ }, { "name": "Reset JavaScript string interpolation expression", - "scope": ["meta.template.expression"], + "scope": [ + "meta.template.expression" + ], "settings": { "foreground": "#000" } @@ -443,7 +457,9 @@ } }, { - "scope": ["support.type.property-name.json"], + "scope": [ + "support.type.property-name.json" + ], "settings": { "foreground": "#000" } @@ -612,14 +628,19 @@ }, { "name": "Constants and enums", - "scope": ["variable.other.constant", "variable.other.enummember"], + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], "settings": { "foreground": "#000" } }, { "name": "Object keys, TS grammar specific", - "scope": ["meta.object-literal.key"], + "scope": [ + "meta.object-literal.key" + ], "settings": { "foreground": "#000" } @@ -672,13 +693,19 @@ } }, { - "scope": ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"], + "scope": [ + "keyword.operator.or.regexp", + "keyword.control.anchor.regexp" + ], "settings": { "foreground": "#000" } }, { - "scope": ["constant.character", "constant.other.option"], + "scope": [ + "constant.character", + "constant.other.option" + ], "settings": { "foreground": "#000" } @@ -694,7 +721,121 @@ "settings": { "foreground": "#000" } - } + }, + { + "scope": "constant.numeric", + "settings": { + "foreground": "#6e9e1b", + } + }, + { + "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin.ts, punctuation.definition.string.end.ts, string.regexp.ts", + "settings": { + "foreground": "#e58714", + } + }, + { + "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": "#5a5a5a" + } + }, + { + "scope": "variable, variable.other, variable.other.readwrite, variable.other.object.ts, support.constant", + "settings": { + "foreground": "#333", + } + }, + { + "scope": "keyword.control.export.ts, keyword.control.import.ts, keyword.control.switch.ts, storage.type.interface.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.conditional.ts, keyword.control.loop.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.operator.new, keyword.control.flow, keyword.control.from, storage.type.function.ts, keyword.control.as, keyword.operator.expression.typeof.ts, keyword.operator.expression.in.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.modifier.async.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "keyword.control.trycatch.ts", + "settings": { + "foreground": "#b62828", + } + }, + { + "scope": "entity.name.type.interface.ts, entity.name.type.ts", + "settings": { + "foreground": "#8fa1b3", + } + }, + { + "scope": "entity.name.function", + "settings": { + "foreground": "#333", + } + }, + { + "scope": "meta.object-literal.member.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "storage.type.function.arrow.ts", + "settings": { + "foreground": "#68778f", + } + }, + { + "scope": "punctuation.separator.key-value.ts, punctuation.accessor.ts, keyword.operator.type.annotation.ts, punctuation.terminator.statement", + "settings": { + "foreground": "#808080", + } + }, + { + "scope": "punctuation.definition.binding-pattern.array.ts", + "settings": { + "foreground": "#808080", + } + }, ], "semanticHighlighting": true, "semanticTokenColors": { From 49ac81227823d2b3ba3b370b8876360bca737108 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 4 Mar 2025 15:52:52 -0500 Subject: [PATCH 37/80] Remove red syntax highlighting (#56) --- extensions/theme-defaults/themes/membrane_dark.json | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/theme-defaults/themes/membrane_dark.json b/extensions/theme-defaults/themes/membrane_dark.json index c67ee1e61c4572..a00e99281a0701 100644 --- a/extensions/theme-defaults/themes/membrane_dark.json +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -687,6 +687,5 @@ "stringLiteral": "#ececec", "customLiteral": "#ececec", "numberLiteral": "#ececec", - "*.async": "#ff0000" } } From a481d0c5eda163188a6bfad57d6e96ccd6615b7f Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Wed, 5 Mar 2025 23:09:13 -0500 Subject: [PATCH 38/80] Update theme to work correctly on .tsx files (#57) --- .../theme-defaults/themes/membrane_dark.json | 37 +++++++++------- .../theme-defaults/themes/membrane_light.json | 42 +++++++++++-------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/extensions/theme-defaults/themes/membrane_dark.json b/extensions/theme-defaults/themes/membrane_dark.json index a00e99281a0701..1391d46b6a76c6 100644 --- a/extensions/theme-defaults/themes/membrane_dark.json +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -573,7 +573,7 @@ } }, { - "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin.ts, punctuation.definition.string.end.ts, string.regexp.ts", + "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin, punctuation.definition.string.end, string.regexp", "settings": { "foreground": "#f89216", } @@ -597,19 +597,19 @@ } }, { - "scope": "variable, variable.other, variable.other.readwrite, variable.other.object.ts, support.constant", + "scope": "variable, variable.other, variable.other.readwrite, variable.other.object, support.constant", "settings": { "foreground": "#eee", } }, { - "scope": "keyword.control.export.ts, keyword.control.import.ts, keyword.control.switch.ts, storage.type.interface.ts", + "scope": "keyword.control.export, keyword.control.import, keyword.control.switch, storage.type.interface", "settings": { "foreground": "#68778f", } }, { - "scope": "keyword.control.conditional.ts, keyword.control.loop.ts", + "scope": "keyword.control.conditional, keyword.control.loop", "settings": { "foreground": "#68778f", } @@ -621,27 +621,27 @@ } }, { - "scope": "keyword.operator.new, keyword.control.flow, keyword.control.from, storage.type.function.ts, keyword.control.as, keyword.operator.expression.typeof.ts, keyword.operator.expression.in.ts", + "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.ts", + "scope": "storage.modifier.async", "settings": { "foreground": "#68778f", } }, { - "scope": "keyword.control.trycatch.ts", + "scope": "keyword.control.trycatch", "settings": { - "foreground": "#b62828", + "foreground": "#c02727", } }, { - "scope": "entity.name.type.interface.ts, entity.name.type.ts", + "scope": "entity.name.type.interface, entity.name.type", "settings": { - "foreground": "#8fa1b3", + "foreground": "#eee", } }, { @@ -651,35 +651,42 @@ } }, { - "scope": "meta.object-literal.member.ts", + "scope": "meta.object-literal.member", "settings": { "foreground": "#68778f", } }, { - "scope": "storage.type.ts", + "scope": "storage.type", "settings": { "foreground": "#68778f", } }, { - "scope": "storage.type.function.arrow.ts", + "scope": "storage.type.function.arrow", "settings": { "foreground": "#68778f", } }, { - "scope": "punctuation.separator.key-value.ts, punctuation.accessor.ts, keyword.operator.type.annotation.ts, punctuation.terminator.statement", + "scope": "punctuation.separator.key-value, punctuation.accessor, keyword.operator.type.annotation, punctuation.terminator.statement", "settings": { "foreground": "#808080", } }, { - "scope": "punctuation.definition.binding-pattern.array.ts", + "scope": "punctuation.definition.binding-pattern.array", "settings": { "foreground": "#808080", } }, + // JSX tags + { + "scope": "entity.name.tag, punctuation.definition.tag", + "settings": { + "foreground": "#68778F", + } + } ], "semanticHighlighting": true, "semanticTokenColors": { diff --git a/extensions/theme-defaults/themes/membrane_light.json b/extensions/theme-defaults/themes/membrane_light.json index a628ee048c1b79..67cbb19f4cca0f 100644 --- a/extensions/theme-defaults/themes/membrane_light.json +++ b/extensions/theme-defaults/themes/membrane_light.json @@ -195,7 +195,6 @@ "foreground": "#000" } }, - // BEGIN STYLE { "scope": [ "constant.numeric", @@ -722,6 +721,7 @@ "foreground": "#000" } }, + // BEGIN STYLE { "scope": "constant.numeric", "settings": { @@ -729,9 +729,9 @@ } }, { - "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin.ts, punctuation.definition.string.end.ts, string.regexp.ts", + "scope": "string.quoted.single, string.quoted.double, string.template, punctuation.definition.string.begin, punctuation.definition.string.end, string.regexp", "settings": { - "foreground": "#e58714", + "foreground": "#d17a11", } }, { @@ -749,23 +749,23 @@ { "scope": "comment, punctuation.definition.comment", "settings": { - "foreground": "#5a5a5a" + "foreground": "#9a9a9a" } }, { - "scope": "variable, variable.other, variable.other.readwrite, variable.other.object.ts, support.constant", + "scope": "variable, variable.other, variable.other.readwrite, variable.other.object, support.constant", "settings": { "foreground": "#333", } }, { - "scope": "keyword.control.export.ts, keyword.control.import.ts, keyword.control.switch.ts, storage.type.interface.ts", + "scope": "keyword.control.export, keyword.control.import, keyword.control.switch, storage.type.interface", "settings": { "foreground": "#68778f", } }, { - "scope": "keyword.control.conditional.ts, keyword.control.loop.ts", + "scope": "keyword.control.conditional, keyword.control.loop", "settings": { "foreground": "#68778f", } @@ -777,27 +777,27 @@ } }, { - "scope": "keyword.operator.new, keyword.control.flow, keyword.control.from, storage.type.function.ts, keyword.control.as, keyword.operator.expression.typeof.ts, keyword.operator.expression.in.ts", + "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.ts", + "scope": "storage.modifier.async", "settings": { "foreground": "#68778f", } }, { - "scope": "keyword.control.trycatch.ts", + "scope": "keyword.control.trycatch", "settings": { "foreground": "#b62828", } }, { - "scope": "entity.name.type.interface.ts, entity.name.type.ts", + "scope": "entity.name.type.interface, entity.name.type", "settings": { - "foreground": "#8fa1b3", + "foreground": "#333", } }, { @@ -807,35 +807,41 @@ } }, { - "scope": "meta.object-literal.member.ts", + "scope": "meta.object-literal.member", "settings": { "foreground": "#68778f", } }, { - "scope": "storage.type.ts", + "scope": "storage.type", "settings": { "foreground": "#68778f", } }, { - "scope": "storage.type.function.arrow.ts", + "scope": "storage.type.function.arrow", "settings": { "foreground": "#68778f", } }, { - "scope": "punctuation.separator.key-value.ts, punctuation.accessor.ts, keyword.operator.type.annotation.ts, punctuation.terminator.statement", + "scope": "punctuation.separator.key-value, punctuation.accessor, keyword.operator.type.annotation, punctuation.terminator.statement", "settings": { "foreground": "#808080", } }, { - "scope": "punctuation.definition.binding-pattern.array.ts", + "scope": "punctuation.definition.binding-pattern.array", "settings": { "foreground": "#808080", } - }, + }, // JSX tags + { + "scope": "entity.name.tag, punctuation.definition.tag", + "settings": { + "foreground": "#68778F", + } + } ], "semanticHighlighting": true, "semanticTokenColors": { From 61a75a6c3c1ddf0e08375898ee11ecdb7a3dad18 Mon Sep 17 00:00:00 2001 From: Jhonny Date: Tue, 29 Apr 2025 18:44:54 -0400 Subject: [PATCH 39/80] Ignore package.json created events. (#64) --- .../web/src/serverHost.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/web/src/serverHost.ts b/extensions/typescript-language-features/web/src/serverHost.ts index 7eb664b37ca641..cca4f6be23da60 100644 --- a/extensions/typescript-language-features/web/src/serverHost.ts +++ b/extensions/typescript-language-features/web/src/serverHost.ts @@ -73,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); From 183c24c0e6159a9e0d4666b94838fbdc31c2996a Mon Sep 17 00:00:00 2001 From: Jhonny Date: Tue, 29 Apr 2025 18:45:08 -0400 Subject: [PATCH 40/80] Disable extensions view and commands. (#65) * Disable extensions view and commands. * Add membrane comments * fix console.log error on Viewlet. --- .../browser/extensions.contribution.ts | 3044 ++++++++--------- 1 file changed, 1361 insertions(+), 1683 deletions(-) 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 From 432783d443b7750f470284c7c7985f04d69a3431 Mon Sep 17 00:00:00 2001 From: Thomas Seeley <104278845+iamseeley@users.noreply.github.com> Date: Fri, 2 May 2025 16:49:23 -0400 Subject: [PATCH 41/80] add sentry report issue command (#69) --- src/vs/code/browser/workbench/workbench.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 1003cd7453cc85..7cfac06c0d426e 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -56,6 +56,16 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; 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() }, + { + id: 'membrane.reportIssue', + handler: (cmdArgs) => { + (window as any).SENTRY_REPORT_ISSUE({ + source: cmdArgs.source, + message: cmdArgs.message, + context: cmdArgs.context + }); + }, + }, { id: 'membrane.getLaunchParams', handler: () => { // eslint-disable-next-line no-restricted-syntax From 4858fd4dcf6c41cc74ded10e09bce7b4f20dd78d Mon Sep 17 00:00:00 2001 From: Thomas Seeley <104278845+iamseeley@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:11:30 -0400 Subject: [PATCH 42/80] change sash hoverBorder color (#75) --- extensions/theme-defaults/themes/membrane_dark.json | 1 + extensions/theme-defaults/themes/membrane_light.json | 1 + 2 files changed, 2 insertions(+) diff --git a/extensions/theme-defaults/themes/membrane_dark.json b/extensions/theme-defaults/themes/membrane_dark.json index 1391d46b6a76c6..e7b6de3f0049e8 100644 --- a/extensions/theme-defaults/themes/membrane_dark.json +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -3,6 +3,7 @@ "name": "Membrane Dark", "include": "./dark_modern.json", "colors": { + "sash.hoverBorder": "#808080", "editorCodeLens.foreground": "#845bff" }, "tokenColors": [ diff --git a/extensions/theme-defaults/themes/membrane_light.json b/extensions/theme-defaults/themes/membrane_light.json index 67cbb19f4cca0f..876b923f385f8d 100644 --- a/extensions/theme-defaults/themes/membrane_light.json +++ b/extensions/theme-defaults/themes/membrane_light.json @@ -3,6 +3,7 @@ "name": "Membrane Light", "include": "./light_modern.json", "colors": { + "sash.hoverBorder": "#999", "diffEditor.unchangedRegionBackground": "#efefef", "activityBar.activeBorder": "#3c3c3c", "activityBar.background": "#ddd", From 0f3427869b1d62b72a7ea0d01eed0bad56941b18 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 25 Jul 2025 16:59:34 -0400 Subject: [PATCH 43/80] gaze to ide --- src/vs/base/browser/indexedDB.ts | 4 +- src/vs/code/browser/workbench/workbench.ts | 84 ++++++- .../api/browser/viewsExtensionPoint.ts | 215 ++++++++---------- .../parts/auxiliarybar/auxiliaryBarPart.ts | 3 +- .../workbench/browser/parts/compositePart.ts | 21 ++ .../browser/parts/paneCompositePart.ts | 3 + .../browser/parts/panel/panelPart.ts | 3 +- .../browser/parts/sidebar/sidebarPart.ts | 3 + .../themes/common/themeConfiguration.ts | 1 - 9 files changed, 205 insertions(+), 132 deletions(-) diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index dc8a5045659182..df10fed745709f 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -230,9 +230,7 @@ export class IndexedDB { function isMembraneKey(key: unknown): boolean { const MEMBRANE_KEYS = [ - 'memento/webviewView.membrane.logs', - 'memento/webviewView.membrane.navigator', - 'memento/webviewView.membrane.program', + 'memento/webviewView.membrane.main', '/User/settings.json', '/User/keybindings.json' ]; diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 7cfac06c0d426e..5077b444451f99 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -10,8 +10,17 @@ import { IWorkspace, // IWorkspaceProvider, } from '../../../workbench/browser/web.api.js'; +import { mainWindow } from '../../../base/browser/window.js'; import { SecretStorageProvider } from '../workbench/membrane.js'; -declare const window: Window & { product?: Writeable }; +declare const window: Window & { + product?: Writeable; + SENTRY_REPORT_ISSUE?: (params: { + source?: string; + message?: string; + context?: unknown; + }) => void; + vscodeTargetContainer?: HTMLElement | null; +}; type Writeable = { -readonly [P in keyof T]: T[P] }; (async function () { @@ -58,8 +67,9 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; { id: 'membrane.refreshPage', handler: () => window.location.reload() }, { id: 'membrane.reportIssue', - handler: (cmdArgs) => { - (window as any).SENTRY_REPORT_ISSUE({ + 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 @@ -67,14 +77,70 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; }, }, { - id: 'membrane.getLaunchParams', handler: () => { + id: 'membrane.extensionToGaze', + handler: (response) => { + console.log('Workbench: Extension to Next.js:', response); + window.dispatchEvent( + new CustomEvent('extensionToGaze', { + detail: response, + }), + ); + return true; + }, + }, + { + id: 'membrane.getLaunchParams', + handler: () => { + // Reading from existing HTML meta tag created outside of workbench // eslint-disable-next-line no-restricted-syntax - const meta = document.querySelector('meta[name="membrane-launch-params"]') as HTMLMetaElement; - return meta?.content ?? ''; + const metas = mainWindow.document.getElementsByTagName('meta'); + for (let i = 0; i < metas.length; i++) { + const meta = metas[i]; + if (meta.name === 'membrane-launch-params') { + return meta.content ?? ''; + } + } + return ''; + }, + }, + ]; + + // config.homeIndicator = { + // href: window.location.origin, + // icon: 'home', + // title: 'Membrane Home', + // }; + + window.addEventListener('gazeToExtension', async (event: Event) => { + const customEvent = event as CustomEvent; + console.log('Workbench: Event received:', customEvent.detail); + + try { + // Use VSCode's built-in command service + const { ICommandService } = await import( + '../../../platform/commands/common/commands.js' + ); + const { StandaloneServices } = await import( + '../../../editor/standalone/browser/standaloneServices.js' + ); + + const commandService = StandaloneServices.get(ICommandService); + if (commandService) { + await commandService.executeCommand( + 'membrane.gazeToExtension', + customEvent.detail, + ); + console.log('Workbench: Command executed successfully'); + } else { + console.error('Command service not available'); } - }]; + } catch (error) { + console.error('Failed to execute command:', error); + } + }); + + console.log('Workbench: Setup complete'); - // eslint-disable-next-line no-restricted-syntax - const domElement = document.body; + const domElement = window.vscodeTargetContainer || mainWindow.document.body; create(domElement, config); })(); \ No newline at end of file diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 315ec54bb260c9..6ce4101954acab 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -7,21 +7,25 @@ import { MarkdownString } from '../../../base/common/htmlContent.js'; import { IJSONSchema } from '../../../base/common/jsonSchema.js'; import { Disposable } from '../../../base/common/lifecycle.js'; import * as resources from '../../../base/common/resources.js'; -import { isFalsyOrWhitespace } from '../../../base/common/strings.js'; +// import { isFalsyOrWhitespace } from '../../../base/common/strings.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js'; -import { ExtensionIdentifier, ExtensionIdentifierSet, IExtensionDescription, IExtensionManifest } from '../../../platform/extensions/common/extensions.js'; +import { ExtensionIdentifier, ExtensionIdentifierSet, IExtensionManifest } from '../../../platform/extensions/common/extensions.js'; +// import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { SyncDescriptor } from '../../../platform/instantiation/common/descriptors.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../platform/log/common/log.js'; import { Registry } from '../../../platform/registry/common/platform.js'; import { PaneCompositeRegistry, Extensions as ViewletExtensions } from '../../browser/panecomposite.js'; import { CustomTreeView, TreeViewPane } from '../../browser/parts/views/treeView.js'; -import { ViewPaneContainer } from '../../browser/parts/views/viewPaneContainer.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'; @@ -280,7 +284,7 @@ const viewsExtensionPoint: IExtensionPoint = ExtensionsR } }); -const CUSTOM_VIEWS_START_ORDER = 7; +// const CUSTOM_VIEWS_START_ORDER = 7; class ViewsExtensionHandler implements IWorkbenchContribution { @@ -311,38 +315,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } private addCustomViewContainers(extensionPoints: readonly IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { - 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': { - // MEMBRANE: ensure Navigator is first in activity bar (left sidebar) - const order = value?.some(v => v.id === 'membraneContainer') ? 0 : activityBarOrder; - activityBarOrder = this.registerCustomViewContainers(value, description, order, existingViewContainers, ViewContainerLocation.Sidebar); - break; - } - case 'panel': { - // MEMBRANE: ensure Logs are first in panel (bottom pane) - const order = description.identifier.value === 'membrane.membrane' ? 0 : panelOrder; - panelOrder = this.registerCustomViewContainers(value, description, order, 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()); @@ -358,88 +333,94 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } } - private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean { - if (!Array.isArray(viewsContainersDescriptors)) { - collector.error(localize('viewcontainer requirearray', "views containers must be an array")); - return false; - } - - for (const descriptor of viewsContainersDescriptors) { - if (typeof descriptor.id !== 'string' && isFalsyOrWhitespace(descriptor.id)) { - collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); - return false; - } - if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) { - collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); - return false; - } - if (typeof descriptor.title !== 'string') { - collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title')); - return false; - } - if (typeof descriptor.icon !== 'string') { - collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon')); - return false; - } - if (isFalsyOrWhitespace(descriptor.title)) { - collector.warn(localize('requirenonemptystring', "property `{0}` is mandatory and must be of type `string` with non-empty value", 'title')); - return true; - } - } - - return true; - } - - private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number { - containers.forEach(descriptor => { - const themeIcon = ThemeIcon.fromString(descriptor.icon); - - // MEMBRANE: move Packages to auxiliary bar (right-side bar) - const overridenLocation = descriptor.id === 'membraneAuxContainer' ? ViewContainerLocation.AuxiliaryBar : location; - - const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); - const id = `workbench.view.extension.${descriptor.id}`; - const title = descriptor.title || id; - const viewContainer = this.registerCustomViewContainer(id, title, icon, order++, extension.identifier, overridenLocation); - - // Move those views that belongs to this container - if (existingViewContainers.length) { - const viewsToMove: IViewDescriptor[] = []; - for (const existingViewContainer of existingViewContainers) { - if (viewContainer !== existingViewContainer) { - viewsToMove.push(...this.viewsRegistry.getViews(existingViewContainer).filter(view => (view as ICustomViewDescriptor).originalContainerId === descriptor.id)); - } - } - if (viewsToMove.length) { - this.viewsRegistry.moveViews(viewsToMove, viewContainer); - } - } - }); - return order; - } - - private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer { - let viewContainer = this.viewContainersRegistry.get(id); - - if (!viewContainer) { - - viewContainer = this.viewContainersRegistry.registerViewContainer({ - id, - title: { value: title, original: title }, - extensionId, - ctorDescriptor: new SyncDescriptor( - ViewPaneContainer, - [id, { mergeViewWithContainerWhenSingleView: true }] - ), - hideIfEmpty: true, - order, - icon, - }, location); - - } - - return viewContainer; - } + // private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean { + // if (!Array.isArray(viewsContainersDescriptors)) { + // collector.error(localize('viewcontainer requirearray', "views containers must be an array")); + // return false; + // } + + // for (const descriptor of viewsContainersDescriptors) { + // if (typeof descriptor.id !== 'string' && isFalsyOrWhitespace(descriptor.id)) { + // collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + // return false; + // } + // if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) { + // collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + // return false; + // } + // if (typeof descriptor.title !== 'string') { + // collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title')); + // return false; + // } + // if (typeof descriptor.icon !== 'string') { + // collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon')); + // return false; + // } + // if (isFalsyOrWhitespace(descriptor.title)) { + // collector.warn(localize('requirenonemptystring', "property `{0}` is mandatory and must be of type `string` with non-empty value", 'title')); + // return true; + // } + // } + + // return true; + // } + + // private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number { + // containers.forEach(descriptor => { + // const themeIcon = ThemeIcon.fromString(descriptor.icon); + + // // MEMBRANE: make Membrane Navigator the default view container on the left sidebar + // const options: { isDefault?: boolean; doNotRegisterOpenCommand?: boolean } = {}; + // if (descriptor.id === 'membraneContainer') { + // options.isDefault = true; + // } + + // // MEMBRANE: move Program Overview to auxiliary bar (right-side bar) + // // const overridenLocation = descriptor.id === 'membraneAuxContainer' ? ViewContainerLocation.AuxiliaryBar : location; + + // const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); + // const id = `workbench.view.extension.${descriptor.id}`; + // const title = descriptor.title || id; + // const viewContainer = this.registerCustomViewContainer(id, title, icon, order++, extension.identifier, location, options); + + // // Move those views that belongs to this container + // if (existingViewContainers.length) { + // const viewsToMove: IViewDescriptor[] = []; + // for (const existingViewContainer of existingViewContainers) { + // if (viewContainer !== existingViewContainer) { + // viewsToMove.push(...this.viewsRegistry.getViews(existingViewContainer).filter(view => (view as ICustomViewDescriptor).originalContainerId === descriptor.id)); + // } + // } + // if (viewsToMove.length) { + // this.viewsRegistry.moveViews(viewsToMove, viewContainer); + // } + // } + // }); + // return order; + // } + + // private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation, options?: { isDefault?: boolean; doNotRegisterOpenCommand?: boolean }): ViewContainer { + // let viewContainer = this.viewContainersRegistry.get(id); + + // if (!viewContainer) { + + // viewContainer = this.viewContainersRegistry.registerViewContainer({ + // id, + // title: { value: title, original: title }, + // extensionId, + // ctorDescriptor: new SyncDescriptor( + // ViewPaneContainer, + // [id, { mergeViewWithContainerWhenSingleView: true }] + // ), + // hideIfEmpty: true, + // order, + // icon, + // }, location, options); + + // } + + // return viewContainer; + // } private deregisterCustomViewContainer(viewContainer: ViewContainer): void { this.viewContainersRegistry.deregisterViewContainer(viewContainer); diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index 6791fd41d89ced..d1123bbdeed43f 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -90,7 +90,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IExtensionService extensionService: IExtensionService, - @ICommandService private commandService: ICommandService, + @ICommandService protected override commandService: ICommandService, @IMenuService menuService: IMenuService, @IConfigurationService private readonly configurationService: IConfigurationService ) { @@ -116,6 +116,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..128bae0d6f25bb 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -36,6 +36,8 @@ import { IBaseActionViewItemOptions } from '../../../base/browser/ui/actionbar/a 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'; +import { ICommandService } from '../../../platform/commands/common/commands.js'; + export interface ICompositeTitleLabel { @@ -92,6 +94,8 @@ export abstract class CompositePart, + // MEMBRANE: include command service, instantiated in SidebarPart for back to Navigator button + protected readonly commandService: ICommandService, private readonly activeCompositeSettingsKey: string, private readonly defaultCompositeId: string, protected readonly nameForTelemetry: string, @@ -405,6 +409,23 @@ export abstract class CompositePart { + this.commandService.executeCommand('membrane.main.focus'); + }; + + titleArea.prepend(backToNavigator); + // Left Title Label this.titleLabel = this.createTitleLabel(titleArea); diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index a452575cf9e991..c3ea3edfdbcee2 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(registryId), + commandService, activePaneCompositeSettingsKey, viewDescriptorService.getDefaultViewContainer(location)?.id || '', nameForTelemetry, diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 585bc16cde2238..de3e3f726dccdc 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -78,7 +78,7 @@ export class PanelPart extends AbstractPaneCompositePart { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IExtensionService extensionService: IExtensionService, - @ICommandService private commandService: ICommandService, + @ICommandService protected override commandService: ICommandService, @IMenuService menuService: IMenuService, @IConfigurationService private configurationService: IConfigurationService ) { @@ -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..c3aa6b9a376cb9 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'; @@ -75,6 +76,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, @@ -99,6 +101,7 @@ export class SidebarPart extends AbstractPaneCompositePart { hoverService, instantiationService, themeService, + commandService, viewDescriptorService, contextKeyService, extensionService, diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index e70f465cc4d9bf..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'; From b192a4c590a56918055f4f34992529564a55a571 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Tue, 29 Jul 2025 13:05:40 -0400 Subject: [PATCH 44/80] don't show aux bar button don't show aux bar buttom in watermark --- .../parts/editor/editorGroupWatermark.ts | 90 +++++++++++-------- .../browser/parts/editor/editorTabsControl.ts | 1 + 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index a8c3d1b0229ddd..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 { 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 { 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'; @@ -29,20 +35,26 @@ interface WatermarkEntry { // MEMBRANE: rm watermark hotkeys // const showCommands: WatermarkEntry = { text: localize('watermark.showCommands', "Show All Commands"), id: 'workbench.action.showCommands' }; -// const quickAccess: WatermarkEntry = { text: localize('watermark.quickAccess', "Go to File"), id: 'workbench.action.quickOpen' }; -// const openFileNonMacOnly: WatermarkEntry = { text: localize('watermark.openFile', "Open File"), id: 'workbench.action.files.openFile', mac: false }; -// const openFolderNonMacOnly: WatermarkEntry = { text: localize('watermark.openFolder', "Open Folder"), id: 'workbench.action.files.openFolder', mac: false }; -// const openFileOrFolderMacOnly: WatermarkEntry = { text: localize('watermark.openFileFolder', "Open File or Folder"), id: 'workbench.action.files.openFileFolder', mac: true }; +// 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 newUntitledFileMacOnly: WatermarkEntry = { text: localize('watermark.newUntitledFile', "New Untitled Text File"), id: 'workbench.action.files.newUntitledFile', mac: true }; +// 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: ContextKeyExpr.equals('terminalProcessSupported', true) }; -// const startDebugging: WatermarkEntry = { text: localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: ContextKeyExpr.equals('terminalProcessSupported', true) }; -// const toggleFullscreen: WatermarkEntry = { text: localize({ key: 'watermark.toggleFullscreen', comment: ['toggle is a verb here'] }, "Toggle Full Screen"), id: 'workbench.action.toggleFullScreen', when: ContextKeyExpr.equals('terminalProcessSupported', true).negate() }; -// const showSettings: WatermarkEntry = { text: localize('watermark.showSettings', "Show Settings"), id: 'workbench.action.openSettings', when: ContextKeyExpr.equals('terminalProcessSupported', true).negate() }; +// 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 emptyWindowEntries: WatermarkEntry[] = coalesce([ +// 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 ]); const randomEmptyWindowEntries: WatermarkEntry[] = [ @@ -50,31 +62,17 @@ const randomEmptyWindowEntries: WatermarkEntry[] = [ ]; const workspaceEntries: WatermarkEntry[] = [ - + // showCommands, + // gotoFile, + // openChat ]; const randomWorkspaceEntries: WatermarkEntry[] = [ - + // findInFiles, + // startDebugging, + // toggleTerminal, + // openSettings, ]; -// const noFolderEntries: WatermarkEntry[] = [ -// showCommands, -// openFileNonMacOnly, -// openFolderNonMacOnly, -// openFileOrFolderMacOnly, -// openRecent, -// newUntitledFileMacOnly -// ]; - -// const folderEntries: WatermarkEntry[] = [ -// showCommands, -// quickAccess, -// findInFiles, -// startDebugging, -// toggleTerminal, -// MEMBRANE: Unnecessary hotkey -// toggleFullscreen, -// showSettings -// ]; export class EditorGroupWatermark extends Disposable { @@ -101,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; @@ -199,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..0fefd08c574b84 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -234,6 +234,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC })); } + private actionViewItemProvider(action: IAction, options: IBaseActionViewItemOptions): IActionViewItem | undefined { const activeEditorPane = this.groupView.activeEditorPane; From affd4dc74114d62c5500f5082e13b74e239c795c Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Thu, 31 Jul 2025 16:28:32 -0400 Subject: [PATCH 45/80] update wb commands --- src/vs/code/browser/workbench/workbench.ts | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 5077b444451f99..184801ae07c0a9 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -20,6 +20,8 @@ declare const window: Window & { context?: unknown; }) => void; vscodeTargetContainer?: HTMLElement | null; + completeInitialization?: () => void; + SENTRY_CAPTURE_EXCEPTION?: (error: Error) => void; }; type Writeable = { -readonly [P in keyof T]: T[P] }; @@ -65,6 +67,47 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; 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}`)); + } + }, + // For product tour, send coordinates of gaze rects to the web app + { + id: 'membrane.reportGazeRect', handler: (...args: unknown[]) => { + // cmdArgs { gaze_instance, rect_id, x, y, width, height } + const cmdArgs = args[0] as { gaze_instance: string; rect_id: string; x: number; y: number; width: number; height: number }; + window.dispatchEvent( + new CustomEvent('gaze:report-rect', { detail: cmdArgs }), + ); + }, + }, + { + id: 'membrane.reportOverlayRects', + handler: (...args: unknown[]) => { + // cmdArgs { gaze_instance, overlay_id, rects_json } + const cmdArgs = args[0] as { gaze_instance: string; overlay_id: string; rects_json: string }; + window.dispatchEvent( + new CustomEvent('gaze:report-overlay-rects', { detail: cmdArgs }), + ); + }, + }, + // 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[]) => { From 62f6a0ae0dac4e9c365af89356c9198e4cfa6410 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Thu, 31 Jul 2025 17:48:25 -0400 Subject: [PATCH 46/80] resolve warnings again? --- .../browser/parts/editor/editorTabsControl.ts | 14 +++++++++++++- .../browser/parts/editor/multiEditorTabsControl.ts | 7 +++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index 0fefd08c574b84..c65b3db6a70899 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -39,7 +39,7 @@ 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'; @@ -48,6 +48,12 @@ 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 'vs/workbench/common/contextkeys'; +// import { Codicon } from 'vs/base/common/codicons'; +// import { ICommandService } from 'vs/platform/commands/common/commands'; export class EditorCommandsContextActionRunner extends ActionRunner { constructor( @@ -140,9 +146,15 @@ 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); + // MEMBRANE: editorGroupsService is injected for future use + void this.editorGroupsService; + this.renderDropdownAsChildElement = false; const container = this.create(parent); diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 332fcb391911cf..c1b0668f1f6c5f 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 From af4c4411d54bff849b55f3a84f865c7d84fe213f Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:24:57 -0400 Subject: [PATCH 47/80] theme and style changes --- extensions/theme-defaults/themes/membrane_dark.json | 3 ++- .../workbench/browser/parts/editor/media/editorgroupview.css | 4 ++-- src/vs/workbench/browser/parts/media/compositepart.css | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/extensions/theme-defaults/themes/membrane_dark.json b/extensions/theme-defaults/themes/membrane_dark.json index e7b6de3f0049e8..19e8bd49390387 100644 --- a/extensions/theme-defaults/themes/membrane_dark.json +++ b/extensions/theme-defaults/themes/membrane_dark.json @@ -4,7 +4,8 @@ "include": "./dark_modern.json", "colors": { "sash.hoverBorder": "#808080", - "editorCodeLens.foreground": "#845bff" + "editorCodeLens.foreground": "#845bff", + "menu.selectionBackground": "#2a2a2a", }, "tokenColors": [ // BEGIN STYLE RESET: makes everything white (actual colors are below) diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index 251666e18b70cb..fe677e0439f989 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -63,8 +63,8 @@ /* 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 { 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; From 042be61157a4b3df6b5d19926ae66d62cecb26df Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:40:36 -0400 Subject: [PATCH 48/80] remove Membrane-specific layout customizations and add some new ones - Restore default activity bar and sidebar visibility settings - Remove forced view container ordering for Membrane Navigator - Restore standard panel registration and ordering - Set sidebar back to left side by default - Make search the active view container for the primary sidebar - Hide sidebar when a user logs out --- .../api/browser/viewsExtensionPoint.ts | 201 ++++++++++-------- src/vs/workbench/browser/layout.ts | 52 +++-- .../browser/workbench.contribution.ts | 3 +- 3 files changed, 143 insertions(+), 113 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 6ce4101954acab..afe59c1d022719 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -7,24 +7,23 @@ import { MarkdownString } from '../../../base/common/htmlContent.js'; import { IJSONSchema } from '../../../base/common/jsonSchema.js'; import { Disposable } from '../../../base/common/lifecycle.js'; import * as resources from '../../../base/common/resources.js'; -// import { isFalsyOrWhitespace } from '../../../base/common/strings.js'; +import { isFalsyOrWhitespace } from '../../../base/common/strings.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js'; import { ExtensionIdentifier, ExtensionIdentifierSet, IExtensionManifest } from '../../../platform/extensions/common/extensions.js'; -// import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { SyncDescriptor } from '../../../platform/instantiation/common/descriptors.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../platform/log/common/log.js'; import { Registry } from '../../../platform/registry/common/platform.js'; import { PaneCompositeRegistry, Extensions as ViewletExtensions } from '../../browser/panecomposite.js'; import { CustomTreeView, TreeViewPane } from '../../browser/parts/views/treeView.js'; -// import { ViewPaneContainer } from '../../browser/parts/views/viewPaneContainer.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 + 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'; @@ -284,7 +283,7 @@ const viewsExtensionPoint: IExtensionPoint = ExtensionsR } }); -// const CUSTOM_VIEWS_START_ORDER = 7; +const CUSTOM_VIEWS_START_ORDER = 7; class ViewsExtensionHandler implements IWorkbenchContribution { @@ -315,6 +314,26 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } private addCustomViewContainers(extensionPoints: readonly IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { + 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; + for (const { value, collector, description } of extensionPoints) { + Object.entries(value).forEach(([key, value]) => { + if (!this.isValidViewsContainer(value, collector)) { + return; + } + switch (key) { + case 'activitybar': { + activityBarOrder = this.registerCustomViewContainers(value, description, activityBarOrder, existingViewContainers, ViewContainerLocation.Sidebar); + break; + } + case 'panel': { + panelOrder = this.registerCustomViewContainers(value, description, panelOrder, existingViewContainers, ViewContainerLocation.Panel); + break; + } + } + }); + } } @@ -333,94 +352,85 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } } - // private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean { - // if (!Array.isArray(viewsContainersDescriptors)) { - // collector.error(localize('viewcontainer requirearray', "views containers must be an array")); - // return false; - // } - - // for (const descriptor of viewsContainersDescriptors) { - // if (typeof descriptor.id !== 'string' && isFalsyOrWhitespace(descriptor.id)) { - // collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); - // return false; - // } - // if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) { - // collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); - // return false; - // } - // if (typeof descriptor.title !== 'string') { - // collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title')); - // return false; - // } - // if (typeof descriptor.icon !== 'string') { - // collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon')); - // return false; - // } - // if (isFalsyOrWhitespace(descriptor.title)) { - // collector.warn(localize('requirenonemptystring', "property `{0}` is mandatory and must be of type `string` with non-empty value", 'title')); - // return true; - // } - // } - - // return true; - // } - - // private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[], location: ViewContainerLocation): number { - // containers.forEach(descriptor => { - // const themeIcon = ThemeIcon.fromString(descriptor.icon); - - // // MEMBRANE: make Membrane Navigator the default view container on the left sidebar - // const options: { isDefault?: boolean; doNotRegisterOpenCommand?: boolean } = {}; - // if (descriptor.id === 'membraneContainer') { - // options.isDefault = true; - // } - - // // MEMBRANE: move Program Overview to auxiliary bar (right-side bar) - // // const overridenLocation = descriptor.id === 'membraneAuxContainer' ? ViewContainerLocation.AuxiliaryBar : location; - - // const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); - // const id = `workbench.view.extension.${descriptor.id}`; - // const title = descriptor.title || id; - // const viewContainer = this.registerCustomViewContainer(id, title, icon, order++, extension.identifier, location, options); - - // // Move those views that belongs to this container - // if (existingViewContainers.length) { - // const viewsToMove: IViewDescriptor[] = []; - // for (const existingViewContainer of existingViewContainers) { - // if (viewContainer !== existingViewContainer) { - // viewsToMove.push(...this.viewsRegistry.getViews(existingViewContainer).filter(view => (view as ICustomViewDescriptor).originalContainerId === descriptor.id)); - // } - // } - // if (viewsToMove.length) { - // this.viewsRegistry.moveViews(viewsToMove, viewContainer); - // } - // } - // }); - // return order; - // } - - // private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation, options?: { isDefault?: boolean; doNotRegisterOpenCommand?: boolean }): ViewContainer { - // let viewContainer = this.viewContainersRegistry.get(id); - - // if (!viewContainer) { - - // viewContainer = this.viewContainersRegistry.registerViewContainer({ - // id, - // title: { value: title, original: title }, - // extensionId, - // ctorDescriptor: new SyncDescriptor( - // ViewPaneContainer, - // [id, { mergeViewWithContainerWhenSingleView: true }] - // ), - // hideIfEmpty: true, - // order, - // icon, - // }, location, options); - - // } - - // return viewContainer; - // } + private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean { + if (!Array.isArray(viewsContainersDescriptors)) { + collector.error(localize('viewcontainer requirearray', "views containers must be an array")); + return false; + } + + for (const descriptor of viewsContainersDescriptors) { + if (typeof descriptor.id !== 'string' && isFalsyOrWhitespace(descriptor.id)) { + collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + return false; + } + if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) { + collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + return false; + } + if (typeof descriptor.title !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title')); + return false; + } + if (typeof descriptor.icon !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon')); + return false; + } + if (isFalsyOrWhitespace(descriptor.title)) { + collector.warn(localize('requirenonemptystring', "property `{0}` is mandatory and must be of type `string` with non-empty value", 'title')); + return true; + } + } + + return true; + } + + 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; + const viewContainer = this.registerCustomViewContainer(id, title, icon, order++, extension.identifier, location); + + // Move those views that belongs to this container + if (existingViewContainers.length) { + const viewsToMove: IViewDescriptor[] = []; + for (const existingViewContainer of existingViewContainers) { + if (viewContainer !== existingViewContainer) { + viewsToMove.push(...this.viewsRegistry.getViews(existingViewContainer).filter(view => (view as ICustomViewDescriptor).originalContainerId === descriptor.id)); + } + } + if (viewsToMove.length) { + this.viewsRegistry.moveViews(viewsToMove, viewContainer); + } + } + }); + return order; + } + + + private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer { + let viewContainer = this.viewContainersRegistry.get(id); + + if (!viewContainer) { + + viewContainer = this.viewContainersRegistry.registerViewContainer({ + id, + title: { value: title, original: title }, + extensionId, + ctorDescriptor: new SyncDescriptor( + ViewPaneContainer, + [id, { mergeViewWithContainerWhenSingleView: true }] + ), + hideIfEmpty: true, + order, + icon, + }, location); + + } + + return viewContainer; + } private deregisterCustomViewContainer(viewContainer: ViewContainer): void { this.viewContainersRegistry.deregisterViewContainer(viewContainer); @@ -446,6 +456,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/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 765fb9b6fc98b4..69355a80baacbf 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -639,7 +639,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi resetLayout: Boolean(this.layoutOptions?.resetLayout) }); - this._register(this.stateModel.onDidChangeState(change => { + // MEMBRANE: hide activitybar, statusbar by default + // 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 +666,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } this.doUpdateLayoutConfiguration(); - })); + }); // Layout Initialization State const initialEditorsState = this.getInitialEditorsState(); @@ -702,6 +707,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); @@ -1366,16 +1374,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const config = getZenModeConfiguration(this.configurationService); const zenModeExitInfo = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_EXIT_INFO); - // MEMBRANE: hard-coded settings when entering zen-mode for the dashboard - const activeEditor = this.mainPartEditorService.activeEditor; - if (activeEditor?.editorId === 'mainThreadWebview-membrane.dashboard') { - config.fullScreen = false; - config.centerLayout = false; - config.hideActivityBar = true; - config.showTabs = 'none'; - config.silentNotifications = false; - } - // Zen Mode Active if (this.isZenModeActive()) { @@ -1479,10 +1477,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.setSideBarHidden(false); } - // MEMBRANE: we don't use the activity bar so don't restore it - // if (!this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, true)) { - // this.setActivityBarHidden(false, true); - // } + if (!this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, true)) { + this.setActivityBarHidden(false); + } if (!this.stateModel.getRuntimeValue(LayoutStateKeys.STATUSBAR_HIDDEN, true)) { this.setStatusBarHidden(false); @@ -2196,7 +2193,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); @@ -2749,8 +2761,10 @@ const LayoutStateKeys = { 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/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 4850bf415869c7..aac4ba97f98f8e 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -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': { From 28c94b7ed2fc6a9febdbdaf296d10ed7b03e1bac Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:41:35 -0400 Subject: [PATCH 49/80] remove command service dependency from composite parts - Remove command service constructor parameter from all part classes - Remove back to Navigator button functionality - Restore standard title area display for parts - Clean up unused import statements --- .../api/browser/viewsExtensionPoint.ts | 2 +- .../parts/auxiliarybar/auxiliaryBarPart.ts | 3 +-- .../workbench/browser/parts/compositePart.ts | 20 ------------------- .../browser/parts/paneCompositePart.ts | 1 - .../browser/parts/panel/panelPart.ts | 4 ++-- .../browser/parts/sidebar/sidebarPart.ts | 5 +++-- 6 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index afe59c1d022719..226012dd54c8ac 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -12,7 +12,7 @@ import { ThemeIcon } from '../../../base/common/themables.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js'; -import { ExtensionIdentifier, ExtensionIdentifierSet, IExtensionManifest } from '../../../platform/extensions/common/extensions.js'; +import { ExtensionIdentifier, ExtensionIdentifierSet, IExtensionDescription, IExtensionManifest } from '../../../platform/extensions/common/extensions.js'; import { SyncDescriptor } from '../../../platform/instantiation/common/descriptors.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index d1123bbdeed43f..4db15c48d16850 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -90,7 +90,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IExtensionService extensionService: IExtensionService, - @ICommandService protected override commandService: ICommandService, + @ICommandService private commandService: ICommandService, @IMenuService menuService: IMenuService, @IConfigurationService private readonly configurationService: IConfigurationService ) { @@ -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, diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 128bae0d6f25bb..16930cc3faa4c0 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -36,7 +36,6 @@ import { IBaseActionViewItemOptions } from '../../../base/browser/ui/actionbar/a 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'; -import { ICommandService } from '../../../platform/commands/common/commands.js'; export interface ICompositeTitleLabel { @@ -94,8 +93,6 @@ export abstract class CompositePart, - // MEMBRANE: include command service, instantiated in SidebarPart for back to Navigator button - protected readonly commandService: ICommandService, private readonly activeCompositeSettingsKey: string, private readonly defaultCompositeId: string, protected readonly nameForTelemetry: string, @@ -409,23 +406,6 @@ export abstract class CompositePart { - this.commandService.executeCommand('membrane.main.focus'); - }; - - titleArea.prepend(backToNavigator); - // Left Title Label this.titleLabel = this.createTitleLabel(titleArea); diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index c3ea3edfdbcee2..53d5469cc75e90 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -179,7 +179,6 @@ export abstract class AbstractPaneCompositePart extends CompositePart(registryId), - commandService, activePaneCompositeSettingsKey, viewDescriptorService.getDefaultViewContainer(location)?.id || '', nameForTelemetry, diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index de3e3f726dccdc..43ecbefa424933 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -78,13 +78,13 @@ export class PanelPart extends AbstractPaneCompositePart { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IExtensionService extensionService: IExtensionService, - @ICommandService protected override commandService: ICommandService, + @ICommandService private commandService: ICommandService, @IMenuService menuService: IMenuService, @IConfigurationService private configurationService: IConfigurationService ) { super( Parts.PANEL_PART, - { hasTitle: true, trailingSeparator: true }, + { hasTitle: true }, PanelPart.activePanelSettingsKey, ActivePanelContext.bindTo(contextKeyService), PanelFocusContext.bindTo(contextKeyService), diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index c3aa6b9a376cb9..7506460ef50271 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -40,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; @@ -85,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), From a024d85312f636904180a3534913a0fc99c0f430 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:44:07 -0400 Subject: [PATCH 50/80] simplify activity bar by removing menu options - Disable activity bar position submenu in context menu - Remove menu visibility toggle functionality - Simplify context menu to only show sidebar position toggle - Clean up unused imports (SubmenuAction, IMenuService, etc.) --- .../parts/activitybar/activitybarPart.ts | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 080b6871061ef5..ac68e944a55418 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -214,7 +214,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 +243,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 +368,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; } } From 8868de1e6d87adbfa06e7f983a7a62548d689e1f Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Fri, 21 Nov 2025 09:21:41 -0400 Subject: [PATCH 51/80] Fix activityBar warnings. --- .../browser/parts/activitybar/activitybarPart.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index ac68e944a55418..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'; From ecf1e57403c014c89a089ae8672ad637c87ef9d3 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:48:36 -0400 Subject: [PATCH 52/80] disable explorer view container and related actions - Prevent explorer view container from being registered - Remove 'Reveal in Explorer View' from editor context menu --- .../contrib/files/browser/explorerViewlet.ts | 21 ++++++++---- .../files/browser/fileActions.contribution.ts | 34 ++++++++++++------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 2295ac9b39445b..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,12 +254,13 @@ 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, // MEMBRANE: Call it "File Explorer" so it doesn't get confused with our "Graph Explorer" title: localize2('explore', "File Explorer"), @@ -270,8 +276,11 @@ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewC 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 24c541938360e6..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'; @@ -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', From 327dda0ae6cfcd19876f7ef85051da2069ab348a Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:49:59 -0400 Subject: [PATCH 53/80] add gaze modal state reporting - Add membrane.reportModalState command handler --- src/vs/code/browser/workbench/workbench.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 184801ae07c0a9..63e5e08453a188 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -98,6 +98,15 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; ); }, }, + { + id: 'membrane.reportModalState', + handler: (cmdArgs) => { + // cmdArgs { gaze_instance, element_id, has_modal } + window.dispatchEvent( + new CustomEvent('gaze:modal-state', { detail: cmdArgs }), + ); + }, + }, // For extension panels to bubble up errors { id: 'membrane.reportError', From 041696f05f31b8dcb076925a7b43d3a8a89c06b0 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:52:48 -0400 Subject: [PATCH 54/80] disable double-click to create untitled file in editor - Remove double-click listener for creating new untitled files - Remove related imports (DEFAULT_EDITOR_ASSOCIATION, EditorServiceImpl) - Clean up unused editor service dependencies --- .../browser/parts/editor/editorGroupView.ts | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index a1050be0194de8..a1271f4c8cbf43 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'; @@ -154,7 +162,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, @@ -378,19 +387,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 => { From c359ed7fcd8270499154158945cfcd624d5cf535 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:54:43 -0400 Subject: [PATCH 55/80] simplify preferences to show only user settings - Show only User settings tab - Remove workspace and folder settings from preferences UI --- .../contrib/preferences/browser/preferencesWidgets.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 { From 07ccde91cb240c74be4fe737aa46feda33e40c43 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:55:13 -0400 Subject: [PATCH 56/80] disable command palette - Remove command palette from editor context menu - Disable ShowAllCommandsAction registration - Clean up related imports and menu registrations --- .../browser/quickAccess.contribution.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) 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); From 71136a54c0ae0f7a154cdb68e4ea447e0c16bf7f Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:55:41 -0400 Subject: [PATCH 57/80] remove membrane webview key from indexedDB - Remove 'memento/webviewView.membrane.main' from membrane keys --- src/vs/base/browser/indexedDB.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index df10fed745709f..9cceea84eea977 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -230,7 +230,6 @@ export class IndexedDB { function isMembraneKey(key: unknown): boolean { const MEMBRANE_KEYS = [ - 'memento/webviewView.membrane.main', '/User/settings.json', '/User/keybindings.json' ]; From 05a384d29e9be1d7128f1b5506c2158f68229ccd Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 15 Aug 2025 08:55:24 -0400 Subject: [PATCH 58/80] move gazeToExtension event listener to web.main.ts out of workbench --- src/vs/code/browser/workbench/workbench.ts | 31 ---------------------- src/vs/workbench/browser/web.main.ts | 29 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 63e5e08453a188..58057841c93294 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -131,7 +131,6 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; { id: 'membrane.extensionToGaze', handler: (response) => { - console.log('Workbench: Extension to Next.js:', response); window.dispatchEvent( new CustomEvent('extensionToGaze', { detail: response, @@ -163,36 +162,6 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; // title: 'Membrane Home', // }; - window.addEventListener('gazeToExtension', async (event: Event) => { - const customEvent = event as CustomEvent; - console.log('Workbench: Event received:', customEvent.detail); - - try { - // Use VSCode's built-in command service - const { ICommandService } = await import( - '../../../platform/commands/common/commands.js' - ); - const { StandaloneServices } = await import( - '../../../editor/standalone/browser/standaloneServices.js' - ); - - const commandService = StandaloneServices.get(ICommandService); - if (commandService) { - await commandService.executeCommand( - 'membrane.gazeToExtension', - customEvent.detail, - ); - console.log('Workbench: Command executed successfully'); - } else { - console.error('Command service not available'); - } - } catch (error) { - console.error('Failed to execute command:', error); - } - }); - - console.log('Workbench: Setup complete'); - const domElement = window.vscodeTargetContainer || mainWindow.document.body; create(domElement, config); })(); \ No newline at end of file diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index b0db8565d0c48c..8d0c7ff6503a07 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -138,6 +138,35 @@ 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: Listen for event from Gaze.tsx. + // This bridges the communication from gaze to extension commands. + // eslint-disable-next-line no-restricted-globals + window.addEventListener('gazeToExtension', async (event: Event) => { + try { + const customEvent = event as CustomEvent; + await commandService.executeCommand('membrane.gazeToExtension', customEvent.detail); + } catch (error) { + console.error('Failed to execute command:', error); + } + }); + }); + // Return API Facade return instantiationService.invokeFunction(accessor => { const commandService = accessor.get(ICommandService); From b3748bdd3fc10246b816f5a5c6a5233e642e4a91 Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 15 Aug 2025 09:01:04 -0400 Subject: [PATCH 59/80] layout overrides for a cleaner editor and smooth transition from old layouts --- src/vs/editor/common/config/editorOptions.ts | 2 +- src/vs/workbench/browser/layout.ts | 17 +++++++++++++---- .../workbench/browser/workbench.contribution.ts | 15 +++++++-------- 3 files changed, 21 insertions(+), 13 deletions(-) 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/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 69355a80baacbf..556f77017a6a80 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -639,10 +639,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi resetLayout: Boolean(this.layoutOptions?.resetLayout) }); - // MEMBRANE: hide activitybar, statusbar by default + // 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.setRuntimeValue(LayoutStateKeys.STATUSBAR_HIDDEN, true); + + // MEMBRANE: More config updates to clean up the editor (overrides user settings) + // MEMBRANE: NOTE(gazetoide): As we replace VSCode settings and move to a simpler settings UI in gaze + // this list will probably grow. The goal is to cut down the settings to our few chosen options and force + // everything else to good defaults. + // MEMBRANE: For activity bar position, make sure it is set to 'top' + this.configurationService.updateValue('workbench.activityBar.location', 'top'); + this.configurationService.updateValue('editor.minimap.enabled', false); this.stateModel.onDidChangeState(change => { if (change.key === LayoutStateKeys.ACTIVITYBAR_HIDDEN) { @@ -2756,14 +2765,14 @@ 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 - // MEMBRANE: Do not hide activitybar by default + // MEMBRANE: Hide activitybar by default ACTIVITYBAR_HIDDEN: new RuntimeStateKey('activityBar.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, true, true), - // MEMBRANE: Do not hide sidebar by default + // MEMBRANE: 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), diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index aac4ba97f98f8e..b7e563793d66c8 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -591,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': { From 19c6624669f4ba2d84a90143ab5cc1b366b42e73 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 16 Sep 2025 01:30:07 -0400 Subject: [PATCH 60/80] Remove deprecated workbench commands since they are now handled directly in Gaze.tsx --- src/vs/code/browser/workbench/workbench.ts | 31 +--------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 58057841c93294..ae16d25dcabcb9 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -78,35 +78,6 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; window.dispatchEvent(new Event(`tour:${cmdArgs.trigger}`)); } }, - // For product tour, send coordinates of gaze rects to the web app - { - id: 'membrane.reportGazeRect', handler: (...args: unknown[]) => { - // cmdArgs { gaze_instance, rect_id, x, y, width, height } - const cmdArgs = args[0] as { gaze_instance: string; rect_id: string; x: number; y: number; width: number; height: number }; - window.dispatchEvent( - new CustomEvent('gaze:report-rect', { detail: cmdArgs }), - ); - }, - }, - { - id: 'membrane.reportOverlayRects', - handler: (...args: unknown[]) => { - // cmdArgs { gaze_instance, overlay_id, rects_json } - const cmdArgs = args[0] as { gaze_instance: string; overlay_id: string; rects_json: string }; - window.dispatchEvent( - new CustomEvent('gaze:report-overlay-rects', { detail: cmdArgs }), - ); - }, - }, - { - id: 'membrane.reportModalState', - handler: (cmdArgs) => { - // cmdArgs { gaze_instance, element_id, has_modal } - window.dispatchEvent( - new CustomEvent('gaze:modal-state', { detail: cmdArgs }), - ); - }, - }, // For extension panels to bubble up errors { id: 'membrane.reportError', @@ -164,4 +135,4 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; const domElement = window.vscodeTargetContainer || mainWindow.document.body; create(domElement, config); -})(); \ No newline at end of file +})(); From 21f24034c30df825b76647cf032690b118a21b68 Mon Sep 17 00:00:00 2001 From: Thomas Seeley <104278845+iamseeley@users.noreply.github.com> Date: Tue, 23 Sep 2025 10:49:01 -0400 Subject: [PATCH 61/80] Intercept VSCode dialogs and notifications to show in Gaze (#77) * hide lock group and editor actions positioning settings from menus * show editor actions in tab bar * add membrane dialog and notification handlers to intercept dialogs and notifications to display them with our own UI in gaze --- extensions/shared.webpack.config.mjs | 4 +- .../base/browser/ui/dialog/membraneDialog.ts | 169 +++++++++ .../parts/dialogs/dialog.web.contribution.ts | 7 +- .../parts/dialogs/membraneDialogHandler.ts | 163 +++++++++ .../parts/editor/editor.contribution.ts | 7 +- .../browser/parts/editor/editorTabsControl.ts | 1 + .../membraneNotificationsToasts.ts | 343 ++++++++++++++++++ src/vs/workbench/browser/web.main.ts | 18 + src/vs/workbench/browser/workbench.ts | 5 +- 9 files changed, 708 insertions(+), 9 deletions(-) create mode 100644 src/vs/base/browser/ui/dialog/membraneDialog.ts create mode 100644 src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts create mode 100644 src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index ecd3dad49dd352..0d9ccdfb3fdc10 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -112,7 +112,7 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo 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(__dirname, '../../ts-plugin/src/platform/browser.ts'), + './platform/vscode': path.resolve(import.meta.dirname, '../../ts-plugin/src/platform/browser.ts'), }, mainFields: ['browser', 'module', 'main'], extensions: ['.ts', '.js'], // support ts-files and js-files @@ -149,7 +149,7 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo } }, { - loader: path.resolve(__dirname, 'mangle-loader.js'), + loader: path.resolve(import.meta.dirname, 'mangle-loader.js'), options: { configFile: path.join(extConfig.context, additionalOptions?.configFile ?? 'tsconfig.json') }, 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..701fe313c7925e --- /dev/null +++ b/src/vs/base/browser/ui/dialog/membraneDialog.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +declare global { + interface Window { + membraneDialogResponseHandler?: (response: MembraneDialogResponse) => void; + } +} + +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 pendingDialogs = new Map void; + reject: (error: Error) => void; + }>(); + + private readonly dialogId: string; + + constructor( + _container: HTMLElement, + private message: string, + private buttons: string[] | undefined, + private readonly options: IDialogOptions + ) { + super(); + this.dialogId = generateUuid(); + + // Set up global response handler if not already done + if (!mainWindow.membraneDialogResponseHandler) { + mainWindow.membraneDialogResponseHandler = (response: MembraneDialogResponse) => { + const pending = MembraneDialog.pendingDialogs.get(response.id); + if (pending) { + MembraneDialog.pendingDialogs.delete(response.id); + pending.resolve({ + button: response.button, + checkboxChecked: response.checkboxChecked, + values: response.values + }); + } + }; + } + } + + async show(): Promise { + return new Promise((resolve, reject) => { + // Store the promise resolvers + MembraneDialog.pendingDialogs.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 to client via custom event (following your existing pattern) + mainWindow.dispatchEvent(new CustomEvent('membraneDialog', { + detail: dialogMessage + })); + + // Set up timeout to prevent hanging dialogs + setTimeout(() => { + const pending = MembraneDialog.pendingDialogs.get(this.dialogId); + if (pending) { + MembraneDialog.pendingDialogs.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.pendingDialogs.has(this.dialogId)) { + mainWindow.dispatchEvent(new CustomEvent('membraneDialogUpdate', { + detail: { + id: this.dialogId, + message: message + } + })); + } + } + + override dispose(): void { + super.dispose(); + // Clean up any pending dialog + MembraneDialog.pendingDialogs.delete(this.dialogId); + } +} 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..b3d35827a812f3 --- /dev/null +++ b/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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, IDialogStyles } 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'; +import type { IButtonStyles } from '../../../../base/browser/ui/button/button.js'; +import type { ICheckboxStyles } from '../../../../base/browser/ui/toggle/toggle.js'; +import type { IInputBoxStyles } from '../../../../base/browser/ui/inputbox/inputBox.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..0fdf1e9d7e7f14 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -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 }); @@ -424,8 +425,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDI 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 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/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index c65b3db6a70899..8c7a2ea247f515 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -273,6 +273,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC const editorActions = this.groupView.createEditorActions(this.editorActionsDisposables); this.editorActionsDisposables.add(editorActions.onDidChange(() => this.updateEditorActionsToolbar())); + // MEMBRANE: No longer hiding editor tab actions (e.g. Split Editor, More Actions). const editorActionsToolbar = assertReturnsDefined(this.editorActionsToolbar); const { primary, secondary } = this.prepareEditorActions(editorActions.actions); editorActionsToolbar.setActions(prepareActions(primary), prepareActions(secondary)); 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..496f8ca5cd73a4 --- /dev/null +++ b/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts @@ -0,0 +1,343 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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 { mainWindow } from '../../../../base/browser/window.js'; + +declare global { + interface Window { + membraneNotificationActionHandler?: (response: MembraneNotificationActionResponse) => void; + } +} + +interface MembraneNotificationActionResponse { + notificationId: string; + actionId?: string; + dismissed?: boolean; +} + +export class MembraneNotificationsToasts implements INotificationsToastController { + + private static readonly MAX_NOTIFICATIONS = 3; + private static readonly SPAM_PROTECTION = { + interval: 800, + limit: this.MAX_NOTIFICATIONS + }; + + private readonly _onDidChangeVisibility = 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 = new DisposableStore(); + + constructor( + private readonly model: INotificationsModel, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this.notificationsToastsVisibleContextKey = NotificationsToastsVisibleContext.bindTo(contextKeyService); + this.registerListeners(); + + // Set up handler for notification action responses from Gaze + mainWindow.membraneNotificationActionHandler = (response: MembraneNotificationActionResponse) => { + this.handleNotificationAction(response); + }; + } + + 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 + })) || [] + }; + + // Dispatch notification event + mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { + detail: { + 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 + mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { + detail: { + 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 + if (this.activeNotifications.size > 0) { + mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { + detail: { + type: 'focus', + target: 'first' + } + })); + return true; + } + return false; + } + + focusNext(): boolean { + if (this.activeNotifications.size > 0) { + mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { + detail: { + type: 'focus', + target: 'next' + } + })); + return true; + } + return false; + } + + focusPrevious(): boolean { + if (this.activeNotifications.size > 0) { + mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { + detail: { + type: 'focus', + target: 'previous' + } + })); + return true; + } + return false; + } + + focusFirst(): boolean { + if (this.activeNotifications.size > 0) { + mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { + detail: { + type: 'focus', + target: 'first' + } + })); + return true; + } + return false; + } + + focusLast(): boolean { + if (this.activeNotifications.size > 0) { + mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { + detail: { + 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; + } + } + } + + dispose(): void { + this.disposables.dispose(); + this._onDidChangeVisibility.dispose(); + + if (mainWindow.membraneNotificationActionHandler) { + delete mainWindow.membraneNotificationActionHandler; + } + } +} diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 8d0c7ff6503a07..a46a240853f61c 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -165,6 +165,24 @@ export class BrowserMain extends Disposable { console.error('Failed to execute command:', error); } }); + + // Listen for dialog and notification responses from Gaze.tsx. + mainWindow.addEventListener('gazeToWorkbench', (event: Event) => { + const customEvent = event as CustomEvent; + if (customEvent.detail.type === 'vscode_dialog_response') { + // Forward dialog response to the dialog system + const windowWithHandler = mainWindow as typeof mainWindow & { membraneDialogResponseHandler?: (response: unknown) => void }; + if (windowWithHandler.membraneDialogResponseHandler) { + windowWithHandler.membraneDialogResponseHandler(customEvent.detail.response); + } + } else if (customEvent.detail.type === 'vscode_notification_action') { + // Forward notification action to the notification system + const windowWithHandler = mainWindow as typeof mainWindow & { membraneNotificationActionHandler?: (response: unknown) => void }; + if (windowWithHandler.membraneNotificationActionHandler) { + windowWithHandler.membraneNotificationActionHandler(customEvent.detail); + } + } + }); }); // Return API Facade diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index afe48b84b2e263..ab33cbbaefbe17 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,8 @@ 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 = this._register(instantiationService.createInstance(MembraneNotificationsToasts)); this._register(instantiationService.createInstance(NotificationsAlerts, notificationService.model)); const notificationsStatus = instantiationService.createInstance(NotificationsStatus, notificationService.model); From a8adeb7605b5fa5a0d00e874553768c175d6a4a4 Mon Sep 17 00:00:00 2001 From: Jhonny Date: Mon, 13 Oct 2025 01:43:51 -0400 Subject: [PATCH 62/80] Replace addEventListener with MessageChannel. (#78) * Replace addEventListener with MessageChannel. * Use handoff to pass port. * Use messageChannel for handle dialogs. --- .../base/browser/ui/dialog/membraneDialog.ts | 53 ++++----- .../browser/ui/dialog/membranePortManager.ts | 101 ++++++++++++++++++ src/vs/code/browser/workbench/workbench.ts | 30 ++++++ .../membraneNotificationsToasts.ts | 94 +++++++--------- src/vs/workbench/browser/web.main.ts | 30 +----- 5 files changed, 200 insertions(+), 108 deletions(-) create mode 100644 src/vs/base/browser/ui/dialog/membranePortManager.ts diff --git a/src/vs/base/browser/ui/dialog/membraneDialog.ts b/src/vs/base/browser/ui/dialog/membraneDialog.ts index 701fe313c7925e..f8b135d2c8a5e8 100644 --- a/src/vs/base/browser/ui/dialog/membraneDialog.ts +++ b/src/vs/base/browser/ui/dialog/membraneDialog.ts @@ -6,7 +6,8 @@ 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 { mainWindow } from '../../../browser/window.js'; +import { MembranePortManager } from '../../../../base/browser/ui/dialog/membranePortManager.js'; declare global { interface Window { @@ -46,6 +47,19 @@ export class MembraneDialog extends Disposable { private readonly dialogId: string; + // Handle dialog responses from Gaze + private handleDialogResponse(response: MembraneDialogResponse): void { + const pending = MembraneDialog.pendingDialogs.get(response.id); + if (pending) { + MembraneDialog.pendingDialogs.delete(response.id); + pending.resolve({ + button: response.button, + checkboxChecked: response.checkboxChecked, + values: response.values + }); + } + } + constructor( _container: HTMLElement, private message: string, @@ -55,20 +69,10 @@ export class MembraneDialog extends Disposable { super(); this.dialogId = generateUuid(); - // Set up global response handler if not already done - if (!mainWindow.membraneDialogResponseHandler) { - mainWindow.membraneDialogResponseHandler = (response: MembraneDialogResponse) => { - const pending = MembraneDialog.pendingDialogs.get(response.id); - if (pending) { - MembraneDialog.pendingDialogs.delete(response.id); - pending.resolve({ - button: response.button, - checkboxChecked: response.checkboxChecked, - values: response.values - }); - } - }; - } + // Register this instance as the dialog response handler + MembranePortManager.setDialogResponseHandler((response: MembraneDialogResponse) => { + this.handleDialogResponse(response); + }); } async show(): Promise { @@ -90,10 +94,11 @@ export class MembraneDialog extends Disposable { iconType: this.options.type }; - // Send to client via custom event (following your existing pattern) - mainWindow.dispatchEvent(new CustomEvent('membraneDialog', { - detail: dialogMessage - })); + // Initialiwze dialog port if not already done + MembranePortManager.initializeDialogPort(); + + // Send via MessagePort using shared port manager + MembranePortManager.sendMessage('membraneDialog', dialogMessage); // Set up timeout to prevent hanging dialogs setTimeout(() => { @@ -152,12 +157,10 @@ export class MembraneDialog extends Disposable { // Send update to Gaze if dialog is currently showing if (MembraneDialog.pendingDialogs.has(this.dialogId)) { - mainWindow.dispatchEvent(new CustomEvent('membraneDialogUpdate', { - detail: { - id: this.dialogId, - message: message - } - })); + MembranePortManager.sendMessage('membraneDialogUpdate', { + id: this.dialogId, + message: message + }); } } 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..773dc591b906f7 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/membranePortManager.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MembraneDialogResponse } from './membraneDialog.js'; + +export interface MembraneNotificationActionResponse { + notificationId: string; + actionId?: string; + dismissed?: boolean; +} + +interface MembraneWindow extends Window { + membraneDialogPortHandoffKey?: string; + [key: string]: unknown; +} + +declare const window: MembraneWindow; + +export class MembranePortManager { + private static dialogPort: MessagePort | null = null; + private static isInitialized = false; + private static notificationResponseHandler: ((response: MembraneNotificationActionResponse) => void) | null = null; + private static dialogResponseHandler: ((response: MembraneDialogResponse) => void) | null = null; + + static setNotificationResponseHandler(handler: (response: MembraneNotificationActionResponse) => void): void { + MembranePortManager.notificationResponseHandler = handler; + } + + static setDialogResponseHandler(handler: (response: MembraneDialogResponse) => void): void { + MembranePortManager.dialogResponseHandler = handler; + } + + static initializeDialogPort(): void { + if (MembranePortManager.isInitialized) { + return; // Already initialized + } + + const dialogHandoffKey = window.membraneDialogPortHandoffKey; + if (dialogHandoffKey && window[dialogHandoffKey]) { + const handoffFunction = window[dialogHandoffKey] as ((callback: (port: MessagePort) => void) => void) | undefined; + if (handoffFunction) { + handoffFunction((dialogPort: MessagePort) => { + MembranePortManager.dialogPort = dialogPort; + MembranePortManager.isInitialized = true; + + // Set up listener for dialog and notification responses + dialogPort.onmessage = (event: MessageEvent & { messageType: string }>) => { + const data = event.data; + if (data.messageType === 'membraneDialogResponse') { + MembranePortManager.handleDialogResponse(data as unknown as MembraneDialogResponse); + } else if (data.messageType === 'membraneNotificationResponse') { + MembranePortManager.handleNotificationResponse(data as unknown as MembraneNotificationActionResponse); + } + }; + }); + } + } else { + console.log('No handoff key or function available'); + } + } + + static sendMessage(messageType: string, data: Record): void { + if (!MembranePortManager.dialogPort) { + MembranePortManager.initializeDialogPort(); + + if (!MembranePortManager.dialogPort) { + return; + } + } + + try { + const message = { + messageType, + ...data + }; + MembranePortManager.dialogPort.postMessage(message); + } catch (error) { + console.error(`Error sending ${messageType} message:`, error); + } + } + + static handleDialogResponse(response: MembraneDialogResponse): void { + // Use registered handler if available + if (MembranePortManager.dialogResponseHandler) { + MembranePortManager.dialogResponseHandler(response); + } else { + console.log('No dialog response handler registered'); + } + } + + static handleNotificationResponse(response: MembraneNotificationActionResponse): void { + // Use registered handler if available + if (MembranePortManager.notificationResponseHandler) { + MembranePortManager.notificationResponseHandler(response); + } else { + console.log('No notification response handler registered'); + } + } +} diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index ae16d25dcabcb9..0954225a46fadd 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -36,6 +36,36 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; config = await result.json(); } + // MEMBRANE: Create a MessageChannel to communicate with the extension + const channel = new MessageChannel(); + // local port is port2, remote port is port1 + config.messagePorts = new Map([ + ['membrane.membrane', channel.port2], + ]); + + // Use secure handoff pattern to pass port to IDE + const handoffKey = `__membraneWorkbenchPort_handoff_${crypto.randomUUID().replace(/-/g, '')}`; + + // Create handoff function in global scope + window[handoffKey] = (callback: (port: MessagePort, dialogPort?: MessagePort) => void) => { + delete window[handoffKey]; // Immediate cleanup + callback(channel.port1, dialogChannel.port2); + }; + + // MEMBRANE: Create separate MessageChannel for dialogs + const dialogChannel = new MessageChannel(); + const dialogHandoffKey = `__membraneDialogPort_handoff_${crypto.randomUUID().replace(/-/g, '')}`; + + // Create handoff function for dialog port + window[dialogHandoffKey] = (callback: (dialogPort: MessagePort) => void) => { + delete window[dialogHandoffKey]; // Immediate cleanup + callback(dialogChannel.port1); + }; + + // Store handoff keys for IDE and dialog to use + window.membraneWorkbenchPortHandoffKey = handoffKey; + window.membraneDialogPortHandoffKey = dialogHandoffKey; + const isHttps = window.location.protocol === 'https:'; const isDev = window.location.hostname === 'localhost'; const extensionUrl = { diff --git a/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts index 496f8ca5cd73a4..d1f0ba1e81d71a 100644 --- a/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts @@ -13,7 +13,7 @@ import { NotificationsFilter, NotificationPriority, Severity } from '../../../.. import { IntervalCounter } from '../../../../base/common/async.js'; import { NotificationsToastsVisibleContext } from '../../../common/contextkeys.js'; import { IContextKeyService, IContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { mainWindow } from '../../../../base/browser/window.js'; +import { MembranePortManager } from '../../../../base/browser/ui/dialog/membranePortManager.js'; declare global { interface Window { @@ -56,10 +56,12 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle this.notificationsToastsVisibleContextKey = NotificationsToastsVisibleContext.bindTo(contextKeyService); this.registerListeners(); - // Set up handler for notification action responses from Gaze - mainWindow.membraneNotificationActionHandler = (response: MembraneNotificationActionResponse) => { + // Initialize port manager to set up notification response listener + MembranePortManager.initializeDialogPort(); + // Register this instance as the notification response handler + MembranePortManager.setNotificationResponseHandler((response: MembraneNotificationActionResponse) => { this.handleNotificationAction(response); - }; + }); } private registerListeners(): void { @@ -160,14 +162,14 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle })) || [] }; - // Dispatch notification event - mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { - detail: { - type: 'toast', - id: `notification-${notificationId}`, - notification: notificationData - } - })); + // Send notification via MembranePortManager + + MembranePortManager.initializeDialogPort(); + MembranePortManager.sendMessage('membraneNotification', { + type: 'toast', + id: `notification-${notificationId}`, + notification: notificationData + }); } private severityToString(severity: Severity): string { @@ -195,13 +197,11 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle if (item.id) { this.activeNotifications.delete(item.id); - // Notify Gaze to hide the notification - mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { - detail: { - type: 'hide', - id: `notification-${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 @@ -224,14 +224,12 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle } focus(): boolean { - // For keyboard navigation send focus request to Gaze + // For keyboard navigation send focus request to Gaze via MembranePortManager if (this.activeNotifications.size > 0) { - mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { - detail: { - type: 'focus', - target: 'first' - } - })); + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'first' + }); return true; } return false; @@ -239,12 +237,10 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle focusNext(): boolean { if (this.activeNotifications.size > 0) { - mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { - detail: { - type: 'focus', - target: 'next' - } - })); + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'next' + }); return true; } return false; @@ -252,12 +248,10 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle focusPrevious(): boolean { if (this.activeNotifications.size > 0) { - mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { - detail: { - type: 'focus', - target: 'previous' - } - })); + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'previous' + }); return true; } return false; @@ -265,12 +259,10 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle focusFirst(): boolean { if (this.activeNotifications.size > 0) { - mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { - detail: { - type: 'focus', - target: 'first' - } - })); + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'first' + }); return true; } return false; @@ -278,12 +270,10 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle focusLast(): boolean { if (this.activeNotifications.size > 0) { - mainWindow.dispatchEvent(new CustomEvent('membraneNotification', { - detail: { - type: 'focus', - target: 'last' - } - })); + MembranePortManager.sendMessage('membraneNotification', { + type: 'focus', + target: 'last' + }); return true; } return false; @@ -335,9 +325,5 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle dispose(): void { this.disposables.dispose(); this._onDidChangeVisibility.dispose(); - - if (mainWindow.membraneNotificationActionHandler) { - delete mainWindow.membraneNotificationActionHandler; - } } } diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index a46a240853f61c..65fbb5e408b250 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -154,35 +154,7 @@ export class BrowserMain extends Disposable { window.addEventListener('tour:show-learn-membrane', async () => { await commandService.executeCommand('membrane.installPackage', 'membrane/learn-membrane'); }); - // MEMBRANE: Listen for event from Gaze.tsx. - // This bridges the communication from gaze to extension commands. - // eslint-disable-next-line no-restricted-globals - window.addEventListener('gazeToExtension', async (event: Event) => { - try { - const customEvent = event as CustomEvent; - await commandService.executeCommand('membrane.gazeToExtension', customEvent.detail); - } catch (error) { - console.error('Failed to execute command:', error); - } - }); - - // Listen for dialog and notification responses from Gaze.tsx. - mainWindow.addEventListener('gazeToWorkbench', (event: Event) => { - const customEvent = event as CustomEvent; - if (customEvent.detail.type === 'vscode_dialog_response') { - // Forward dialog response to the dialog system - const windowWithHandler = mainWindow as typeof mainWindow & { membraneDialogResponseHandler?: (response: unknown) => void }; - if (windowWithHandler.membraneDialogResponseHandler) { - windowWithHandler.membraneDialogResponseHandler(customEvent.detail.response); - } - } else if (customEvent.detail.type === 'vscode_notification_action') { - // Forward notification action to the notification system - const windowWithHandler = mainWindow as typeof mainWindow & { membraneNotificationActionHandler?: (response: unknown) => void }; - if (windowWithHandler.membraneNotificationActionHandler) { - windowWithHandler.membraneNotificationActionHandler(customEvent.detail); - } - } - }); + // MEMBRANE: gazeToExtension and gazeToWorkbench handling moved to MessagePort in workbench.ts }); // Return API Facade From c1ec6bc83408a17ba4516de209a992c5a4b1d21c Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Wed, 12 Nov 2025 03:32:22 -0500 Subject: [PATCH 63/80] Add inner shadow to vscode --- .../viewParts/scrollDecoration/scrollDecoration.css | 4 ++-- src/vs/workbench/browser/parts/editor/editorGroupView.ts | 9 +++++++++ .../browser/parts/editor/media/editorgroupview.css | 7 +++++++ src/vs/workbench/browser/workbench.contribution.ts | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) 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/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index a1271f4c8cbf43..680c7060c831fc 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -136,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)); @@ -227,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; diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index fe677e0439f989..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 */ } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index b7e563793d66c8..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', From f951fca771236523dc928b77f4007f92a8faede1 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Wed, 12 Nov 2025 03:33:33 -0500 Subject: [PATCH 64/80] Use `extensionToGazePort` --- .../browser/ui/dialog/membranePortManager.ts | 80 ++++++------------- src/vs/code/browser/workbench/workbench.ts | 66 +++++---------- 2 files changed, 43 insertions(+), 103 deletions(-) diff --git a/src/vs/base/browser/ui/dialog/membranePortManager.ts b/src/vs/base/browser/ui/dialog/membranePortManager.ts index 773dc591b906f7..1f1b0e36609137 100644 --- a/src/vs/base/browser/ui/dialog/membranePortManager.ts +++ b/src/vs/base/browser/ui/dialog/membranePortManager.ts @@ -11,16 +11,12 @@ export interface MembraneNotificationActionResponse { dismissed?: boolean; } -interface MembraneWindow extends Window { - membraneDialogPortHandoffKey?: string; - [key: string]: unknown; -} - -declare const window: MembraneWindow; +declare const window: Window & { + dialogsToGazePort?: MessagePort; +}; export class MembranePortManager { - private static dialogPort: MessagePort | null = null; - private static isInitialized = false; + private static dialogsPort: MessagePort | null = null; private static notificationResponseHandler: ((response: MembraneNotificationActionResponse) => void) | null = null; private static dialogResponseHandler: ((response: MembraneDialogResponse) => void) | null = null; @@ -32,70 +28,40 @@ export class MembranePortManager { MembranePortManager.dialogResponseHandler = handler; } - static initializeDialogPort(): void { - if (MembranePortManager.isInitialized) { + static ensureInitialized(): void { + if (MembranePortManager.dialogsPort) { return; // Already initialized } - const dialogHandoffKey = window.membraneDialogPortHandoffKey; - if (dialogHandoffKey && window[dialogHandoffKey]) { - const handoffFunction = window[dialogHandoffKey] as ((callback: (port: MessagePort) => void) => void) | undefined; - if (handoffFunction) { - handoffFunction((dialogPort: MessagePort) => { - MembranePortManager.dialogPort = dialogPort; - MembranePortManager.isInitialized = true; + MembranePortManager.dialogsPort = window.dialogsToGazePort ?? null; + delete window.dialogsToGazePort; - // Set up listener for dialog and notification responses - dialogPort.onmessage = (event: MessageEvent & { messageType: string }>) => { - const data = event.data; - if (data.messageType === 'membraneDialogResponse') { - MembranePortManager.handleDialogResponse(data as unknown as MembraneDialogResponse); - } else if (data.messageType === 'membraneNotificationResponse') { - MembranePortManager.handleNotificationResponse(data as unknown as MembraneNotificationActionResponse); - } - }; - }); + // 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); } - } else { - console.log('No handoff key or function available'); - } + }; + } static sendMessage(messageType: string, data: Record): void { - if (!MembranePortManager.dialogPort) { - MembranePortManager.initializeDialogPort(); - - if (!MembranePortManager.dialogPort) { - return; - } - } - try { + MembranePortManager.ensureInitialized(); const message = { messageType, ...data }; - MembranePortManager.dialogPort.postMessage(message); + MembranePortManager.dialogsPort!.postMessage(message); } catch (error) { console.error(`Error sending ${messageType} message:`, error); } } - static handleDialogResponse(response: MembraneDialogResponse): void { - // Use registered handler if available - if (MembranePortManager.dialogResponseHandler) { - MembranePortManager.dialogResponseHandler(response); - } else { - console.log('No dialog response handler registered'); - } - } - - static handleNotificationResponse(response: MembraneNotificationActionResponse): void { - // Use registered handler if available - if (MembranePortManager.notificationResponseHandler) { - MembranePortManager.notificationResponseHandler(response); - } else { - console.log('No notification response handler registered'); - } - } -} +} \ No newline at end of file diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 0954225a46fadd..e0b949300a7ba7 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -10,7 +10,6 @@ import { IWorkspace, // IWorkspaceProvider, } from '../../../workbench/browser/web.api.js'; -import { mainWindow } from '../../../base/browser/window.js'; import { SecretStorageProvider } from '../workbench/membrane.js'; declare const window: Window & { product?: Writeable; @@ -22,9 +21,11 @@ declare const window: Window & { 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; @@ -36,39 +37,18 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; config = await result.json(); } - // MEMBRANE: Create a MessageChannel to communicate with the extension - const channel = new MessageChannel(); - // local port is port2, remote port is port1 - config.messagePorts = new Map([ - ['membrane.membrane', channel.port2], - ]); - - // Use secure handoff pattern to pass port to IDE - const handoffKey = `__membraneWorkbenchPort_handoff_${crypto.randomUUID().replace(/-/g, '')}`; - - // Create handoff function in global scope - window[handoffKey] = (callback: (port: MessagePort, dialogPort?: MessagePort) => void) => { - delete window[handoffKey]; // Immediate cleanup - callback(channel.port1, dialogChannel.port2); - }; - - // MEMBRANE: Create separate MessageChannel for dialogs - const dialogChannel = new MessageChannel(); - const dialogHandoffKey = `__membraneDialogPort_handoff_${crypto.randomUUID().replace(/-/g, '')}`; - - // Create handoff function for dialog port - window[dialogHandoffKey] = (callback: (dialogPort: MessagePort) => void) => { - delete window[dialogHandoffKey]; // Immediate cleanup - callback(dialogChannel.port1); - }; - - // Store handoff keys for IDE and dialog to use - window.membraneWorkbenchPortHandoffKey = handoffKey; - window.membraneDialogPortHandoffKey = dialogHandoffKey; + // 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', }; @@ -143,26 +123,20 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; { id: 'membrane.getLaunchParams', handler: () => { - // Reading from existing HTML meta tag created outside of workbench // eslint-disable-next-line no-restricted-syntax - const metas = mainWindow.document.getElementsByTagName('meta'); - for (let i = 0; i < metas.length; i++) { - const meta = metas[i]; - if (meta.name === 'membrane-launch-params') { - return meta.content ?? ''; - } - } - return ''; + const meta = document.querySelector('meta[name="membrane-launch-params"]') as HTMLMetaElement; + return meta?.content ?? ''; }, }, ]; - // config.homeIndicator = { - // href: window.location.origin, - // icon: 'home', - // title: 'Membrane Home', - // }; + (config as Writeable & { homeIndicator?: { href: string; icon: string; title: string } }).homeIndicator = { + href: window.location.origin, + icon: 'home', + title: 'Membrane Home', + }; - const domElement = window.vscodeTargetContainer || mainWindow.document.body; + // eslint-disable-next-line no-restricted-syntax + const domElement = window.vscodeTargetContainer || document.body; create(domElement, config); -})(); +})(); \ No newline at end of file From 9f97f1ab9a2611fd78bd55ed10186b4f1c3b38ed Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Wed, 12 Nov 2025 04:55:39 -0500 Subject: [PATCH 65/80] Simplify message passing (#80) * Replace addEventListener with MessageChannel. * Use handoff to pass port. * Use messageChannel for handle dialogs. * Simplify message passing between gaze, extension and membrane dialog manager --------- Co-authored-by: aranajhonny --- src/vs/code/browser/workbench/workbench.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index e0b949300a7ba7..8e5533a94bc5d3 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -45,6 +45,7 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; delete window.extensionToGazePort; } + const isHttps = window.location.protocol === 'https:'; const isDev = window.location.hostname === 'localhost'; const extensionUrl = { From 1f0683d57ae8c68e202d758855b9d393bb8f2183 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Mon, 24 Nov 2025 23:23:06 -0400 Subject: [PATCH 66/80] Fix vscode types. --- .../src/tsServer/serverProcess.browser.ts | 2 +- .../base/browser/ui/dialog/membraneDialog.ts | 30 ++-- .../browser/ui/dialog/membranePortManager.ts | 47 +++--- src/vs/code/browser/workbench/workbench.ts | 10 +- .../api/browser/viewsExtensionPoint.ts | 10 +- .../parts/dialogs/membraneDialogHandler.ts | 5 +- .../parts/editor/editor.contribution.ts | 2 +- .../browser/parts/editor/editorTabsControl.ts | 137 ++++++++++++------ .../parts/editor/multiEditorTabsControl.ts | 2 +- .../parts/editor/singleEditorTabsControl.ts | 2 +- .../membraneNotificationsToasts.ts | 21 +-- src/vs/workbench/browser/workbench.ts | 3 +- 12 files changed, 156 insertions(+), 115 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index 41c21872d5bbcf..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, diff --git a/src/vs/base/browser/ui/dialog/membraneDialog.ts b/src/vs/base/browser/ui/dialog/membraneDialog.ts index f8b135d2c8a5e8..7ef9b4d00b2cf9 100644 --- a/src/vs/base/browser/ui/dialog/membraneDialog.ts +++ b/src/vs/base/browser/ui/dialog/membraneDialog.ts @@ -9,12 +9,6 @@ import { generateUuid } from '../../../common/uuid.js'; // import { mainWindow } from '../../../browser/window.js'; import { MembranePortManager } from '../../../../base/browser/ui/dialog/membranePortManager.js'; -declare global { - interface Window { - membraneDialogResponseHandler?: (response: MembraneDialogResponse) => void; - } -} - export interface MembraneDialogMessage { type: 'confirm' | 'prompt' | 'info' | 'warn' | 'error' | 'input'; id: string; @@ -40,7 +34,7 @@ export interface MembraneDialogResponse { } export class MembraneDialog extends Disposable { - private static pendingDialogs = new Map void; reject: (error: Error) => void; }>(); @@ -49,9 +43,9 @@ export class MembraneDialog extends Disposable { // Handle dialog responses from Gaze private handleDialogResponse(response: MembraneDialogResponse): void { - const pending = MembraneDialog.pendingDialogs.get(response.id); + const pending = MembraneDialog.pendingResponses.get(response.id); if (pending) { - MembraneDialog.pendingDialogs.delete(response.id); + MembraneDialog.pendingResponses.delete(response.id); pending.resolve({ button: response.button, checkboxChecked: response.checkboxChecked, @@ -70,15 +64,15 @@ export class MembraneDialog extends Disposable { this.dialogId = generateUuid(); // Register this instance as the dialog response handler - MembranePortManager.setDialogResponseHandler((response: MembraneDialogResponse) => { - this.handleDialogResponse(response); + MembranePortManager.setDialogResponseHandler((response: unknown) => { + this.handleDialogResponse(response as MembraneDialogResponse); }); } async show(): Promise { return new Promise((resolve, reject) => { // Store the promise resolvers - MembraneDialog.pendingDialogs.set(this.dialogId, { resolve, reject }); + MembraneDialog.pendingResponses.set(this.dialogId, { resolve, reject }); // Prepare the message to send to the client const dialogMessage: MembraneDialogMessage = { @@ -94,17 +88,15 @@ export class MembraneDialog extends Disposable { iconType: this.options.type }; - // Initialiwze dialog port if not already done - MembranePortManager.initializeDialogPort(); // Send via MessagePort using shared port manager MembranePortManager.sendMessage('membraneDialog', dialogMessage); // Set up timeout to prevent hanging dialogs setTimeout(() => { - const pending = MembraneDialog.pendingDialogs.get(this.dialogId); + const pending = MembraneDialog.pendingResponses.get(this.dialogId); if (pending) { - MembraneDialog.pendingDialogs.delete(this.dialogId); + MembraneDialog.pendingResponses.delete(this.dialogId); // Default to cancel/close behavior pending.resolve({ button: this.options.cancelId || 0, @@ -156,7 +148,7 @@ export class MembraneDialog extends Disposable { this.message = message; // Send update to Gaze if dialog is currently showing - if (MembraneDialog.pendingDialogs.has(this.dialogId)) { + if (MembraneDialog.pendingResponses.has(this.dialogId)) { MembranePortManager.sendMessage('membraneDialogUpdate', { id: this.dialogId, message: message @@ -167,6 +159,6 @@ export class MembraneDialog extends Disposable { override dispose(): void { super.dispose(); // Clean up any pending dialog - MembraneDialog.pendingDialogs.delete(this.dialogId); + 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 index 1f1b0e36609137..5a1b653758b4a5 100644 --- a/src/vs/base/browser/ui/dialog/membranePortManager.ts +++ b/src/vs/base/browser/ui/dialog/membranePortManager.ts @@ -2,29 +2,22 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { MembraneDialogResponse } from './membraneDialog.js'; - -export interface MembraneNotificationActionResponse { - notificationId: string; - actionId?: string; - dismissed?: boolean; +interface IMembraneWindow extends Window { + dialogsToGazePort?: MessagePort; } -declare const window: Window & { - dialogsToGazePort?: MessagePort; -}; +declare const window: IMembraneWindow; export class MembranePortManager { private static dialogsPort: MessagePort | null = null; - private static notificationResponseHandler: ((response: MembraneNotificationActionResponse) => void) | null = null; - private static dialogResponseHandler: ((response: MembraneDialogResponse) => void) | null = null; + private static notificationResponseHandler: ((response: unknown) => void) | null = null; + private static dialogResponseHandler: ((response: unknown) => void) | null = null; - static setNotificationResponseHandler(handler: (response: MembraneNotificationActionResponse) => void): void { + static setNotificationResponseHandler(handler: (response: unknown) => void): void { MembranePortManager.notificationResponseHandler = handler; } - static setDialogResponseHandler(handler: (response: MembraneDialogResponse) => void): void { + static setDialogResponseHandler(handler: (response: unknown) => void): void { MembranePortManager.dialogResponseHandler = handler; } @@ -33,16 +26,24 @@ export class MembranePortManager { return; // Already initialized } - MembranePortManager.dialogsPort = window.dialogsToGazePort ?? null; - delete window.dialogsToGazePort; + 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) => { + MembranePortManager.dialogsPort.onmessage = (event) => { try { if (event.data.messageType === 'membraneDialogResponse') { - MembranePortManager.dialogResponseHandler!(event.data); + MembranePortManager.dialogResponseHandler?.(event.data); } else if (event.data.messageType === 'membraneNotificationResponse') { - MembranePortManager.notificationResponseHandler!(event.data); + MembranePortManager.notificationResponseHandler?.(event.data); } } catch (error) { console.error(`Error handling ${event.data.messageType} message:`, error); @@ -51,14 +52,18 @@ export class MembranePortManager { } - static sendMessage(messageType: string, data: Record): void { + 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); + MembranePortManager.dialogsPort.postMessage(message); } catch (error) { console.error(`Error sending ${messageType} message:`, error); } diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 8e5533a94bc5d3..14e7e2cd009bfe 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -21,7 +21,7 @@ declare const window: Window & { vscodeTargetContainer?: HTMLElement | null; completeInitialization?: () => void; SENTRY_CAPTURE_EXCEPTION?: (error: Error) => void; - extensionToGazePort?: MessagePort; + gazeExtensionPort?: MessagePort; }; type Writeable = { -readonly [P in keyof T]: T[P] }; @@ -38,11 +38,11 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; } // Forward the MessagePort to the extension so it can directly talk to gaze - if (window.extensionToGazePort) { - config.messagePorts = new Map([ - ['membrane.membrane', window.extensionToGazePort], + if (window.gazeExtensionPort) { + config.messagePorts = new Map([ + ['membrane.membrane', window.gazeExtensionPort], ]); - delete window.extensionToGazePort; + delete window.gazeExtensionPort; } diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 226012dd54c8ac..c066fa7614f3ec 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -316,7 +316,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private addCustomViewContainers(extensionPoints: readonly IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { 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; + // let panelOrder = 5 + viewContainersRegistry.all.filter(v => !!v.extensionId && viewContainersRegistry.getViewContainerLocation(v) === ViewContainerLocation.Panel).length + 1; for (const { value, collector, description } of extensionPoints) { Object.entries(value).forEach(([key, value]) => { if (!this.isValidViewsContainer(value, collector)) { @@ -327,10 +327,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution { activityBarOrder = this.registerCustomViewContainers(value, description, activityBarOrder, existingViewContainers, ViewContainerLocation.Sidebar); break; } - case 'panel': { - panelOrder = this.registerCustomViewContainers(value, description, panelOrder, existingViewContainers, ViewContainerLocation.Panel); - break; - } + // case 'panel': { + // panelOrder = this.registerCustomViewContainers(value, description, panelOrder, existingViewContainers, ViewContainerLocation.Panel); + // break; + // } } }); } diff --git a/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts b/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts index b3d35827a812f3..9e7737093bb898 100644 --- a/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts +++ b/src/vs/workbench/browser/parts/dialogs/membraneDialogHandler.ts @@ -9,14 +9,11 @@ import { ILayoutService } from '../../../../platform/layout/browser/layoutServic 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, IDialogStyles } from '../../../../base/browser/ui/dialog/dialog.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'; -import type { IButtonStyles } from '../../../../base/browser/ui/button/button.js'; -import type { ICheckboxStyles } from '../../../../base/browser/ui/toggle/toggle.js'; -import type { IInputBoxStyles } from '../../../../base/browser/ui/inputbox/inputBox.js'; export class MembraneDialogHandler extends AbstractDialogHandler { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 0fdf1e9d7e7f14..021f6a79e2ba41 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -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'; diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index 8c7a2ea247f515..9a805d3732d42d 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -6,15 +6,15 @@ 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'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; -import { IContextKeyService, IContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { IContextKeyService, IContextKey, AuxiliaryBarVisibleContext } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; @@ -24,7 +24,7 @@ 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 { AnchorAlignment } from '../../../../base/browser/ui/contextview/contextview.js'; @@ -43,15 +43,13 @@ import { IAuxiliaryEditorPart, IEditorGroupsService, MergeGroupMode } from '../. 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 'vs/workbench/common/contextkeys'; +// 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 { @@ -106,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()); @@ -152,18 +152,10 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC ) { super(themeService); - // MEMBRANE: editorGroupsService is injected for future use - void this.editorGroupsService; - - 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)); @@ -177,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 { @@ -189,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); @@ -220,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), @@ -244,15 +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(); } + // 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, options: IBaseActionViewItemOptions): IActionViewItem | undefined { + 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; @@ -260,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 { @@ -273,10 +330,11 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC const editorActions = this.groupView.createEditorActions(this.editorActionsDisposables); this.editorActionsDisposables.add(editorActions.onDidChange(() => this.updateEditorActionsToolbar())); - // MEMBRANE: No longer hiding editor tab actions (e.g. Split Editor, More Actions). - 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; @@ -331,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; @@ -464,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`); } @@ -529,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/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index c1b0668f1f6c5f..293f7b59e5c43e 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -1636,7 +1636,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, 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/notifications/membraneNotificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts index d1f0ba1e81d71a..9f7524274d11b8 100644 --- a/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/membraneNotificationsToasts.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { INotificationsModel, NotificationChangeType, INotificationChangeEvent, INotificationViewItem } from '../../../common/notifications.js'; -import { DisposableStore } from '../../../../base/common/lifecycle.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'; @@ -27,7 +27,7 @@ interface MembraneNotificationActionResponse { dismissed?: boolean; } -export class MembraneNotificationsToasts implements INotificationsToastController { +export class MembraneNotificationsToasts extends Disposable implements INotificationsToastController { private static readonly MAX_NOTIFICATIONS = 3; private static readonly SPAM_PROTECTION = { @@ -35,7 +35,7 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle limit: this.MAX_NOTIFICATIONS }; - private readonly _onDidChangeVisibility = new Emitter(); + private readonly _onDidChangeVisibility = this._register(new Emitter()); readonly onDidChangeVisibility = this._onDidChangeVisibility.event; private _isVisible = false; @@ -46,21 +46,22 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle private readonly notificationsToastsVisibleContextKey: IContextKey; private readonly activeNotifications = new Map(); - private readonly disposables = new DisposableStore(); + 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.initializeDialogPort(); + MembranePortManager.ensureInitialized(); // Register this instance as the notification response handler - MembranePortManager.setNotificationResponseHandler((response: MembraneNotificationActionResponse) => { - this.handleNotificationAction(response); + MembranePortManager.setNotificationResponseHandler((response: unknown) => { + this.handleNotificationAction(response as MembraneNotificationActionResponse); }); } @@ -164,7 +165,7 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle // Send notification via MembranePortManager - MembranePortManager.initializeDialogPort(); + MembranePortManager.ensureInitialized(); MembranePortManager.sendMessage('membraneNotification', { type: 'toast', id: `notification-${notificationId}`, @@ -322,8 +323,8 @@ export class MembraneNotificationsToasts implements INotificationsToastControlle } } - dispose(): void { + override dispose(): void { this.disposables.dispose(); - this._onDidChangeVisibility.dispose(); + super.dispose(); } } diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index ab33cbbaefbe17..bddcd8182c064c 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -379,7 +379,8 @@ export class Workbench extends Layout { // Instantiate Notification components const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.mainContainer, notificationService.model)); // MEMBRANE: Using our own toasts. - const notificationsToasts = this._register(instantiationService.createInstance(MembraneNotificationsToasts)); + 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); From 809cf81322bc7df402d8cd886cbf16bc3e5731da Mon Sep 17 00:00:00 2001 From: Thomas Seeley Date: Fri, 8 Aug 2025 17:40:36 -0400 Subject: [PATCH 67/80] Fix loading events module. --- extensions/shared.webpack.config.mjs | 15 +++++++++++++-- .../extension-browser.webpack.config.js | 18 +++++++++++++++++- .../api/browser/viewsExtensionPoint.ts | 11 +++++------ src/vs/workbench/browser/layout.ts | 15 +++------------ 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index 0d9ccdfb3fdc10..12c55cc60ec6eb 100644 --- a/extensions/shared.webpack.config.mjs +++ b/extensions/shared.webpack.config.mjs @@ -108,17 +108,20 @@ 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: { 'os': require.resolve('os-browserify'), - 'events': require.resolve('events'), + 'events': require.resolve('events/'), // 'os': require.resolve('os-browserify'), 'path': require.resolve('path-browserify'), 'util': require.resolve('util/') @@ -209,7 +212,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/typescript-language-features/extension-browser.webpack.config.js b/extensions/typescript-language-features/extension-browser.webpack.config.js index ca45aa8169ea70..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 @@ -55,7 +64,7 @@ export default [withBrowserDefaults({ })) ], }), - ], + ], }), withBrowserDefaults({ context: import.meta.dirname, entry: { @@ -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/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index c066fa7614f3ec..6eade48f658920 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -316,7 +316,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private addCustomViewContainers(extensionPoints: readonly IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { 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; + let panelOrder = 5 + viewContainersRegistry.all.filter(v => !!v.extensionId && viewContainersRegistry.getViewContainerLocation(v) === ViewContainerLocation.Panel).length + 1; for (const { value, collector, description } of extensionPoints) { Object.entries(value).forEach(([key, value]) => { if (!this.isValidViewsContainer(value, collector)) { @@ -327,10 +327,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution { activityBarOrder = this.registerCustomViewContainers(value, description, activityBarOrder, existingViewContainers, ViewContainerLocation.Sidebar); break; } - // case 'panel': { - // panelOrder = this.registerCustomViewContainers(value, description, panelOrder, existingViewContainers, ViewContainerLocation.Panel); - // break; - // } + case 'panel': { + panelOrder = this.registerCustomViewContainers(value, description, panelOrder, existingViewContainers, ViewContainerLocation.Panel); + break; + } } }); } @@ -408,7 +408,6 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return order; } - private registerCustomViewContainer(id: string, title: string, icon: URI | ThemeIcon, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer { let viewContainer = this.viewContainersRegistry.get(id); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 556f77017a6a80..e5571eeb093a04 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -643,15 +643,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // 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.setRuntimeValue(LayoutStateKeys.STATUSBAR_HIDDEN, true); - - // MEMBRANE: More config updates to clean up the editor (overrides user settings) - // MEMBRANE: NOTE(gazetoide): As we replace VSCode settings and move to a simpler settings UI in gaze - // this list will probably grow. The goal is to cut down the settings to our few chosen options and force - // everything else to good defaults. - // MEMBRANE: For activity bar position, make sure it is set to 'top' - this.configurationService.updateValue('workbench.activityBar.location', 'top'); - this.configurationService.updateValue('editor.minimap.enabled', false); this.stateModel.onDidChangeState(change => { if (change.key === LayoutStateKeys.ACTIVITYBAR_HIDDEN) { @@ -1487,7 +1478,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)) { @@ -2770,9 +2761,9 @@ const LayoutStateKeys = { PANEL_ALIGNMENT: new RuntimeStateKey('panel.alignment', StorageScope.PROFILE, StorageTarget.USER, 'center'), // Part Visibility - // MEMBRANE: Hide activitybar by default + // MEMBRANE: Do not hide activitybar by default ACTIVITYBAR_HIDDEN: new RuntimeStateKey('activityBar.hidden', StorageScope.WORKSPACE, StorageTarget.MACHINE, true, true), - // MEMBRANE: Hide sidebar by default + // 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), From 2bce8a86a2fa9ff8dbfdd6e466179b9271adc6d2 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Tue, 25 Nov 2025 17:58:07 -0400 Subject: [PATCH 68/80] Force webpack to bundle events module. --- extensions/typescript-language-features/web/src/webServer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/typescript-language-features/web/src/webServer.ts b/extensions/typescript-language-features/web/src/webServer.ts index 525aa2ba3df532..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'; From a5d198722b23d24bfad0fe79164d85fc2ac22838 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Tue, 25 Nov 2025 17:59:23 -0400 Subject: [PATCH 69/80] Fix tabs, and menus. --- src/vs/code/browser/workbench/workbench.ts | 17 ++ .../browser/actions/layoutActions.ts | 135 +++++----- .../parts/auxiliarybar/auxiliaryBarActions.ts | 91 ++++--- .../parts/editor/editor.contribution.ts | 40 +-- .../browser/parts/editor/editorTabsControl.ts | 4 +- .../parts/editor/editorTitleControl.ts | 2 +- .../editor/media/multieditortabscontrol.css | 5 + .../parts/editor/multiEditorTabsControl.ts | 13 +- .../browser/parts/globalCompositeBar.ts | 5 + .../browser/parts/panel/panelActions.ts | 52 ++-- .../browser/parts/titlebar/titlebarPart.ts | 56 ++-- .../browser/parts/titlebar/windowTitle.ts | 252 ++++++++---------- 12 files changed, 344 insertions(+), 328 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 14e7e2cd009bfe..6840799e35c057 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -137,6 +137,23 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; 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 + }; + // eslint-disable-next-line no-restricted-syntax const domElement = window.vscodeTargetContainer || document.body; create(domElement, config); 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/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/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 021f6a79e2ba41..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'; @@ -389,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 }); @@ -423,8 +426,9 @@ 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 }); +// 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 */ }); diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index 9a805d3732d42d..0b7ee491a2f91a 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -14,7 +14,7 @@ import { ResolvedKeybinding } from '../../../../base/common/keybindings.js'; import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; -import { IContextKeyService, IContextKey, AuxiliaryBarVisibleContext } from '../../../../platform/contextkey/common/contextkey.js'; +import { IContextKeyService, IContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; @@ -26,7 +26,7 @@ import { EditorPane } from './editorPane.js'; import { IEditorGroupsView, IEditorGroupView, IEditorPartsView, IInternalEditorOpenOptions } from './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'; 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/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 293f7b59e5c43e..0e5eb2ef7c80be 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -194,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(); @@ -1871,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 @@ -1889,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) { @@ -1907,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/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/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 1391a22f7a6fd7..31bbee8c6e085a 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -9,7 +9,7 @@ import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; import { MenuId, MenuRegistry, registerAction2, Action2, IAction2Options } from '../../../../platform/actions/common/actions.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { isHorizontal, IWorkbenchLayoutService, PanelAlignment, Parts, Position, positionToString } from '../../../services/layout/browser/layoutService.js'; -import { IsAuxiliaryWindowContext, PanelAlignmentContext, PanelMaximizedContext, PanelPositionContext, PanelVisibleContext } from '../../../common/contextkeys.js'; +import { PanelAlignmentContext, PanelMaximizedContext, PanelPositionContext, PanelVisibleContext } from '../../../common/contextkeys.js'; import { ContextKeyExpr, ContextKeyExpression } from '../../../../platform/contextkey/common/contextkey.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; @@ -24,8 +24,7 @@ import { SwitchCompositeViewAction } from '../compositeBarActions.js'; const maximizeIcon = registerIcon('panel-maximize', Codicon.screenFull, localize('maximizeIcon', 'Icon to maximize a panel.')); export const closeIcon = registerIcon('panel-close', Codicon.close, localize('closeIcon', 'Icon to close a panel.')); -const panelIcon = registerIcon('panel-layout-icon', Codicon.layoutPanel, localize('togglePanelOffIcon', 'Icon to toggle the panel off when it is on.')); -const panelOffIcon = registerIcon('panel-layout-icon-off', Codicon.layoutPanelOff, localize('togglePanelOnIcon', 'Icon to toggle the panel on when it is off.')); + export class TogglePanelAction extends Action2 { @@ -314,29 +313,30 @@ registerAction2(class extends Action2 { } }); -MenuRegistry.appendMenuItems([ - { - id: MenuId.LayoutControlMenu, - item: { - group: '2_pane_toggles', - command: { - id: TogglePanelAction.ID, - title: localize('togglePanel', "Toggle Panel"), - icon: panelOffIcon, - toggled: { condition: PanelVisibleContext, icon: panelIcon } - }, - when: - ContextKeyExpr.and( - IsAuxiliaryWindowContext.negate(), - ContextKeyExpr.or( - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), - ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both') - ) - ), - order: 1 - } - } -]); +// MEMBRANE: Hide Toggle Panel option +// MenuRegistry.appendMenuItems([ +// { +// id: MenuId.LayoutControlMenu, +// item: { +// group: '2_pane_toggles', +// command: { +// id: TogglePanelAction.ID, +// title: localize('togglePanel', "Toggle Panel"), +// icon: panelOffIcon, +// toggled: { condition: PanelVisibleContext, icon: panelIcon } +// }, +// when: +// ContextKeyExpr.and( +// IsAuxiliaryWindowContext.negate(), +// ContextKeyExpr.or( +// ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), +// ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both') +// ) +// ), +// order: 1 +// } +// } +// ]); class MoveViewsBetweenPanelsAction extends Action2 { constructor(private readonly source: ViewContainerLocation, private readonly destination: ViewContainerLocation, desc: Readonly) { 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 { From d70096e3e1a28cb9d98329ae28166a6d9708c952 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Tue, 25 Nov 2025 23:35:38 -0400 Subject: [PATCH 70/80] Fix vscode web build. --- build/gulpfile.vscode.web.mjs | 7 +++++-- src/tsconfig.json | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/build/gulpfile.vscode.web.mjs b/build/gulpfile.vscode.web.mjs index e976ed77a61bf7..932a6075ef4422 100644 --- a/build/gulpfile.vscode.web.mjs +++ b/build/gulpfile.vscode.web.mjs @@ -17,7 +17,7 @@ import filter from 'gulp-filter'; import * as dependenciesModule from './lib/dependencies.js'; import vfs from 'vinyl-fs'; import packageJson from '../package.json' with { type: 'json' }; -import { compileBuildWithManglingTask } from './gulpfile.compile.mjs'; +import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.mjs'; import extensions from './lib/extensions.js'; import VinylFile from 'vinyl'; import jsonEditor from 'gulp-json-editor'; @@ -234,7 +234,10 @@ const dashed = (/** @type {string} */ str) => (str ? `-${str}` : ``); gulp.task(vscodeWebTaskCI); const vscodeWebTask = task.define(`vscode-web${dashed(minified)}`, task.series( - compileBuildWithManglingTask, + // MEMBRANE: Use this instead of `compileBuildTask` to speed up local builds + // by skipping the mangle step (~15min) + // compileBuildWithManglingTask, + minified ? compileBuildWithoutManglingTask : compileBuildWithManglingTask, vscodeWebTaskCI )); gulp.task(vscodeWebTask); diff --git a/src/tsconfig.json b/src/tsconfig.json index cb4c3d2ed37430..759e5f975cd8d2 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -34,6 +34,9 @@ "./vscode-dts/vscode.d.ts" ], "exclude": [ - "vs/workbench/contrib/webview/browser/pre/service-worker.js" + "vs/workbench/contrib/webview/browser/pre/service-worker.js", + "vs/workbench/contrib/terminal/browser/terminalInstanceService.ts", + "vs/workbench/contrib/terminal/browser/terminalProfileService.ts", + "vs/workbench/contrib/terminal/browser/terminalService.ts" ] } From efb6b946bdabe06c1c8c192b879cb9a4482786aa Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Tue, 25 Nov 2025 23:53:32 -0400 Subject: [PATCH 71/80] Fix TypeScript version in extensions package-lock.json --- extensions/package-lock.json | 464 ++--------------------------------- 1 file changed, 18 insertions(+), 446 deletions(-) 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" From 0efcc9b3c777b471ae0c5e66882274b899101727 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Wed, 26 Nov 2025 00:25:54 -0400 Subject: [PATCH 72/80] Skip type checking. --- extensions/shared.webpack.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/shared.webpack.config.mjs b/extensions/shared.webpack.config.mjs index 12c55cc60ec6eb..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) { From 23235e1deebd6386f882c3d4116ebd1e88789fd3 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Wed, 26 Nov 2025 18:50:14 -0400 Subject: [PATCH 73/80] include codeweb on vscode build. --- build/gulpfile.vscode.web.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gulpfile.vscode.web.mjs b/build/gulpfile.vscode.web.mjs index 932a6075ef4422..648c4a4411af88 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(); From d5c5cbd67d9d72fee61db47929ec8e1656b1e842 Mon Sep 17 00:00:00 2001 From: aranajhonny Date: Thu, 27 Nov 2025 08:05:53 -0400 Subject: [PATCH 74/80] Restore compileBuildWithManglingTask. --- build/gulpfile.vscode.web.mjs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/build/gulpfile.vscode.web.mjs b/build/gulpfile.vscode.web.mjs index 648c4a4411af88..f568f47efa09f7 100644 --- a/build/gulpfile.vscode.web.mjs +++ b/build/gulpfile.vscode.web.mjs @@ -17,7 +17,7 @@ import filter from 'gulp-filter'; import * as dependenciesModule from './lib/dependencies.js'; import vfs from 'vinyl-fs'; import packageJson from '../package.json' with { type: 'json' }; -import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.mjs'; +import { compileBuildWithManglingTask } from './gulpfile.compile.mjs'; import extensions from './lib/extensions.js'; import VinylFile from 'vinyl'; import jsonEditor from 'gulp-json-editor'; @@ -235,10 +235,7 @@ const dashed = (/** @type {string} */ str) => (str ? `-${str}` : ``); gulp.task(vscodeWebTaskCI); const vscodeWebTask = task.define(`vscode-web${dashed(minified)}`, task.series( - // MEMBRANE: Use this instead of `compileBuildTask` to speed up local builds - // by skipping the mangle step (~15min) - // compileBuildWithManglingTask, - minified ? compileBuildWithoutManglingTask : compileBuildWithManglingTask, + compileBuildWithManglingTask, vscodeWebTaskCI )); gulp.task(vscodeWebTask); From ca1de88b5393f83888cad240975b15e3a402532c Mon Sep 17 00:00:00 2001 From: Jhonny Arana Date: Fri, 5 Dec 2025 11:48:45 -0400 Subject: [PATCH 75/80] Fix membrane terminal errors. Signed-off-by: Jhonny Arana --- src/tsconfig.json | 5 +- .../mainThreadTerminalService.membrane.ts | 112 -- .../api/browser/mainThreadTerminalService.ts | 500 +------ .../mainThreadTerminalShellIntegration.ts | 134 +- .../browser/terminal.contribution.membrane.ts | 23 - .../terminal/browser/terminal.contribution.ts | 126 +- .../terminalInstanceService.membrane.ts | 59 - .../browser/terminalInstanceService.ts | 61 +- .../terminalProfileService.membrane.ts | 95 -- .../browser/terminalProfileService.ts | 248 +--- .../browser/terminalService.membrane.ts | 270 ---- .../terminal/browser/terminalService.ts | 1315 ++--------------- 12 files changed, 211 insertions(+), 2737 deletions(-) delete mode 100644 src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts delete mode 100644 src/vs/workbench/contrib/terminal/browser/terminal.contribution.membrane.ts delete mode 100644 src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts delete mode 100644 src/vs/workbench/contrib/terminal/browser/terminalProfileService.membrane.ts delete mode 100644 src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts diff --git a/src/tsconfig.json b/src/tsconfig.json index 759e5f975cd8d2..cb4c3d2ed37430 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -34,9 +34,6 @@ "./vscode-dts/vscode.d.ts" ], "exclude": [ - "vs/workbench/contrib/webview/browser/pre/service-worker.js", - "vs/workbench/contrib/terminal/browser/terminalInstanceService.ts", - "vs/workbench/contrib/terminal/browser/terminalProfileService.ts", - "vs/workbench/contrib/terminal/browser/terminalService.ts" + "vs/workbench/contrib/webview/browser/pre/service-worker.js" ] } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts deleted file mode 100644 index e0f8823c282443..00000000000000 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.membrane.ts +++ /dev/null @@ -1,112 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ExtHostTerminalIdentifier } from '../common/extHost.protocol.js'; -import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; -import { IProcessProperty, IProcessReadyWindowsPty, ITerminalOutputMatch, ITerminalOutputMatcher } from '../../../platform/terminal/common/terminal.js'; -import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from '../../../platform/terminal/common/environmentVariable.js'; - -@extHostNamedCustomer(MainContext.MainThreadTerminalService) -export class MainThreadTerminalService implements MainThreadTerminalServiceShape { - constructor(_extHostContext: IExtHostContext) { } - - public dispose(): void { } - - public async $createTerminal(extHostTerminalId: string, launchConfig: TerminalLaunchConfig): Promise { - throw new Error('Unsupported'); - } - - public async $show(id: ExtHostTerminalIdentifier, preserveFocus: boolean): Promise { - throw new Error('Unsupported'); - } - - public async $hide(id: ExtHostTerminalIdentifier): Promise { - throw new Error('Unsupported'); - } - - public async $dispose(id: ExtHostTerminalIdentifier): Promise { - throw new Error('Unsupported'); - } - - public async $sendText(id: ExtHostTerminalIdentifier, text: string, shouldExecute: boolean): Promise { - throw new Error('Unsupported'); - } - - public $sendProcessExit(terminalId: number, exitCode: number | undefined): void { - throw new Error('Unsupported'); - } - - public $startSendingDataEvents(): void { - throw new Error('Unsupported'); - } - - public $stopSendingDataEvents(): void { - throw new Error('Unsupported'); - } - - public $startSendingCommandEvents(): void { - throw new Error('Unsupported'); - } - - public $stopSendingCommandEvents(): void { - throw new Error('Unsupported'); - } - - public $startLinkProvider(): void { - throw new Error('Unsupported'); - } - - public $stopLinkProvider(): void { - throw new Error('Unsupported'); - } - - public $registerProcessSupport(isSupported: boolean): void { - // Empty - } - - public $registerProfileProvider(id: string, extensionIdentifier: string): void { - throw new Error('Unsupported'); - } - - public $unregisterProfileProvider(id: string): void { - throw new Error('Unsupported'); - } - - public async $registerQuickFixProvider(id: string, extensionId: string): Promise { - throw new Error('Unsupported'); - } - - public $unregisterQuickFixProvider(id: string): void { - throw new Error('Unsupported'); - } - - public $registerCompletionProvider(id: string, extensionId: string): void { - throw new Error('Unsupported'); - } - - public $unregisterCompletionProvider(id: string): void { - throw new Error('Unsupported'); - } - - public $sendProcessData(terminalId: number, data: string): void { - throw new Error('Unsupported'); - } - - public $sendProcessReady(terminalId: number, pid: number, cwd: string, windowsPty: IProcessReadyWindowsPty | undefined): void { - throw new Error('Unsupported'); - } - - public $sendProcessProperty(terminalId: number, property: IProcessProperty): void { - throw new Error('Unsupported'); - } - - $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined, descriptionMap: ISerializableEnvironmentDescriptionMap): void { - throw new Error('Unsupported'); - } -} - -export function getOutputMatchForLines(lines: string[], outputMatcher: ITerminalOutputMatcher): ITerminalOutputMatch | undefined { - const match: RegExpMatchArray | null | undefined = lines.join('\n').match(outputMatcher.lineMatcher); - return match ? { regexMatch: match, outputLines: lines } : undefined; -} 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/contrib/terminal/browser/terminal.contribution.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.membrane.ts deleted file mode 100644 index 2a11ba15d02099..00000000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.membrane.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ITerminalProfileService } from '../common/terminal.js'; -import { TerminalService } from './terminalService.membrane.js'; -import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { ITerminalEditorService, ITerminalGroupService, ITerminalInstanceService, ITerminalService } from './terminal.js'; -import { ITerminalLogService } from '../../../../platform/terminal/common/terminal.js'; -import { TerminalInstanceService } from './terminalInstanceService.membrane.js'; -import { TerminalEditorService } from './terminalEditorService.js'; -import { TerminalGroupService } from './terminalGroupService.js'; -import { TerminalProfileService } from './terminalProfileService.membrane.js'; -import { TerminalLogService } from '../../../../platform/terminal/common/terminalLogService.js'; - -// Register services -registerSingleton(ITerminalLogService, TerminalLogService, InstantiationType.Delayed); -registerSingleton(ITerminalService, TerminalService, InstantiationType.Delayed); -registerSingleton(ITerminalEditorService, TerminalEditorService, InstantiationType.Delayed); -registerSingleton(ITerminalGroupService, TerminalGroupService, InstantiationType.Delayed); -registerSingleton(ITerminalInstanceService, TerminalInstanceService, InstantiationType.Delayed); -registerSingleton(ITerminalProfileService, TerminalProfileService, InstantiationType.Delayed); 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.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts deleted file mode 100644 index 13e75977d15734..00000000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.membrane.ts +++ /dev/null @@ -1,59 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { 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 { URI } from '../../../../base/common/uri.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; -import { Registry } from '../../../../platform/registry/common/platform.js'; - -export class TerminalInstanceService extends Disposable implements ITerminalInstanceService { - declare _serviceBrand: undefined; - // private _configHelper: TerminalConfigHelper; - private _backendRegistration = new Map; resolve: () => void }>(); - - private readonly _onDidCreateInstance = this._register(new Emitter()); - get onDidCreateInstance(): Event { return this._onDidCreateInstance.event; } - - private readonly _onDidRegisterBackend = this._register(new Emitter()); - get onDidRegisterBackend(): Event { return this._onDidRegisterBackend.event; } - - constructor( - // @IInstantiationService private readonly _instantiationService: IInstantiationService, - // @IContextKeyService private readonly _contextKeyService: IContextKeyService, - // @IWorkbenchEnvironmentService readonly _environmentService: IWorkbenchEnvironmentService, - ...args: unknown[] - ) { - super(); - } - - createInstance(profile: ITerminalProfile, target: TerminalLocation): ITerminalInstance; - createInstance(shellLaunchConfig: IShellLaunchConfig, target: TerminalLocation): ITerminalInstance; - createInstance(config: IShellLaunchConfig | ITerminalProfile, target: TerminalLocation): ITerminalInstance { - throw new Error('Unimplemented'); - } - - convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { - // Return empty shell launch config - return {}; - } - - async getBackend(remoteAuthority?: string): Promise { - return undefined; - } - - getRegisteredBackends(): IterableIterator { - return Registry.as(TerminalExtensions.Backend).backends.values(); - } - - didRegisterBackend(backend: ITerminalBackend) { - this._backendRegistration.get(backend.remoteAuthority)?.resolve(); - this._onDidRegisterBackend.fire(backend); - } -} - -registerSingleton(ITerminalInstanceService, TerminalInstanceService, InstantiationType.Delayed); 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.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.membrane.ts deleted file mode 100644 index eaa2640e3ea35c..00000000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.membrane.ts +++ /dev/null @@ -1,95 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { throttle } from '../../../../base/common/decorators.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'; - -/* - * Links TerminalService with TerminalProfileResolverService - * and keeps the available terminal profiles updated - */ -export class TerminalProfileService extends Disposable implements ITerminalProfileService { - declare _serviceBrand: undefined; - - get onDidChangeAvailableProfiles(): Event { throw new Error('Unsupported'); } - - get profilesReady(): Promise { - - throw new Error('Unsupported'); - } - get availableProfiles(): ITerminalProfile[] { - - throw new Error('Unsupported'); - } - get contributedProfiles(): IExtensionTerminalProfile[] { - - throw new Error('Unsupported'); - } - - 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 - ...args: unknown[] - ) { - super(); - } - - getDefaultProfileName(): string | undefined { - - throw new Error('Unsupported'); - } - - getDefaultProfile(os?: OperatingSystem): ITerminalProfile | undefined { - - throw new Error('Unsupported'); - } - - - @throttle(2000) - refreshAvailableProfiles(): void { - - throw new Error('Unsupported'); - } - - protected async _refreshAvailableProfilesNow(): Promise { - - throw new Error('Unsupported'); - } - - getContributedProfileProvider(extensionIdentifier: string, id: string): ITerminalProfileProvider | undefined { - - throw new Error('Unsupported'); - } - - async getPlatformKey(): Promise { - - throw new Error('Unsupported'); - } - - registerTerminalProfileProvider(extensionIdentifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable { - - throw new Error('Unsupported'); - } - - async registerContributedProfile(args: IRegisterContributedProfileArgs): Promise { - - throw new Error('Unsupported'); - } - - async getContributedDefaultProfile(shellLaunchConfig: IShellLaunchConfig): Promise { - - throw new Error('Unsupported'); - } - -} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts index 0cbef05b9221b0..eaa2640e3ea35c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts @@ -3,27 +3,12 @@ * 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 @@ -32,244 +17,79 @@ import { hasKey, isString } from '../../../../base/common/types.js'; 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 { throw new Error('Unsupported'); } - private readonly _onDidChangeAvailableProfiles = this._register(new Emitter()); - get onDidChangeAvailableProfiles(): Event { return this._onDidChangeAvailableProfiles.event; } + get profilesReady(): Promise { - get profilesReady(): Promise { return this._profilesReadyPromise; } + throw new Error('Unsupported'); + } get availableProfiles(): ITerminalProfile[] { - if (!this._platformConfigJustRefreshed) { - this.refreshAvailableProfiles(); - } - return this._availableProfiles || []; + + throw new Error('Unsupported'); } 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)) || []; + + throw new Error('Unsupported'); } 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; + + throw new Error('Unsupported'); } 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'; - } + throw new Error('Unsupported'); } @throttle(2000) refreshAvailableProfiles(): void { - this._refreshAvailableProfilesNow(); + + throw new Error('Unsupported'); } 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; + throw new Error('Unsupported'); } 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); + throw new Error('Unsupported'); } 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'); + + throw new Error('Unsupported'); } 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)); + + throw new Error('Unsupported'); } 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; + throw new Error('Unsupported'); } 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; -} + throw new Error('Unsupported'); + } -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.membrane.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts deleted file mode 100644 index 983d1ebab22868..00000000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.membrane.ts +++ /dev/null @@ -1,270 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { 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 { 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 { GroupIdentifier } from '../../../common/editor.js'; - -export class TerminalService extends Disposable implements ITerminalService { - readonly _serviceBrand: undefined; - - get isProcessSupportRegistered(): boolean { throw new Error('Unsupported'); } - - get connectionState(): TerminalConnectionState { throw new Error('Unsupported'); } - - // Never resolve - get whenConnected(): Promise { return new Promise(() => { }); } - - get restoredGroupCount(): number { throw new Error('Unsupported'); } - - get instances(): ITerminalInstance[] { - throw new Error('Unsupported'); - } - get foregroundInstances(): ITerminalInstance[] { - throw new Error('Unsupported'); - } - get detachedInstances(): Iterable { - throw new Error('Unsupported'); - } - - getReconnectedTerminals(_reconnectionOwner: string): ITerminalInstance[] | undefined { - return undefined; - } - - get defaultLocation(): TerminalLocation { return TerminalLocation.Panel; } - - get activeInstance(): ITerminalInstance | undefined { - return undefined; - } - - get onDidCreateInstance(): Event { throw new Error('Unsupported'); } - get onDidChangeInstanceDimensions(): Event { throw new Error('Unsupported'); } - get onDidRegisterProcessSupport(): Event { throw new Error('Unsupported'); } - get onDidChangeConnectionState(): Event { throw new Error('Unsupported'); } - get onDidRequestStartExtensionTerminal(): Event { throw new Error('Unsupported'); } - - // ITerminalInstanceHost events - get onDidDisposeInstance(): Event { throw new Error('Unsupported'); } - get onDidFocusInstance(): Event { throw new Error('Unsupported'); } - get onDidChangeActiveInstance(): Event { throw new Error('Unsupported'); } - get onDidChangeInstances(): Event { throw new Error('Unsupported'); } - get onDidChangeInstanceCapability(): Event { throw new Error('Unsupported'); } - - // Terminal view events - get onDidChangeActiveGroup(): Event { throw new Error('Unsupported'); } - - // Multiplexed events - @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 => 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, - // @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(); - - } - - async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise { - throw new Error('Unsupported'); - } - - async initializePrimaryBackend() { - throw new Error('Unsupported'); - } - - getPrimaryBackend(): ITerminalBackend | undefined { - throw new Error('Unsupported'); - } - - async setNextCommandId(id: number, commandLine: string, commandId: string): Promise { - throw new Error('Unsupported'); - } - - setActiveInstance(value: ITerminalInstance) { - throw new Error('Unsupported'); - } - - async focusActiveInstance(): Promise { - throw new Error('Unsupported'); - } - - focusInstance(instance: ITerminalInstance): void { - throw new Error('Unsupported'); - } - - async createContributedTerminalProfile(extensionIdentifier: string, id: string, options: ICreateContributedTerminalProfileOptions): Promise { - throw new Error('Unsupported'); - } - - async safeDisposeTerminal(instance: ITerminalInstance): Promise { - throw new Error('Unsupported'); - } - - async getActiveOrCreateInstance(options?: { acceptsInput?: boolean }): Promise { - throw new Error('Unsupported'); - } - - async revealTerminal(source: ITerminalInstance, preserveFocus?: boolean): Promise { - throw new Error('Unsupported'); - } - - async showBackgroundTerminal(instance: ITerminalInstance, suppressSetActive?: boolean): Promise { - throw new Error('Unsupported'); - } - - async revealActiveTerminal(preserveFocus?: boolean): Promise { - throw new Error('Unsupported'); - } - - setEditable(instance: ITerminalInstance, data?: IEditableData | null): void { - throw new Error('Unsupported'); - } - - isEditable(instance: ITerminalInstance | undefined): boolean { - throw new Error('Unsupported'); - } - - getEditableData(instance: ITerminalInstance): IEditableData | undefined { - throw new Error('Unsupported'); - } - - requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise { - throw new Error('Unsupported'); - } - - setNativeDelegate(nativeDelegate: ITerminalServiceNativeDelegate): void { - throw new Error('Unsupported'); - } - - refreshActiveGroup(): void { - throw new Error('Unsupported'); - } - - getInstanceFromId(terminalId: number): ITerminalInstance | undefined { - throw new Error('Unsupported'); - } - - getInstanceFromIndex(terminalIndex: number): ITerminalInstance { - throw new Error('Unsupported'); - } - - getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { - throw new Error('Unsupported'); - } - - isAttachedToTerminal(remoteTerm: IRemoteTerminalAttachTarget): boolean { - throw new Error('Unsupported'); - } - - moveToEditor(source: ITerminalInstance, group?: GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | AUX_WINDOW_GROUP_TYPE): void { - throw new Error('Unsupported'); - } - - moveIntoNewEditor(source: ITerminalInstance): void { - throw new Error('Unsupported'); - } - - async moveToTerminalView(source?: ITerminalInstance | URI, target?: ITerminalInstance, side?: 'before' | 'after'): Promise { - throw new Error('Unsupported'); - } - - registerProcessSupport(isSupported: boolean): void { - throw new Error('Unsupported'); - } - - protected async _showTerminalCloseConfirmation(singleTerminal?: boolean): Promise { - throw new Error('Unsupported'); - } - - getDefaultInstanceHost(): ITerminalInstanceHost { - throw new Error('Unsupported'); - } - - async getInstanceHost(location: ITerminalLocationOptions | undefined): Promise { - throw new Error('Unsupported'); - } - - async createTerminal(options?: ICreateTerminalOptions): Promise { - throw new Error('Unsupported'); - } - - async createAndFocusTerminal(options?: ICreateTerminalOptions): Promise { - throw new Error('Unsupported'); - } - - async createDetachedTerminal(options: IDetachedXTermOptions): Promise { - throw new Error('Unsupported'); - } - - async resolveLocation(location?: ITerminalLocationOptions): Promise { - throw new Error('Unsupported'); - } - - async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { - throw new Error('Unsupported'); - } - - getEditingTerminal(): ITerminalInstance | undefined { - return undefined; - } - - setEditingTerminal(instance: ITerminalInstance | undefined) { - return undefined; - } - - createOnInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): DynamicListEventMultiplexer { - // 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 }> { - throw new Error('Unsupported'); - } - - openResource(resource: URI): void { - throw new Error('Unsupported'); - } -} 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 } } From 3c011a4a960dda6372cf333cba0f3104fb7ecf9b Mon Sep 17 00:00:00 2001 From: Jhonny Arana Date: Fri, 5 Dec 2025 13:01:47 -0400 Subject: [PATCH 76/80] Fix GazePort and TerminalProfile Signed-off-by: Jhonny Arana --- src/vs/code/browser/workbench/workbench.ts | 8 ++-- .../browser/terminalProfileService.ts | 43 ++++++++----------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 6840799e35c057..779f7506bc6ce1 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -21,7 +21,7 @@ declare const window: Window & { vscodeTargetContainer?: HTMLElement | null; completeInitialization?: () => void; SENTRY_CAPTURE_EXCEPTION?: (error: Error) => void; - gazeExtensionPort?: MessagePort; + extensionToGazePort?: MessagePort; }; type Writeable = { -readonly [P in keyof T]: T[P] }; @@ -38,11 +38,11 @@ type Writeable = { -readonly [P in keyof T]: T[P] }; } // Forward the MessagePort to the extension so it can directly talk to gaze - if (window.gazeExtensionPort) { + if (window.extensionToGazePort) { config.messagePorts = new Map([ - ['membrane.membrane', window.gazeExtensionPort], + ['membrane.membrane', window.extensionToGazePort], ]); - delete window.gazeExtensionPort; + delete window.extensionToGazePort; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts index eaa2640e3ea35c..c3d117846764a5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts @@ -13,23 +13,24 @@ import { IRegisterContributedProfileArgs, ITerminalProfileProvider, ITerminalPro /* * 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; - get onDidChangeAvailableProfiles(): Event { throw new Error('Unsupported'); } + get onDidChangeAvailableProfiles(): Event { return Event.None; } get profilesReady(): Promise { - - throw new Error('Unsupported'); + return Promise.resolve(); } - get availableProfiles(): ITerminalProfile[] { - throw new Error('Unsupported'); + get availableProfiles(): ITerminalProfile[] { + return []; } - get contributedProfiles(): IExtensionTerminalProfile[] { - throw new Error('Unsupported'); + get contributedProfiles(): IExtensionTerminalProfile[] { + return []; } constructor( @@ -46,50 +47,40 @@ export class TerminalProfileService extends Disposable implements ITerminalProfi } getDefaultProfileName(): string | undefined { - - throw new Error('Unsupported'); + return undefined; } getDefaultProfile(os?: OperatingSystem): ITerminalProfile | undefined { - - throw new Error('Unsupported'); + return undefined; } - @throttle(2000) refreshAvailableProfiles(): void { - - throw new Error('Unsupported'); + // No-op } protected async _refreshAvailableProfilesNow(): Promise { - - throw new Error('Unsupported'); + // No-op } getContributedProfileProvider(extensionIdentifier: string, id: string): ITerminalProfileProvider | undefined { - - throw new Error('Unsupported'); + return undefined; } async getPlatformKey(): Promise { - - throw new Error('Unsupported'); + return 'web'; } registerTerminalProfileProvider(extensionIdentifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable { - - throw new Error('Unsupported'); + return { dispose: () => { } }; } async registerContributedProfile(args: IRegisterContributedProfileArgs): Promise { - - throw new Error('Unsupported'); + // No-op } async getContributedDefaultProfile(shellLaunchConfig: IShellLaunchConfig): Promise { - - throw new Error('Unsupported'); + return undefined; } } From b9c4bcb6d5c273602a964ef7b9ad2df6ca707f22 Mon Sep 17 00:00:00 2001 From: Jhonny Arana Date: Mon, 8 Dec 2025 10:34:56 -0400 Subject: [PATCH 77/80] Include typescript basics. --- build/lib/extensions.js | 3 ++- build/lib/extensions.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 74761bd0d7557a..9e690d46a99e82 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -376,6 +376,7 @@ const allowedExtensions = [ 'json-language-features', 'log', 'markdown', + 'markdown-basics', 'markdown-language-features', 'markdown-math', 'media-preview', @@ -392,6 +393,7 @@ const allowedExtensions = [ 'theme-solarized-dark', 'theme-solarized-light', 'typescript', + 'typescript-basics', 'typescript-language-features', 'xml', 'yaml', @@ -399,7 +401,6 @@ const allowedExtensions = [ function isAllowedInMembrane(name) { return allowedExtensions.some(allowedExtensionName => allowedExtensionName === name); } -exports.isAllowedInMembrane = isAllowedInMembrane; /** * 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 diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 429276f1d7b7f9..e4ec1e7764d9ff 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -387,6 +387,7 @@ const allowedExtensions = [ 'json-language-features', 'log', 'markdown', + 'markdown-basics', 'markdown-language-features', 'markdown-math', 'media-preview', @@ -403,6 +404,7 @@ const allowedExtensions = [ 'theme-solarized-dark', 'theme-solarized-light', 'typescript', + 'typescript-basics', 'typescript-language-features', 'xml', 'yaml', From ab38b9cd424189d633d3aedf6e6c5eb53eb06891 Mon Sep 17 00:00:00 2001 From: Jhonny Arana Date: Thu, 11 Dec 2025 21:58:37 -0400 Subject: [PATCH 78/80] Hide titlebar. --- src/vs/workbench/browser/workbench.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index bddcd8182c064c..d904d14b3092d6 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -339,7 +339,8 @@ export class Workbench extends Layout { // Create Parts for (const { id, role, classes, options } of [ - { id: Parts.TITLEBAR_PART, role: 'none', classes: ['titlebar'] }, + // MEMBRANE: Disabled titlebar to remove the entire title bar with controls and menu + // { id: Parts.TITLEBAR_PART, role: 'none', classes: ['titlebar'] }, { id: Parts.BANNER_PART, role: 'banner', classes: ['banner'] }, { id: Parts.ACTIVITYBAR_PART, role: 'none', classes: ['activitybar', this.getSideBarPosition() === Position.LEFT ? 'left' : 'right'] }, // Use role 'none' for some parts to make screen readers less chatty #114892 { id: Parts.SIDEBAR_PART, role: 'none', classes: ['sidebar', this.getSideBarPosition() === Position.LEFT ? 'left' : 'right'] }, From 6b049033720a580c6b9d64604a701554dc3dc292 Mon Sep 17 00:00:00 2001 From: Jhonny Arana Date: Thu, 11 Dec 2025 22:34:29 -0400 Subject: [PATCH 79/80] Fix disable editor title bar --- src/vs/workbench/browser/layout.ts | 24 +++++++++++++++++------- src/vs/workbench/browser/workbench.ts | 3 +-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index e5571eeb093a04..ced65232caaa52 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); @@ -1577,6 +1580,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.mainContainer.setAttribute('role', 'application'); 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 => { @@ -2242,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); @@ -2415,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 { @@ -2546,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', diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index d904d14b3092d6..bddcd8182c064c 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -339,8 +339,7 @@ export class Workbench extends Layout { // Create Parts for (const { id, role, classes, options } of [ - // MEMBRANE: Disabled titlebar to remove the entire title bar with controls and menu - // { id: Parts.TITLEBAR_PART, role: 'none', classes: ['titlebar'] }, + { id: Parts.TITLEBAR_PART, role: 'none', classes: ['titlebar'] }, { id: Parts.BANNER_PART, role: 'banner', classes: ['banner'] }, { id: Parts.ACTIVITYBAR_PART, role: 'none', classes: ['activitybar', this.getSideBarPosition() === Position.LEFT ? 'left' : 'right'] }, // Use role 'none' for some parts to make screen readers less chatty #114892 { id: Parts.SIDEBAR_PART, role: 'none', classes: ['sidebar', this.getSideBarPosition() === Position.LEFT ? 'left' : 'right'] }, From 1618b2c5edf8e9ce627caccf4b395b0283a1061d Mon Sep 17 00:00:00 2001 From: Jhonny Arana Date: Thu, 11 Dec 2025 22:35:19 -0400 Subject: [PATCH 80/80] Fix white space --- src/vs/workbench/browser/layout.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index ced65232caaa52..731f38984c6f70 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1580,7 +1580,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.mainContainer.setAttribute('role', 'application'); this.workbenchGrid = workbenchGrid; this.workbenchGrid.edgeSnapping = this.state.runtime.mainWindowFullscreen; - + // MEMBRANE: Ensure titlebar is hidden from the start this.workbenchGrid.setViewVisible(this.titleBarPartView, false);