From 3de96bb9058bbc593a0dce212869f86eeb1cf1a7 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 21 Nov 2023 09:15:23 +0100 Subject: [PATCH] [vscode] Support env.onDidChangeShell event contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + packages/plugin-ext/src/common/plugin-api-rpc.ts | 1 + .../plugin-ext/src/main/browser/terminal-main.ts | 4 ++++ packages/plugin-ext/src/plugin/env.ts | 8 -------- packages/plugin-ext/src/plugin/plugin-context.ts | 5 ++++- packages/plugin-ext/src/plugin/plugin-manager.ts | 2 +- packages/plugin-ext/src/plugin/terminal-ext.ts | 15 +++++++++++++++ packages/plugin/src/theia.d.ts | 10 +++++++++- .../terminal/src/browser/base/terminal-service.ts | 2 ++ .../src/browser/shell-terminal-profile.ts | 13 +++++++++++-- .../src/browser/terminal-frontend-contribution.ts | 7 +++++++ .../src/browser/terminal-profile-service.ts | 10 ++++++++++ 12 files changed, 65 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 927adddc387aa..b1bff53bc5cff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ## v1.44.0 - [task] prevent task widget title from being changed by task process [#13003](https://github.com/eclipse-theia/theia/pull/13003) +- [vscode] add support to env.ondidChangeShell event [#13097](https://github.com/eclipse-theia/theia/pull/13097) - contributed on behalf of STMicroelectronics ## v1.43.0 - 10/26/2023 diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index c0ce507624058..03031c4ca30a1 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -296,6 +296,7 @@ export interface TerminalServiceExt { $provideTerminalLinks(line: string, terminalId: string, token: theia.CancellationToken): Promise; $handleTerminalLink(link: ProvidedTerminalLink): Promise; getEnvironmentVariableCollection(extensionIdentifier: string): theia.GlobalEnvironmentVariableCollection; + $setShell(shell: string): void; } export interface OutputChannelRegistryExt { createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel, diff --git a/packages/plugin-ext/src/main/browser/terminal-main.ts b/packages/plugin-ext/src/main/browser/terminal-main.ts index d894afd779f03..6ad478c6f7e02 100644 --- a/packages/plugin-ext/src/main/browser/terminal-main.ts +++ b/packages/plugin-ext/src/main/browser/terminal-main.ts @@ -64,6 +64,10 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin this.pluginTerminalRegistry.startCallback = id => this.startProfile(id); container.bind(TerminalLinkProvider).toDynamicValue(() => this); + + this.toDispose.push(this.terminals.onDidChangeShell(shell => { + this.extProxy.$setShell(shell); + })); } async startProfile(id: string): Promise { diff --git a/packages/plugin-ext/src/plugin/env.ts b/packages/plugin-ext/src/plugin/env.ts index 80547683d52fa..8fe8494a49c47 100644 --- a/packages/plugin-ext/src/plugin/env.ts +++ b/packages/plugin-ext/src/plugin/env.ts @@ -25,7 +25,6 @@ export abstract class EnvExtImpl { private queryParameters: QueryParameters; private lang: string; private applicationName: string; - private defaultShell: string; private ui: theia.UIKind; private envMachineId: string; private envSessionId: string; @@ -68,10 +67,6 @@ export abstract class EnvExtImpl { this.lang = lang; } - setShell(shell: string): void { - this.defaultShell = shell; - } - setUIKind(uiKind: theia.UIKind): void { this.ui = uiKind; } @@ -112,9 +107,6 @@ export abstract class EnvExtImpl { get uriScheme(): string { return 'theia'; } - get shell(): string { - return this.defaultShell; - } get uiKind(): theia.UIKind { return this.ui; } diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index f972127dbec48..448183017274d 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -796,7 +796,10 @@ export function createAPIFactory( get machineId(): string { return envExt.machineId; }, get sessionId(): string { return envExt.sessionId; }, get uriScheme(): string { return envExt.uriScheme; }, - get shell(): string { return envExt.shell; }, + get shell(): string { return terminalExt.defaultShell; }, + get onDidChangeShell(): theia.Event { + return terminalExt.onDidChangeShell; + }, get uiKind(): theia.UIKind { return envExt.uiKind; }, clipboard, getEnvVariable(envVarName: string): PromiseLike { diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index 3d3c5296bdc7a..e3a95e87970de 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -206,7 +206,7 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { this.envExt.setQueryParameters(params.env.queryParams); this.envExt.setLanguage(params.env.language); - this.envExt.setShell(params.env.shell); + this.terminalService.$setShell(params.env.shell); this.envExt.setUIKind(params.env.uiKind); this.envExt.setApplicationName(params.env.appName); this.envExt.setAppHost(params.env.appHost); diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index c48e618a0e651..7c158fd36a8cb 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -71,6 +71,10 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { protected environmentVariableCollections: MultiKeyMap = new MultiKeyMap(2); + private shell: string; + private readonly onDidChangeShellEmitter = new Emitter(); + readonly onDidChangeShell: theia.Event = this.onDidChangeShellEmitter.event; + constructor(rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.TERMINAL_MAIN); } @@ -79,6 +83,17 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { return [...this._terminals.values()]; } + get defaultShell(): string { + return this.shell || ''; + } + + async $setShell(shell: string): Promise { + if (this.shell !== shell) { + this.shell = shell; + this.onDidChangeShellEmitter.fire(shell); + } + } + createTerminal( nameOrOptions: TerminalOptions | PseudoTerminalOptions | ExtensionTerminalOptions | (string | undefined), shellPath?: string, shellArgs?: string[] | string diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 15e5c628c7c90..d26356b3100b4 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -7720,6 +7720,12 @@ export module '@theia/plugin' { */ export const isTelemetryEnabled: boolean; + /** + * An {@link Event} which fires when the default shell changes. This fires with the new + * shell path. + */ + export const onDidChangeShell: Event; + /** * An {@link Event} which fires when the user enabled or disables telemetry. * `true` if the user has enabled telemetry or `false` if the user has disabled telemetry. @@ -7747,7 +7753,9 @@ export module '@theia/plugin' { export const remoteName: string | undefined; /** - * The detected default shell for the extension host. + * The detected default shell for the extension host, this is overridden by the + * `terminal.integrated.defaultProfile` setting for the extension host's platform. Note that in + * environments that do not support a shell the value is the empty string. */ export const shell: string; diff --git a/packages/terminal/src/browser/base/terminal-service.ts b/packages/terminal/src/browser/base/terminal-service.ts index 02f8e17ec4e68..785a9e5c188d4 100644 --- a/packages/terminal/src/browser/base/terminal-service.ts +++ b/packages/terminal/src/browser/base/terminal-service.ts @@ -50,6 +50,8 @@ export interface TerminalService { */ getDefaultShell(): Promise; + readonly onDidChangeShell: Event + readonly onDidCreateTerminal: Event; readonly currentTerminal: TerminalWidget | undefined; diff --git a/packages/terminal/src/browser/shell-terminal-profile.ts b/packages/terminal/src/browser/shell-terminal-profile.ts index aaefd630bd67d..962f1ffbae2ab 100644 --- a/packages/terminal/src/browser/shell-terminal-profile.ts +++ b/packages/terminal/src/browser/shell-terminal-profile.ts @@ -14,13 +14,16 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { URI } from '@theia/core'; +import { URI, isObject } from '@theia/core'; import { TerminalService } from './base/terminal-service'; import { TerminalWidget, TerminalWidgetOptions } from './base/terminal-widget'; import { TerminalProfile } from './terminal-profile-service'; export class ShellTerminalProfile implements TerminalProfile { - constructor(protected readonly terminalService: TerminalService, protected readonly options: TerminalWidgetOptions) { } + + readonly shellPath: string; + + constructor(protected readonly terminalService: TerminalService, protected readonly options: TerminalWidgetOptions) { this.shellPath = options.shellPath!; } async start(): Promise { const widget = await this.terminalService.newTerminal(this.options); @@ -38,3 +41,9 @@ export class ShellTerminalProfile implements TerminalProfile { return new ShellTerminalProfile(this.terminalService, { ...this.options, ...options }); } } + +export namespace ShellTerminalProfile { + export function is(candidate: unknown): candidate is ShellTerminalProfile { + return isObject(candidate) && 'shellPath' in candidate; + } +} diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts index 2263f1402e692..10f362694ecbd 100644 --- a/packages/terminal/src/browser/terminal-frontend-contribution.ts +++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts @@ -220,6 +220,9 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu protected readonly onDidChangeCurrentTerminalEmitter = new Emitter(); readonly onDidChangeCurrentTerminal: Event = this.onDidChangeCurrentTerminalEmitter.event; + protected readonly onDidChangeShellEmitter = new Emitter(); + readonly onDidChangeShell: Event = this.onDidChangeShellEmitter.event; + @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; @@ -253,6 +256,9 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu } }); }); + this.profileService.onDidChangeDefaultShell(async shell => { + this.onDidChangeShellEmitter.fire(shell !== '' ? shell : await this.getDefaultShell()); + }); } get terminalHideSearch(): boolean { @@ -1021,6 +1027,7 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu } this.preferenceService.set(`terminal.integrated.defaultProfile.${OS.backend.type().toLowerCase()}`, result[0], PreferenceScope.User); + this.profileService.setDefaultProfile(result[0]); } protected async openActiveWorkspaceTerminal(options?: ApplicationShell.WidgetOptions): Promise { diff --git a/packages/terminal/src/browser/terminal-profile-service.ts b/packages/terminal/src/browser/terminal-profile-service.ts index 5d8094ce24021..f39dfa06d9c70 100644 --- a/packages/terminal/src/browser/terminal-profile-service.ts +++ b/packages/terminal/src/browser/terminal-profile-service.ts @@ -17,6 +17,7 @@ import { Emitter, Event } from '@theia/core'; import { injectable } from '@theia/core/shared/inversify'; import { TerminalWidget } from './base/terminal-widget'; +import { ShellTerminalProfile } from './shell-terminal-profile'; export const TerminalProfileService = Symbol('TerminalProfileService'); export const ContributedTerminalProfileStore = Symbol('ContributedTerminalProfileStore'); @@ -36,6 +37,7 @@ export interface TerminalProfileService { getProfile(id: string): TerminalProfile | undefined readonly all: [string, TerminalProfile][]; setDefaultProfile(id: string): void; + readonly onDidChangeDefaultShell: Event; readonly defaultProfile: TerminalProfile | undefined; } @@ -87,9 +89,11 @@ export class DefaultTerminalProfileService implements TerminalProfileService { protected readonly onAddedEmitter: Emitter = new Emitter(); protected readonly onRemovedEmitter: Emitter = new Emitter(); + protected readonly onDidChangeDefaultShellEmitter: Emitter = new Emitter(); onAdded: Event = this.onAddedEmitter.event; onRemoved: Event = this.onRemovedEmitter.event; + onDidChangeDefaultShell: Event = this.onDidChangeDefaultShellEmitter.event; constructor(...stores: TerminalProfileStore[]) { this.stores = stores; @@ -144,6 +148,12 @@ export class DefaultTerminalProfileService implements TerminalProfileService { throw new Error(`Cannot set default to unknown profile '${id}' `); } this.defaultProfileIndex = this.order.indexOf(id); + + if (ShellTerminalProfile.is(profile) && profile.shellPath) { + this.onDidChangeDefaultShellEmitter.fire(profile.shellPath!); + } else { + this.onDidChangeDefaultShellEmitter.fire(''); + } } getProfile(id: string): TerminalProfile | undefined {