diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index d5407e7af06..3c9c10db0d3 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event'; import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export const enum TerminalSettingId { ShellLinux = 'terminal.integrated.shell.linux', @@ -116,7 +117,7 @@ export interface IPtyHostAttachTarget { workspaceId: string; workspaceName: string; isOrphan: boolean; - icon: string | undefined; + icon: TerminalIcon | undefined; } export type ITerminalsLayoutInfo = IRawTerminalsLayoutInfo; @@ -174,7 +175,7 @@ export interface IOffProcessTerminalService { getShellEnvironment(): Promise; setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): Promise; updateTitle(id: number, title: string): Promise; - updateIcon(id: number, icon: string, color?: string): Promise; + updateIcon(id: number, icon: TerminalIcon, color?: string): Promise; getTerminalLayoutInfo(): Promise; reduceConnectionGraceTime(): Promise; } @@ -240,7 +241,7 @@ export interface IPtyService { /** Confirm the process is _not_ an orphan. */ orphanQuestionReply(id: number): Promise; updateTitle(id: number, title: string): Promise; - updateIcon(id: number, icon: string, color?: string): Promise; + updateIcon(id: number, icon: TerminalIcon, color?: string): Promise; getDefaultSystemShell(osOverride?: OperatingSystem): Promise; getProfiles?(includeDetectedProfiles?: boolean): Promise; getEnvironment(): Promise; @@ -348,7 +349,7 @@ export interface IShellLaunchConfig { /** * This is a terminal that attaches to an already running terminal. */ - attachPersistentProcess?: { id: number; pid: number; title: string; cwd: string; icon?: string; color?: string }; + attachPersistentProcess?: { id: number; pid: number; title: string; cwd: string; icon?: TerminalIcon; color?: string }; /** * Whether the terminal process environment should be exactly as provided in @@ -388,10 +389,9 @@ export interface IShellLaunchConfig { isExtensionOwnedTerminal?: boolean; /** - * The codicon ID to use for this terminal. If not specified it will use the default fallback - * icon. + * The icon for the terminal, used primarily in the terminal tab. */ - icon?: string; + icon?: TerminalIcon; /** * The color ID to use for this terminal. If not specified it will use the default fallback @@ -399,6 +399,8 @@ export interface IShellLaunchConfig { color?: string; } +export type TerminalIcon = ThemeIcon | URI | { light: URI; dark: URI }; + export interface IShellLaunchConfigDto { name?: string; executable?: string; @@ -549,7 +551,7 @@ export interface ITerminalProfile { args?: string | string[] | undefined; env?: ITerminalEnvironment; overrideName?: boolean; - icon?: string; + icon?: ThemeIcon | URI | { light: URI, dark: URI }; } export interface ITerminalDimensionsOverride extends Readonly { @@ -570,7 +572,7 @@ export interface IBaseUnresolvedTerminalProfile { args?: string | string[] | undefined; isAutoDetected?: boolean; overrideName?: boolean; - icon?: string; + icon?: ThemeIcon | URI | { light: URI, dark: URI }; env?: ITerminalEnvironment; } diff --git a/src/vs/platform/terminal/common/terminalProcess.ts b/src/vs/platform/terminal/common/terminalProcess.ts index 3fd0e6bb77b..914e85c8a94 100644 --- a/src/vs/platform/terminal/common/terminalProcess.ts +++ b/src/vs/platform/terminal/common/terminalProcess.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { UriComponents } from 'vs/base/common/uri'; -import { IRawTerminalTabLayoutInfo, ITerminalEnvironment, ITerminalTabLayoutInfoById } from 'vs/platform/terminal/common/terminal'; +import { IRawTerminalTabLayoutInfo, ITerminalEnvironment, ITerminalTabLayoutInfoById, TerminalIcon } from 'vs/platform/terminal/common/terminal'; import { ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; export interface ISingleTerminalConfiguration { @@ -55,7 +55,7 @@ export interface IProcessDetails { workspaceId: string; workspaceName: string; isOrphan: boolean; - icon: string | undefined; + icon: TerminalIcon | undefined; color: string | undefined; } diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index b57f55bc25e..b9ad90e8602 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -5,7 +5,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, TerminalIpcChannels, IHeartbeatService, HeartbeatConstants, TerminalShellType, ITerminalProfile, IRequestResolveVariablesEvent, SafeConfigProvider, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, TerminalIpcChannels, IHeartbeatService, HeartbeatConstants, TerminalShellType, ITerminalProfile, IRequestResolveVariablesEvent, SafeConfigProvider, TerminalSettingId, TerminalIcon } from 'vs/platform/terminal/common/terminal'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { FileAccess } from 'vs/base/common/network'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -170,7 +170,7 @@ export class PtyHostService extends Disposable implements IPtyService { updateTitle(id: number, title: string): Promise { return this._proxy.updateTitle(id, title); } - updateIcon(id: number, icon: string, color?: string): Promise { + updateIcon(id: number, icon: TerminalIcon, color?: string): Promise { return this._proxy.updateIcon(id, icon, color); } attachToProcess(id: number): Promise { diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 1d06ff418eb..950d09a069a 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -5,7 +5,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IProcessEnvironment, isWindows, OperatingSystem, OS } from 'vs/base/common/platform'; -import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, LocalReconnectConstants, ITerminalsLayoutInfo, IRawTerminalInstanceLayoutInfo, ITerminalTabLayoutInfoById, ITerminalInstanceLayoutInfoById, TerminalShellType, IProcessReadyEvent } from 'vs/platform/terminal/common/terminal'; +import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, LocalReconnectConstants, ITerminalsLayoutInfo, IRawTerminalInstanceLayoutInfo, ITerminalTabLayoutInfoById, ITerminalInstanceLayoutInfoById, TerminalShellType, IProcessReadyEvent, TerminalIcon } from 'vs/platform/terminal/common/terminal'; import { AutoOpenBarrier, Queue, RunOnceScheduler } from 'vs/base/common/async'; import { Emitter } from 'vs/base/common/event'; import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; @@ -17,6 +17,7 @@ import { getSystemShell } from 'vs/base/node/shell'; import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; import { execFile } from 'child_process'; import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; +import { URI } from 'vs/base/common/uri'; type WorkspaceId = string; @@ -118,7 +119,7 @@ export class PtyService extends Disposable implements IPtyService { this._throwIfNoPty(id).setTitle(title); } - async updateIcon(id: number, icon: string, color?: string): Promise { + async updateIcon(id: number, icon: URI | { light: URI; dark: URI } | { id: string, color?: { id: string } }, color?: string): Promise { this._throwIfNoPty(id).setIcon(icon, color); } @@ -303,14 +304,14 @@ export class PersistentTerminalProcess extends Disposable { get pid(): number { return this._pid; } get title(): string { return this._title || this._terminalProcess.currentTitle; } - get icon(): string | undefined { return this._icon; } + get icon(): TerminalIcon | undefined { return this._icon; } get color(): string | undefined { return this._color; } setTitle(title: string): void { this._title = title; } - setIcon(icon: string, color?: string): void { + setIcon(icon: TerminalIcon, color?: string): void { this._icon = icon; this._color = color; } @@ -323,7 +324,7 @@ export class PersistentTerminalProcess extends Disposable { readonly shouldPersistTerminal: boolean, cols: number, rows: number, private readonly _logService: ILogService, - private _icon?: string, + private _icon?: TerminalIcon, private _color?: string ) { super(); diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index 150b2dca5bf..f2777cd794b 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -13,6 +13,8 @@ import * as pfs from 'vs/base/node/pfs'; import { ITerminalEnvironment, ITerminalProfile, ITerminalProfileObject, ProfileSource, SafeConfigProvider, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { Codicon } from 'vs/base/common/codicons'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { URI } from 'vs/base/common/uri'; let profileSources: Map | undefined; @@ -80,12 +82,12 @@ async function detectAvailableWindowsProfiles( if (includeDetectedProfiles) { detectedProfiles.set('PowerShell', { source: ProfileSource.Pwsh, - icon: Codicon.terminalPowershell.id, + icon: Codicon.terminalPowershell, isAutoDetected: true }); detectedProfiles.set('Windows PowerShell', { path: `${system32Path}\\WindowsPowerShell\\v1.0\\powershell.exe`, - icon: Codicon.terminalPowershell.id, + icon: Codicon.terminalPowershell, isAutoDetected: true }); detectedProfiles.set('Git Bash', { @@ -102,7 +104,7 @@ async function detectAvailableWindowsProfiles( }); detectedProfiles.set('Command Prompt', { path: `${system32Path}\\cmd.exe`, - icon: Codicon.terminalCmd.id, + icon: Codicon.terminalCmd, isAutoDetected: true }); } @@ -137,7 +139,7 @@ async function transformToTerminalProfiles( if (profile === null) { continue; } let originalPaths: string[]; let args: string[] | string | undefined; - let icon: string | undefined; + let icon: ThemeIcon | URI | { light: URI, dark: URI } | undefined = undefined; if ('source' in profile) { const source = profileSources?.get(profile.source); if (!source) { @@ -147,11 +149,15 @@ async function transformToTerminalProfiles( // if there are configured args, override the default ones args = profile.args || source.args; - icon = profile.icon || source.icon; + if (profile.icon) { + icon = profile.icon; + } else if (source.icon) { + icon = source.icon; + } } else { originalPaths = Array.isArray(profile.path) ? profile.path : [profile.path]; args = isWindows ? profile.args : Array.isArray(profile.args) ? profile.args : undefined; - icon = profile.icon; + icon = profile.icon || undefined; } const paths = (await variableResolver?.(originalPaths)) || originalPaths.slice(); @@ -190,7 +196,7 @@ async function initializeWindowsProfiles(): Promise { profileSources.set('PowerShell', { profileName: 'PowerShell', paths: await getPowershellPaths(), - icon: 'terminal-powershell' + icon: ThemeIcon.asThemeIcon(Codicon.terminalPowershell) }); } @@ -240,12 +246,12 @@ async function getWslProfiles(wslPath: string, defaultProfileName: string | unde isDefault: profileName === defaultProfileName }; if (distroName.includes('Ubuntu')) { - profile.icon = 'terminal-ubuntu'; + profile.icon = ThemeIcon.asThemeIcon(Codicon.terminalUbuntu); } else if (distroName.includes('Debian')) { - profile.icon = 'terminal-debian'; + profile.icon = ThemeIcon.asThemeIcon(Codicon.terminalDebian); } else { - profile.icon = 'terminal-linux'; + profile.icon = ThemeIcon.asThemeIcon(Codicon.terminalLinux); } // Add the profile @@ -343,5 +349,5 @@ interface IPotentialTerminalProfile { profileName: string; paths: string[]; args?: string[]; - icon?: string; + icon?: ThemeIcon | URI | { light: URI, dark: URI }; } diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 4032bab91fc..9867c464ae3 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -67,6 +67,10 @@ export namespace ThemeIcon { return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id; } + export function asThemeIcon(codicon: Codicon): ThemeIcon { + return { id: codicon.id }; + } + export const asClassNameArray: (icon: ThemeIcon) => string[] = CSSIcon.asClassNameArray; export const asClassName: (icon: ThemeIcon) => string = CSSIcon.asClassName; export const asCSSSelector: (icon: ThemeIcon) => string = CSSIcon.asCSSSelector; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index b2985b4990f..138eb225334 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -891,9 +891,16 @@ declare module 'vscode' { export interface TerminalOptions { /** - * A codicon ID to associate with this terminal. + * The icon path or {@link ThemeIcon} for the terminal. */ - readonly icon?: string; + readonly iconPath?: Uri | { light: Uri; dark: Uri } | { id: string, color?: { id: string } }; + } + + export interface ExtensionTerminalOptions { + /** + * A themeIcon, Uri, or light and dark Uris to use as the terminal tab icon + */ + readonly iconPath?: Uri | { light: Uri; dark: Uri } | { id: string, color?: { id: string } }; } //#endregion diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 9bde67e9b9f..094d3f2ae32 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -639,7 +639,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I if ('pty' in nameOrOptions) { return extHostTerminalService.createExtensionTerminal(nameOrOptions); } - if (nameOrOptions.icon) { + if (nameOrOptions.iconPath) { checkProposedApiEnabled(extension); } return extHostTerminalService.createTerminalFromOptions(nameOrOptions); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 4cb3d5e300d..f9068ca9931 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -40,7 +40,7 @@ import { ProvidedPortAttributes, TunnelCreationOptions, TunnelOptions, TunnelPro import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile } from 'vs/platform/terminal/common/terminal'; -import { ThemeColor } from 'vs/platform/theme/common/themeService'; +import { ThemeColor, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync'; import { WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; @@ -454,7 +454,7 @@ export interface TerminalLaunchConfig { shellArgs?: string[] | string; cwd?: string | UriComponents; env?: ITerminalEnvironment; - icon?: string; + icon?: URI | { light: URI; dark: URI } | ThemeIcon; initialText?: string; waitOnExit?: boolean; strictEnv?: boolean; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 25b677b7041..be6ce95e65f 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -20,6 +20,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IProcessReadyEvent, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, TerminalShellType } from 'vs/platform/terminal/common/terminal'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable { @@ -116,7 +117,7 @@ export class ExtHostTerminal { shellArgs?: string[] | string, cwd?: string | URI, env?: ITerminalEnvironment, - icon?: string, + icon?: URI | { light: URI; dark: URI } | ThemeIcon, initialText?: string, waitOnExit?: boolean, strictEnv?: boolean, @@ -130,11 +131,11 @@ export class ExtHostTerminal { await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, icon, initialText, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal, isExtensionOwnedTerminal }); } - public async createExtensionTerminal(): Promise { + public async createExtensionTerminal(iconPath?: URI | { light: URI; dark: URI } | ThemeIcon): Promise { if (typeof this._id !== 'string') { throw new Error('Terminal has already been created'); } - await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionCustomPtyTerminal: true }); + await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionCustomPtyTerminal: true, icon: iconPath }); // At this point, the id has been set via `$acceptTerminalOpened` if (typeof this._id === 'string') { throw new Error('Terminal creation failed'); @@ -348,7 +349,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name); const p = new ExtHostPseudoterminal(options.pty); - terminal.createExtensionTerminal().then(id => { + terminal.createExtensionTerminal(options.iconPath).then(id => { const disposable = this._setupExtHostProcessListeners(id, p); this._terminalProcessDisposables[id] = disposable; }); diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 91043450d7e..8173c051ff2 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -32,7 +32,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { withNullAsUndefined(options.shellArgs), withNullAsUndefined(options.cwd), withNullAsUndefined(options.env), - withNullAsUndefined(options.icon), + withNullAsUndefined(options.iconPath), withNullAsUndefined(options.message), /*options.waitOnExit*/ undefined, withNullAsUndefined(options.strictEnv), diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 0ee3da84157..09697c78813 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1209,7 +1209,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } if (this.currentTask.shellLaunchConfig) { - this.currentTask.shellLaunchConfig.icon = 'tools'; + this.currentTask.shellLaunchConfig.icon = { id: 'tools' }; } let prefersSameTerminal = presentationOptions.panel === PanelKind.Dedicated; diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 1b1291a4c67..58cd1e99f25 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -290,6 +290,25 @@ color: inherit; } +.monaco-workbench .pane-body.integrated-terminal .tabs-container.has-text .tabs-list .terminal-tabs-entry .uri-icon { + background-repeat: no-repeat; + background-size: contain; + margin-right: 4px; + height: 100%; +} + +.monaco-workbench .terminal-uri-icon .monaco-highlighted-label .codicon, +.monaco-action-bar .terminal-uri-icon.single-terminal-tab.action-label .codicon { + background-size: 16px; +} +.monaco-workbench .terminal-uri-icon .monaco-highlighted-label .codicon::before, +.monaco-action-bar .terminal-uri-icon.single-terminal-tab.action-label:not(.alt-command) .codicon::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; +} + .monaco-workbench .pane-body.integrated-terminal .terminal-drop-overlay { display: block; position: absolute; diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts index 9495b9f928f..3ed3747df3d 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts @@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILogService } from 'vs/platform/log/common/log'; import { INotificationHandle, INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { IRequestResolveVariablesEvent, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal'; +import { IRequestResolveVariablesEvent, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalIcon } from 'vs/platform/terminal/common/terminal'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { RemotePty } from 'vs/workbench/contrib/terminal/browser/remotePty'; import { IRemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -215,7 +215,7 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal await this._remoteTerminalChannel?.updateTitle(id, title); } - async updateIcon(id: number, icon: string, color?: string): Promise { + async updateIcon(id: number, icon: TerminalIcon, color?: string): Promise { await this._remoteTerminalChannel?.updateIcon(id, icon, color); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 632674fa598..6c2315fc798 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -8,7 +8,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IOffProcessTerminalService, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal'; +import { IOffProcessTerminalService, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, TerminalIcon, TerminalShellType } from 'vs/platform/terminal/common/terminal'; import { ICommandTracker, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalProcessExtHostProxy, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as XTermTerminal } from 'xterm'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; @@ -17,7 +17,6 @@ import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { ICompleteTerminalConfiguration } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; export const ITerminalService = createDecorator('terminalService'); export const ITerminalInstanceService = createDecorator('terminalInstanceService'); @@ -267,7 +266,7 @@ export interface ITerminalInstance { readonly rows: number; readonly maxCols: number; readonly maxRows: number; - readonly icon?: ThemeIcon; + readonly icon?: TerminalIcon; readonly color?: string; readonly statusList: ITerminalStatusList; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalIcon.ts b/src/vs/workbench/contrib/terminal/browser/terminalIcon.ts new file mode 100644 index 00000000000..b4b18a7b9bd --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalIcon.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { hash } from 'vs/base/common/hash'; +import { URI } from 'vs/base/common/uri'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; + + +export function getColorClass(terminal: ITerminalInstance): string | undefined { + let color = undefined; + if (terminal.color) { + color = terminal.color; + } else if (ThemeIcon.isThemeIcon(terminal.icon) && terminal.icon.color) { + color = terminal.icon.color.id.replace('.', '_'); + } + if (color) { + return `terminal-icon-${color}`; + } + return undefined; +} + +export function getUriClasses(terminal: ITerminalInstance, colorScheme: ColorScheme): string[] | undefined { + const icon = terminal.icon; + if (!icon) { + return undefined; + } + const iconClasses: string[] = []; + let uri = undefined; + if (icon instanceof URI) { + uri = icon; + } else if (icon instanceof Object && 'light' in icon && 'dark' in icon) { + uri = colorScheme === ColorScheme.LIGHT ? icon.light : icon.dark; + } + if (uri instanceof URI) { + const uriIconKey = hash(uri.path).toString(36); + const className = `terminal-uri-icon-${uriIconKey}`; + iconClasses.push(className); + iconClasses.push(`terminal-uri-icon`); + } + return iconClasses; +} + +export function getIconId(terminal: ITerminalInstance): string { + if (!terminal.icon || (terminal.icon instanceof Object && !('id' in terminal.icon))) { + return Codicon.terminal.id; + } + return terminal.icon.id; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index ec26541a6c5..fb2acc2531f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -21,7 +21,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, TERMINAL_CREATION_COMMANDS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, SUGGESTED_RENDERER_TYPE, ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -46,7 +46,7 @@ import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTy import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType, TerminalSettingId, TerminalIcon } from 'vs/platform/terminal/common/terminal'; import { IProductService } from 'vs/platform/product/common/productService'; import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { AutoOpenBarrier } from 'vs/base/common/async'; @@ -187,7 +187,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { get isDisconnected(): boolean { return this._processManager.isDisconnected; } get isRemote(): boolean { return this._processManager.remoteAuthority !== undefined; } get title(): string { return this._title; } - get icon(): ThemeIcon | undefined { return this._getIcon(); } + get icon(): TerminalIcon | undefined { return this._getIcon(); } get color(): string | undefined { return this._getColor(); } private readonly _onExit = new Emitter(); @@ -320,17 +320,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { })); } - private _getIcon(): Codicon | undefined { - if (this.shellLaunchConfig.icon) { - return iconRegistry.get(this.shellLaunchConfig.icon); + private _getIcon(): TerminalIcon | undefined { + const icon = this._shellLaunchConfig.icon || this._shellLaunchConfig.attachPersistentProcess?.icon; + if (!icon) { + return this._processManager.processState >= ProcessState.Launching ? Codicon.terminal : undefined; } - if (this.shellLaunchConfig?.attachPersistentProcess?.icon) { - return iconRegistry.get(this.shellLaunchConfig.attachPersistentProcess.icon); - } - if (this._processManager.processState >= ProcessState.Launching) { - return Codicon.terminal; - } - return undefined; + return icon; } private _getColor(): string | undefined { @@ -1804,8 +1799,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { title: nls.localize('changeTerminalIcon', "Change Icon"), matchOnDescription: true }); - if (result) { - this.shellLaunchConfig.icon = result.description; + if (result && result.description) { + this.shellLaunchConfig.icon = iconRegistry.get(result.description); this._onIconChanged.fire(this); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts index 399cf6b2f63..71a5445705e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts @@ -13,12 +13,14 @@ import { IRemoteTerminalService, ITerminalService } from 'vs/workbench/contrib/t import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IProcessEnvironment, OperatingSystem, OS } from 'vs/base/common/platform'; -import { IShellLaunchConfig, ITerminalProfile, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalProfile, TerminalIcon, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IShellLaunchConfigResolveOptions, ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; import * as path from 'vs/base/common/path'; import { Codicon, iconRegistry } from 'vs/base/common/codicons'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { debounce } from 'vs/base/common/decorators'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { URI, UriComponents } from 'vs/base/common/uri'; export interface IProfileContextProvider { getDefaultSystemShell: (remoteAuthority: string | undefined, os: OperatingSystem) => Promise; @@ -43,7 +45,7 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro private readonly _logService: ILogService, private readonly _terminalService: ITerminalService, private readonly _workspaceContextService: IWorkspaceContextService, - private readonly _remoteAgentService: IRemoteAgentService, + private readonly _remoteAgentService: IRemoteAgentService ) { if (this._remoteAgentService.getConnection()) { this._remoteAgentService.getEnvironment().then(env => this._primaryBackendOs = env?.os || OS); @@ -71,10 +73,14 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } resolveIcon(shellLaunchConfig: IShellLaunchConfig, os: OperatingSystem): void { - if (shellLaunchConfig.executable) { + if (shellLaunchConfig.icon) { + shellLaunchConfig.icon = this._getCustomIcon(shellLaunchConfig.icon) || Codicon.terminal; return; } + if (shellLaunchConfig.executable) { + return; + } const defaultProfile = this._getUnresolvedRealDefaultProfile(os); if (defaultProfile) { shellLaunchConfig.icon = defaultProfile.icon; @@ -106,8 +112,7 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro // Verify the icon is valid, and fallback correctly to the generic terminal id if there is // an issue - shellLaunchConfig.icon = this._verifyIcon(shellLaunchConfig.icon) || this._verifyIcon(resolvedProfile.icon) || Codicon.terminal.id; - + shellLaunchConfig.icon = this._getCustomIcon(shellLaunchConfig.icon) || this._getCustomIcon(resolvedProfile.icon) || Codicon.terminal; // Override the name if specified if (resolvedProfile.overrideName) { shellLaunchConfig.name = resolvedProfile.profileName; @@ -119,12 +124,6 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } } - private _verifyIcon(iconId?: string): string | undefined { - if (!iconId || !iconRegistry.get(iconId)) { - return undefined; - } - return iconId; - } async getDefaultShell(options: IShellLaunchConfigResolveOptions): Promise { return (await this.getDefaultProfile(options)).path; @@ -142,6 +141,36 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro return this._context.getEnvironment(remoteAuthority); } + private _getCustomIcon(icon?: unknown): TerminalIcon | undefined { + if (!icon) { + return undefined; + } + if (typeof icon === 'string') { + return iconRegistry.get(icon); + } + if (ThemeIcon.isThemeIcon(icon)) { + return icon; + } + if (URI.isUri(icon) || this._isUriComponents(icon)) { + return URI.revive(icon); + } + if (typeof icon === 'object' && icon && 'light' in icon && 'dark' in icon) { + const castedIcon = (icon as { light: unknown, dark: unknown }); + if ((URI.isUri(castedIcon.light) || this._isUriComponents(castedIcon.light)) && (URI.isUri(castedIcon.dark) || this._isUriComponents(castedIcon.dark))) { + return { light: URI.revive(castedIcon.light), dark: URI.revive(castedIcon.dark) }; + } + } + return undefined; + } + + private _isUriComponents(thing: unknown): thing is UriComponents { + if (!thing) { + return false; + } + return typeof (thing).path === 'string' && + typeof (thing).scheme === 'string'; + } + private async _getUnresolvedDefaultProfile(options: IShellLaunchConfigResolveOptions): Promise { // If automation shell is allowed, prefer that if (options.allowAutomationShell) { @@ -315,18 +344,18 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } } - private _guessProfileIcon(shell: string): string | undefined { + private _guessProfileIcon(shell: string): ThemeIcon | undefined { const file = path.parse(shell).name; switch (file) { case 'bash': - return Codicon.terminalBash.id; + return Codicon.terminalBash; case 'pwsh': case 'powershell': - return Codicon.terminalPowershell.id; + return Codicon.terminalPowershell; case 'tmux': - return Codicon.terminalTmux.id; + return Codicon.terminalTmux; case 'cmd': - return Codicon.terminalCmd.id; + return Codicon.terminalCmd; default: return undefined; } @@ -389,7 +418,7 @@ export class BrowserTerminalProfileResolverService extends BaseTerminalProfileRe @IRemoteTerminalService remoteTerminalService: IRemoteTerminalService, @ITerminalService terminalService: ITerminalService, @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { super( { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts index d5fd6d9db0a..d62c06f238c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts @@ -10,8 +10,9 @@ import { matchesFuzzy } from 'vs/base/common/filters'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { killTerminalIcon, renameTerminalIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons'; +import { getColorClass, getIconId, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; export class TerminalQuickAccessProvider extends PickerQuickAccessProvider { @@ -20,6 +21,7 @@ export class TerminalQuickAccessProvider extends PickerQuickAccessProvider { switch (buttonIndex) { case 0: diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index ab0e1e585c6..269bf44f401 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -418,7 +418,7 @@ export class TerminalService implements ITerminalService { if (!this.configHelper.config.enablePersistentSessions || !instance || !instance.persistentProcessId || !instance.icon) { return; } - this._offProcessTerminalService?.updateIcon(instance.persistentProcessId, instance.icon.id, instance.color); + this._offProcessTerminalService?.updateIcon(instance.persistentProcessId, instance.icon, instance.color); } private _removeGroup(group: ITerminalGroup): void { @@ -964,7 +964,7 @@ export class TerminalService implements ITerminalService { iconClass: ThemeIcon.asClassName(configureTerminalProfileIcon), tooltip: nls.localize('createQuickLaunchProfile', "Configure Terminal Profile") }]; - const icon = profile.icon ? (iconRegistry.get(profile.icon) || Codicon.terminal) : Codicon.terminal; + const icon = (profile.icon && ThemeIcon.isThemeIcon(profile.icon)) ? profile.icon : Codicon.terminal; const label = `$(${icon.id}) ${profile.profileName}`; if (profile.args) { if (typeof profile.args === 'string') { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index f1845c45f5a..70238c70884 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -32,6 +32,7 @@ import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; import { disposableTimeout } from 'vs/base/common/async'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { URI } from 'vs/base/common/uri'; +import { getColorClass, getIconId, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { Schemas } from 'vs/base/common/network'; const $ = DOM.$; @@ -60,7 +61,8 @@ export class TerminalTabList extends WorkbenchList { @ITerminalService private _terminalService: ITerminalService, @ITerminalInstanceService _terminalInstanceService: ITerminalInstanceService, @IInstantiationService instantiationService: IInstantiationService, - @IDecorationsService _decorationsService: IDecorationsService + @IDecorationsService _decorationsService: IDecorationsService, + @IThemeService private readonly _themeService: IThemeService ) { super('TerminalTabsList', container, { @@ -91,6 +93,8 @@ export class TerminalTabList extends WorkbenchList { this._terminalService.onInstanceTitleChanged(() => this.render()); this._terminalService.onInstanceIconChanged(() => this.render()); this._terminalService.onInstancePrimaryStatusChanged(() => this.render()); + this._terminalService.onDidChangeConnectionState(() => this.render()); + this._themeService.onDidColorThemeChange(() => this.render()); this._terminalService.onActiveInstanceChanged(e => { if (e) { const i = this._terminalService.terminalInstances.indexOf(e); @@ -176,7 +180,8 @@ class TerminalTabsRenderer implements IListRenderer Severity.Ignore) { - label = `${prefix}$(${primaryStatus.icon?.id || instance.icon?.id})`; + label = `${prefix}$(${primaryStatus.icon?.id || iconId})`; } else { - label = `${prefix}$(${instance.icon?.id})`; + label = `${prefix}$(${iconId})`; } } else { this.fillActionBar(instance, template); - label = `${prefix}$(${instance.icon?.id})`; + label = `${prefix}$(${iconId})`; // Only add the title if the icon is set, this prevents the title jumping around for // example when launching with a ShellLaunchConfig.name and no icon if (instance.icon) { @@ -275,12 +280,6 @@ class TerminalTabsRenderer implements IListRenderer('.codicon'); - if (codicon) { - codicon.style.color = instance?.icon?.color?.id || ''; - } - - if (!hasActionbar) { template.actionBar.clear(); } @@ -296,6 +295,16 @@ class TerminalTabsRenderer implements IListRenderer container.removeChild(this._styleElement))); + this.updateStyles(); + } + + private _registerListeners(): void { + this._register(this._terminalService.onInstanceIconChanged(() => this.updateStyles())); + this._register(this._terminalService.onInstancesChanged(() => this.updateStyles())); + } + + override updateStyles(): void { + super.updateStyles(); + let css = ''; + // TODO add a rule collector to avoid duplication + for (const instance of this._terminalService.terminalInstances) { + const icon = instance.icon; + if (!icon) { + return; + } + let uri = undefined; + if (icon instanceof URI) { + uri = icon; + } else if (icon instanceof Object && 'light' in icon && 'dark' in icon) { + uri = this._themeService.getColorTheme().type === ColorScheme.LIGHT ? icon.light : icon.dark; + } + const iconClasses = getUriClasses(instance, this._themeService.getColorTheme().type); + if (uri instanceof URI && iconClasses && iconClasses.length > 1) { + css += `.monaco-workbench .${iconClasses[0]} .monaco-highlighted-label .codicon, .monaco-action-bar .terminal-uri-icon.single-terminal-tab.action-label:not(.alt-command) .codicon {`; + css += `background-image: ${dom.asCSSUrl(uri)};}`; + } + } + this._styleElement.textContent = css; + } +} diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts index 3c9c2b9e797..633d8c00770 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts @@ -18,7 +18,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { Schemas } from 'vs/base/common/network'; import { ILabelService } from 'vs/platform/label/common/label'; import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessDataEvent, IRequestResolveVariablesEvent, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IRequestResolveVariablesEvent, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalIcon, TerminalShellType } from 'vs/platform/terminal/common/terminal'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform'; @@ -266,7 +266,7 @@ export class RemoteTerminalChannelClient { return this._channel.call('$updateTitle', [id, title]); } - updateIcon(id: number, icon: string, color?: string): Promise { + updateIcon(id: number, icon: TerminalIcon, color?: string): Promise { return this._channel.call('$updateIcon', [id, icon, color]); } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 6fb4a49568c..4788b47c4f1 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -12,6 +12,7 @@ import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/comm import { IProcessDataEvent, IProcessReadyEvent, IShellLaunchConfig, ITerminalDimensions, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalProfile, ITerminalProfileObject, TerminalShellType } from 'vs/platform/terminal/common/terminal'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; export const TERMINAL_VIEW_ID = 'terminal'; @@ -234,7 +235,7 @@ export interface IRemoteTerminalAttachTarget { workspaceId: string; workspaceName: string; isOrphan: boolean; - icon: string | undefined; + icon: URI | { light: URI; dark: URI } | { id: string, color?: { id: string } } | undefined; color: string | undefined; } diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts index 7bdaa59e580..ed79d042d24 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalService.ts @@ -8,6 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -118,7 +119,7 @@ export class LocalTerminalService extends Disposable implements ILocalTerminalSe await this._localPtyService.updateTitle(id, title); } - async updateIcon(id: number, icon: string, color?: string): Promise { + async updateIcon(id: number, icon: URI | { light: URI; dark: URI } | { id: string, color?: { id: string } }, color?: string): Promise { await this._localPtyService.updateIcon(id, icon, color); } diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts index 8c61df06052..93363863f9e 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts @@ -24,7 +24,7 @@ export class ElectronTerminalProfileResolverService extends BaseTerminalProfileR @ILocalTerminalService localTerminalService: ILocalTerminalService, @IRemoteTerminalService remoteTerminalService: IRemoteTerminalService, @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { super( { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 2414d2220b9..dad91802038 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1611,7 +1611,7 @@ export class TestLocalTerminalService implements ILocalTerminalService { async reduceConnectionGraceTime(): Promise { throw new Error('Method not implemented.'); } processBinary(id: number, data: string): Promise { throw new Error('Method not implemented.'); } updateTitle(id: number, title: string): Promise { throw new Error('Method not implemented.'); } - updateIcon(id: number, icon: string, color?: string): Promise { throw new Error('Method not implemented.'); } + updateIcon(id: number, icon: URI | { light: URI; dark: URI } | { id: string, color?: { id: string } }, color?: string): Promise { throw new Error('Method not implemented.'); } } class TestTerminalChildProcess implements ITerminalChildProcess {